Assembly ljmp(在64位模式下)被#GP(0)拒绝。为什么?
代码是最小的:Assembly ljmp(在64位模式下)被#GP(0)拒绝。为什么?,assembly,x86,x86-64,Assembly,X86,X86 64,代码是最小的: 使用2个条目(NULL和代码描述符)设置GDT 尝试将jmp扩展到新安装的代码部分 但最后一步我得到了GP(0)。 为什么? 以下是代码(已在64位模式下从运行): call do_lgdt#“do_lgdt”在底部定义 push$(1do_lgdt中不应该有ret吗?否则您将执行gdt…GAS assemblesljmp*(%rsp)无REX前缀,因此操作数大小为m16:32。长模式也允许,但不允许16:32形式 不幸的是,当使用无后缀的ljmp时,GAS没有警告操作数大小
- 使用2个条目(NULL和代码描述符)设置GDT
- 尝试将jmp扩展到新安装的代码部分
call do_lgdt#“do_lgdt”在底部定义
push$(1do_lgdt中不应该有ret吗?否则您将执行gdt…GAS assemblesljmp*(%rsp)
无REX前缀,因此操作数大小为m16:32
。长模式也允许,但不允许16:32形式
不幸的是,当使用无后缀的ljmp
时,GAS没有警告操作数大小不明确,而binutils 2.29.1似乎甚至不理解REX形式。它不接受ljmpq
,但它接受ljmpl
(作为非REX形式)
第一代AMD64 CPU(AMD K8)不支持REX.Wm16:64
格式的jmp
,binutils在引入时可能从未更新过。如果需要64位目标地址,则推送iretq
或retfq
的操作数,并且可能出于兼容原因仍在AMD手册中。如果这是您唯一的错误,则使用wrong
在你的气源里,我用了
.byte 0x48 # REX.W
ljmp *(%rsp) # m16:64
在NDISAM语法中,有两种形式:
0000004F 48FF2C24 jmp far [rsp]
0000004F FF2C24 jmp dword far [rsp]
在objdump
output中(可能与GAS接受作为输入的语法相同):
另一种选择(如果指针也位于低位32位)可能会保存几个代码字节(尽管寻址模式比推送imm8长,所以可能不会)
push%rax
movw$(1正确。修复了它。@marianok:ljmp
现在可以使用了吗?如果在gdt:
之前使用.p2align 3
是否重要?是否使用了调试器(如BOCHS)单步执行内核?你能告诉我是远跳转本身出了故障,还是跳转后的第一条指令不是你所期望的,它出了故障?(即检查RIP)。@PeterCordes Align仍然没有做到这一点。我可以看到IDT处理程序堆栈顶部的ljmp指令地址(对于#GP).@PeterCordes base和limit字段被忽略。无论如何,更改为0仍然没有起到作用。使用64位GDT时,base和limit为0I刚刚检查了我的一些工作64位代码,它使用ljmp,不带32位偏移量的rex.w。@prl:sopush%rax
/movw$(1不,我认为不是。操作数是m16:32,没有REX.W。只是对JMP m16:64的观察。并非所有处理器都支持它(即:原始AMD64指令集)因此AMD出于兼容性原因不鼓励使用它。如果m16:32是可行的,那么它就会工作。否则建议使用retfq
或iretq
。SDM中似乎存在错误,这就是为什么我一开始不确定这个答案的原因。SDM说jmp指令的操作数大小固定在64位。也许s该语句仅用于引用近跳转。@prl手册列出了有效的m16:32和m16:64编码。我认为“指令的操作大小固定为64位。”只是意味着目标始终是RIP而不是EIP。它继续使用“如果选择器指向一个门,那么RIP等于从门获取的64位位移;否则RIP等于指令中引用的远指针的零扩展偏移量。”所以它都是指远跳转和32位内存操作数。@罗斯,谢谢,我不知何故忽略了“零扩展”部分。
0000004F 48FF2C24 jmp far [rsp]
0000004F FF2C24 jmp dword far [rsp]
# 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]
push %rax
movw $(1<<3), 4(%rsp) # rewrite the top half of what you pushed
ljmpl *(%rsp) # m16:32