6

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!*/

Michael Petch
  • 46,082
  • 8
  • 107
  • 198
  • Hi Michael that doesn't seem to work `jmp $0x08, flush2` gives me this error "Error: operand type mismatch for `jmp'" –  Mar 22 '18 at 21:28
  • 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 `$`. – Michael Petch Mar 22 '18 at 21:31

2 Answers2

11

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
Michael Petch
  • 46,082
  • 8
  • 107
  • 198
  • 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. – NickKnack Mar 04 '22 at 06:52
  • @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](https://sourceware.org/binutils/docs/as/i386_002dBugs.html) 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. – Peter Cordes Aug 03 '22 at 20:28
2

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
Nick
  • 463
  • 4
  • 13