Assembly executing a long jump with an offset with different syntax
Asked Answered
E

2

9

I am writing a GDT for a Kernel and all is going well, I'm following this tutorial.

http://www.osdever.net/bkerndev/Docs/gdt.htm

When link the C code to the assembly code he uses this piece of code.

; This will set up our new segment registers. We need to do
; something special in order to set CS. We do what is called a
; far jump. A jump that includes a segment as well as an offset.
; This is declared in C as 'extern void gdt_flush();'
global _gdt_flush     ; Allows the C code to link to this
extern _gp            ; Says that '_gp' is in another file
_gdt_flush:
lgdt [_gp]        ; Load the GDT with our '_gp' which is a special pointer
    mov ax, 0x10      ; 0x10 is the offset in the GDT to our data segment
    mov ds, ax
    mov es, ax
    mov fs, ax
    mov gs, ax
    mov ss, ax
    jmp 0x08:flush2   ; 0x08 is the offset to our code segment: Far jump!
flush2:
ret               ; Returns back to the C code!

However, my assembly syntax is different here is what I have so far as part of my boot.s file.

.global gdt_flush     /*Allows the C code to link to this*/
.extern gp            /*Says that '_gp' is in another file*/
_gdt_flush:
    lgdt gp        /*; Load the GDT with our '_gp' which is a special pointer*/
    mov %ax, 0x10     /* ; 0x10 is the offset in the GDT to our data segment*/
    mov %ds, %ax
    mov %es, %ax
    mov %fs, %ax
    mov %gs, %ax
    mov %ss, %ax
    jmp flush2   /*; 0x08 is the offset to our code segment: Far jump!*/
flush2:
ret               /*; Returns back to the C code!*/

My question is how do I translate the syntax of this instruction into the format I am using?

His: jmp 0x08:flush2 ; 0x08 is the offset to our code segment: Far jump!

Mine: (long l?)jmp ????flush2 /*; 0x08 is the offset to our code segment: Far jump!*/

Excavate answered 22/3, 2018 at 21:11 Comment(2)
Hi Michael that doesn't seem to work jmp $0x08, flush2 gives me this error "Error: operand type mismatch for `jmp'"Excavate
Yes I had a small typo in my first comment that I deleted before you even responded. AT&T syntax would look like jmp $0x08,$flush2 . The label in this case needs to be preceded by a $.Vookles
V
13

A few things. The AT&T syntax for a far jump is:

jmp $0x08,$flush2 

The label in this case needs to be preceded by a $. Immediate values like 0x08 also need a $. This line doesn't do what you think it does:

mov %ax, 0x10

Important thing about AT&T syntax is that unlike Intel Syntax the operands are reversed. Source operand is first and destination operation is after. Secondly, immediate values in AT&T syntax on x86/x86-64 need to have a $ sign prepended to them or they are actually treated as a memory operand. Your instruction actually moved the 16-bit contents of AX to the memory address 0x00000010 which is not what you intended. What you wanted was:

mov $0x10, %ax

This moves the immediate value 0x10 to AX. The problem with operands being reversed also applies to all your lines like:

mov %ds, %ax

Should be:

mov %ax, %ds

I usually prefer calling your function load_gdt. I'm usually a fan of passing the segment values (CS and DS) and the address of the GDTR with code like:

load_gdt:
    mov 4(%esp), %edx    # EDX is 1st argument - GDT record pointer
    mov 8(%esp), %eax    # EAX is 2nd argument - Data Selector
    lgdt (%edx)          # Load GDT with GDT record pointer passed as 1st argument
    mov %eax, %ds        # Reload all the data descriptors with Data selector (2nd arg)
    mov %eax, %es
    mov %eax, %gs
    mov %eax, %fs
    mov %eax, %ss

    pushl 12(%esp)      # Create FAR pointer on stack using Code selector (3rd argument)
    push $.setcs         # Offset of FAR JMP will be setcs label below
    ljmp *(%esp)        # Do the FAR JMP to next instruction to set CS with Code selector,
                        #    and set the EIP (instruction pointer) to offset of setcs
.setcs:
    add $8, %esp        # Restore stack (remove 2 DWORD values we put on stack to
                        #     create FAR Pointer)
    ret

The C prototype would be something like:

void load_gdt(struct gdt_ptr *gdt_ptr, unsigned int data_sel, unsigned int code_sel);

If you want to use GNU assembler with a variant of Intel Syntax you could try adding this directive to the top of all your assembly files:

.intel_syntax noprefix
Vookles answered 22/3, 2018 at 21:54 Comment(2)
Just so people reading this are aware, GNU's Intel syntax has some differences from 'actual' Intel syntax. (I'm aware the answer says "variant of Intel syntax", but I feel that isn't specific enough.) Such as some instructions having operands reversed compared to Intel syntax, and probably a few others I can't remember.Lollipop
@NickKnack: GAS .intel_syntax noprefix has the same operand-order as Intel's manuals for every instruction I've ever looked at. Unless you're thinking of x87 instructions like fsubr st(1), st vs. fsub st(1), st which may still have the AT&T syntax design bug even with GAS in .intel_syntax mode or objdump in -Mintel mode, at least in older binutils versions (it's fixed now, intel mode uses Intel mnemonics, matching NASM for those x87 instructions). That's swapping mnemonics, not operand-order.Inhumanity
H
3

In addition to Michael's answer, which definitely out-informs mine, this would be my translation:

.global gdt_flush

gdt_flush:
   movl 4(%esp),%eax
   lgdt (%eax)

   movw $0x10, %ax
   movw %ax, %ds
   movw %ax, %es
   movw %ax, %fs
   movw %ax, %gs
   movw %ax, %ss
   jmp  $0x08,$flush

flush:
   ret
Heathheathberry answered 8/4, 2019 at 0:18 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.