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扩展到新安装的代码部分
但最后一步我得到了GP(0)。 为什么?

以下是代码(已在64位模式下从运行):

call do_lgdt#“do_lgdt”在底部定义

push$(1do_lgdt中不应该有ret吗?否则您将执行gdt…

GAS assembles
ljmp*(%rsp)
无REX前缀,因此操作数大小为
m16:32
。长模式也允许,但不允许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
的操作数,并且可能出于兼容原因仍在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:so
push%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