【问题标题】:rbp not allowed as SIB base?rbp 不允许作为 SIB 基础?
【发布时间】:2019-03-02 12:23:02
【问题描述】:

我对 x86-64 二进制编码很陌生。我正在尝试修复一些旧的“汇编”代码。

无论如何,我正在尝试做这样的事情(英特尔语法):

mov    [rbp+rcx], al

汇编器当前正在生成这个:

88 04 0D

但这似乎不是一个有效的指令。如果我将 SIB 字节中的基数从 rbp 更改为其他一些寄存器,它工作正常。使其工作的另一种方法是添加一个零字节位移 (88 44 0D 00)。这似乎发生在其他类似的操作码上。

为什么我不能在那里使用rbpmod=00

【问题讨论】:

  • 现在我想知道您使用的是哪个汇编程序,它会生成错误的指令!
  • @AlexisWilke 这更像是一个笑话而不是一个汇编程序,但这个东西:github.com/seattlerb/wilson 我正在尝试将它改编为 x86-64 和现代 Ruby(用于教育目的!),并偶然发现这个错误。

标签: assembly x86 x86-64 machine-code addressing-mode


【解决方案1】:

另请参阅 https://wiki.osdev.org/X86-64_Instruction_Encoding#32.2F64-bit_addressing_2 或 Intel 的 vol.2 手册,了解这些特殊情况的编码表和脚注。 这个答案指出了特殊情况,并讨论了为什么这些设计选择有意义,即他们需要解决什么设计问题。


表示rbp 的编码是无基址寄存器的转义码(只是 SIB 中的 disp32 或 ModRM 中的 RIP 相对 rel32)。大多数汇编程序将[rbp] 组装成[rbp + disp8=0]

由于您不需要对其进行缩放,因此请改用 [rcx + rbp] 以避免需要 disp8=0,因为 rbp 可以成为索引。

(SS 和 DS 在长模式下总是等价的,因此 base=RBP 暗示 SS 而 base=RCX 暗示使用 DS 段并不重要。)


x86/x86-64 ModRM寻址模式编码特例

(来自我在Why are rbp and rsp called general purpose registers? 上写的答案)。这个问题看起来是复制或移植本节的理想场所。

rbp/r13 不能是没有位移的基址寄存器:编码的意思是:(在 ModRM 中)rel32(相对于 RIP),或(在 SIB 中) disp32 没有基址寄存器。 (r13 在 ModRM/SIB 中使用相同的 3 位,因此该选择通过不让指令长度解码器查看the REX.B bit 来获得第 4 个基寄存器位来简化解码)。 [r13] 组装成 [r13 + disp8=0][r13+rdx] 组装成 [rdx+r13](通过交换基数/索引来避免问题)。

rsp/r12 作为基址寄存器总是需要一个 SIB 字节。 (base=RSP 的 ModR/M 编码是用于发送 SIB 字节的转义码,同样,如果 r12 的处理方式不同,更多的解码器将不得不关心 REX 前缀)。

rsp 不能是索引寄存器。这使得编码[rsp] 成为可能,这比[rsp + rsp] 更有用。 (英特尔本可以为 32 位寻址模式(386 中的新功能)设计 ModRM/SIB 编码,因此只有 base=ESP 才有可能使用无索引的 SIB。这将使[eax + esp*4] 成为可能,并且只排除[esp + esp*1/2/4/8] . 但这没有用,因此他们通过使 index=ESP 成为无索引的代码来简化硬件,无论基数如何。这允许两种冗余方式来编码任何基数或基数 + disp 寻址模式:有或没有 SIB。)

r12可以是索引寄存器。与其他情况不同,这不会影响指令长度解码。此外,它无法像其他情况那样使用更长的编码来解决。 AMD 希望 AMD64 的寄存器集尽可能正交,因此他们会花费一些额外的晶体管来检查 REX.X 作为索引/无索引解码的一部分是有道理的。例如,[rsp + r12*4] 需要 index=r12,因此 r12 不完全通用会使 AMD64 成为更糟糕的编译器目标。

   0:   41 8b 03                mov    eax,DWORD PTR [r11]
   3:   41 8b 04 24             mov    eax,DWORD PTR [r12]      # needs a SIB like RSP
   7:   41 8b 45 00             mov    eax,DWORD PTR [r13+0x0]  # needs a disp8 like RBP
   b:   41 8b 06                mov    eax,DWORD PTR [r14]
   e:   41 8b 07                mov    eax,DWORD PTR [r15]
  11:   43 8b 04 e3             mov    eax,DWORD PTR [r11+r12*8] # *can* be an index

这些都适用于 32 位寻址模式;编码是相同的,除了没有 EIP 相对编码,只是编码 disp32 的两种冗余方式,没有基础。


这似乎发生在其他类似的操作码上。

r/m 操作数的 ModRM 编码始终相同。有些操作码需要寄存器操作数,有些需要内存,但实际的 ModRM + 可选 SIB + 可选位移是固定的,因此无论指令如何,相同的硬件都可以对其进行解码。

有一些罕见的操作码,例如 mov al/ax/eax/rax, [qword absolute_address],它们的操作数根本不使用 ModRM 编码,但任何使用相同格式的操作码。

【讨论】:

  • 啊,可以看看这里的表格:wiki.osdev.org/…。谢谢。
  • @ollpu:是的,很好的链接。但是您必须仔细阅读才能发现模式和三种特殊情况。用我最初为不同答案写的部分更新了这个答案。
猜你喜欢
  • 1970-01-01
  • 2017-06-12
  • 2013-08-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-06-24
  • 1970-01-01
相关资源
最近更新 更多