问题
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!*/
回答1:
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
回答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
来源:https://stackoverflow.com/questions/49438550/assembly-executing-a-long-jump-with-an-offset-with-different-syntax