代码很少:
但是我得到了#GP(0),这是最后一步。 为什么呢?
以下是代码(在64位模式下从已经运行):
call do_lgdt # 'do_lgdt' is defined at the bottom
push $(1<<3) # Push code-selector,TI=0,RPL=0
lea func(%rip),%rax # The (relocated) address of 'func'
push %rax # Push it.
ljmp *(%rsp) # FAILS!!! #GP(0) (error code 0).
func:
jmp . # Hang
do_lgdt:
lea gdt(%rip),%rax # The (relocated) address of 'gdt'.
# 'gdt' is defined at the bottom.
push %rax # Base
pushw $(gdt_end-gdt-1) # Limit
lgdt (%rsp)
add $0xa,%rsp # Re-align the stack.
ret # Thanks @mevets..
.align 8 # Thanks @PeterCordes
gdt:
.quad 0x0000000000000000 # NULL Descriptor
.quad 0x00a09a0000000000 # limit: 0x0000 (Ignored. Thanks @MichaelPetch)
# base : 0x0000
# type : 0xa (code/data=1,non-conforming,exec,accessed=0)
# : 0x9 (system=1,DPL=0,present=1)
# limit: 0x0 (Ignored. Thanks @MichaelPetch)
# : 0xa (avl=0,l=1,d=0,granularity=1)
# base : 0x00
gdt_end:
答案 0 :(得分:2)
GAS汇总ljmp *(%rsp)
没有REX前缀,因此操作数大小为m16:32
。长模式也允许a m16:64
form for far jmp
(REW.W FF /5
),但不允许 dis - 允许16:32表格。
ljmp
而没有后缀时,GAS不会警告不明确的操作数大小,而binutils 2.29.1似乎甚至不了解REX形式。它不接受ljmpq
,但它接受ljmpl
(作为非REX表单)。
第一代AMD64 CPU(AMD K8)并不支持REX.W m16:64
形式的jmp
,而binutils在推出时可能永远不会更新。如果您需要64位目标地址,则推送iretq
或retfq
was recommended的操作数,并且由于compat原因,可能仍然在AMD手册中。如果这是你唯一的错误,iretq
didn't work,那你就错了。
在您的GAS来源中,我使用了
.byte 0x48 # REX.W
ljmp *(%rsp) # m16:64
在NDISASM语法中,两种形式是:
0000004F 48FF2C24 jmp far [rsp]
0000004F FF2C24 jmp dword far [rsp]
在objdump
输出中(可能与GAS接受的语法相同):
# non-REX m16:32
f: ff 2c 24 jmp FWORD PTR [rsp]
f: ff 2c 24 ljmp *(%rsp)
# REX.W m16:64
f: 48 ff 2c 24 rex.W ljmp *(%rsp)
f: 48 ff 2c 24 rex.W jmp FWORD PTR [rsp]
另一种选择(如果你的指针也是低32位)可能会节省几个代码字节(虽然movw imm16
+寻址模式比push imm8
长,所以可能没有。
push %rax
movw $(1<<3), 4(%rsp) # rewrite the top half of what you pushed
ljmpl *(%rsp) # m16:32
另请注意,如果您的代码在禁用中断的情况下运行,则do_gdt
函数可以push %rax
,然后存储到-2(%rsp)
,而不是执行单词推送。虽然这可能实际上并不小。但是对于代码大小,您可以使用单个pop
而不是2来恢复堆栈。(pop
+ popw
仍然会比add $10, %rsp
更短(总共3B)(4B总))。
感谢@prl发现m16:32
问题,我只是花时间写下来并检查两个反汇编程序的作用。
答案 1 :(得分:1)
do_lgdt不应该有一个ret?否则你将执行gdt ...