执行具有不同语法偏移量的长跳转的程序集
Assembly executing a long jump with an offset with different syntax
我正在为内核编写 GDT,一切顺利,我正在学习本教程。
http://www.osdever.net/bkerndev/Docs/gdt.htm
当link将C代码转为汇编代码时他用的是这段代码
; 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!
但是,我的汇编语法是不同的,这里是我 boot.s
文件的一部分。
.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!*/
我的问题是如何将此指令的语法转换为我正在使用的格式?
他的:jmp 0x08:flush2 ; 0x08 is the offset to our code segment: Far jump!
我的:(long l?)jmp ????flush2 /*; 0x08 is the offset to our code segment: Far jump!*/
几件事。远跳的 AT&T 语法是:
jmp [=10=]x08,$flush2
本例中的标签需要在前面加上 $
。 0x08
等立即数也需要 $
。此行与您认为的不同:
mov %ax, 0x10
关于 AT&T 语法的重要一点是,与 Intel 语法不同,操作数是相反的。源操作数在前,目标操作在后。其次,x86/x86-64 上的 AT&T 语法中的立即值需要在它们前面加上一个 $
符号,否则它们实际上被视为内存操作数。您的指令实际上将 AX 的 16 位内容移动到内存地址 0x00000010,这不是您想要的。你想要的是:
mov [=12=]x10, %ax
这会将立即值 0x10 移动到 AX。操作数被反转的问题也适用于您的所有行,例如:
mov %ds, %ax
应该是:
mov %ax, %ds
我通常更喜欢调用您的函数 load_gdt
。我通常喜欢使用如下代码传递段值(CS 和 DS)和 GDTR 地址:
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 , %esp # Restore stack (remove 2 DWORD values we put on stack to
# create FAR Pointer)
ret
C 原型类似于:
void load_gdt(struct gdt_ptr *gdt_ptr, unsigned int data_sel, unsigned int code_sel);
如果您想使用具有 Intel 语法变体的 GNU 汇编程序,您可以尝试将此指令添加到所有汇编文件的顶部:
.intel_syntax noprefix
除了 Michael 的回答,这肯定比我的更明智,这将是我的翻译:
.global gdt_flush
gdt_flush:
movl 4(%esp),%eax
lgdt (%eax)
movw [=10=]x10, %ax
movw %ax, %ds
movw %ax, %es
movw %ax, %fs
movw %ax, %gs
movw %ax, %ss
jmp [=10=]x08,$flush
flush:
ret
我正在为内核编写 GDT,一切顺利,我正在学习本教程。
http://www.osdever.net/bkerndev/Docs/gdt.htm
当link将C代码转为汇编代码时他用的是这段代码
; 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!
但是,我的汇编语法是不同的,这里是我 boot.s
文件的一部分。
.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!*/
我的问题是如何将此指令的语法转换为我正在使用的格式?
他的:jmp 0x08:flush2 ; 0x08 is the offset to our code segment: Far jump!
我的:(long l?)jmp ????flush2 /*; 0x08 is the offset to our code segment: Far jump!*/
几件事。远跳的 AT&T 语法是:
jmp [=10=]x08,$flush2
本例中的标签需要在前面加上 $
。 0x08
等立即数也需要 $
。此行与您认为的不同:
mov %ax, 0x10
关于 AT&T 语法的重要一点是,与 Intel 语法不同,操作数是相反的。源操作数在前,目标操作在后。其次,x86/x86-64 上的 AT&T 语法中的立即值需要在它们前面加上一个 $
符号,否则它们实际上被视为内存操作数。您的指令实际上将 AX 的 16 位内容移动到内存地址 0x00000010,这不是您想要的。你想要的是:
mov [=12=]x10, %ax
这会将立即值 0x10 移动到 AX。操作数被反转的问题也适用于您的所有行,例如:
mov %ds, %ax
应该是:
mov %ax, %ds
我通常更喜欢调用您的函数 load_gdt
。我通常喜欢使用如下代码传递段值(CS 和 DS)和 GDTR 地址:
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 , %esp # Restore stack (remove 2 DWORD values we put on stack to
# create FAR Pointer)
ret
C 原型类似于:
void load_gdt(struct gdt_ptr *gdt_ptr, unsigned int data_sel, unsigned int code_sel);
如果您想使用具有 Intel 语法变体的 GNU 汇编程序,您可以尝试将此指令添加到所有汇编文件的顶部:
.intel_syntax noprefix
除了 Michael 的回答,这肯定比我的更明智,这将是我的翻译:
.global gdt_flush
gdt_flush:
movl 4(%esp),%eax
lgdt (%eax)
movw [=10=]x10, %ax
movw %ax, %ds
movw %ax, %es
movw %ax, %fs
movw %ax, %gs
movw %ax, %ss
jmp [=10=]x08,$flush
flush:
ret