【问题标题】:What happens to instruction pointers when address overrides are used to target a smaller address space?当地址覆盖用于针对较小的地址空间时,指令指针会发生什么情况?
【发布时间】:2017-09-13 02:37:01
【问题描述】:

当地址覆盖用于定位较小的地址空间时,指令指针会发生什么,例如默认为 32 位地址,但覆盖转换为 16?

因此,假设我们处于 x86-32 模式,并且默认为我们所在的当前代码段的 32 位内存空间。

此外,IP 寄存器包含值 87654321h。

如果我使用 67h 覆盖默认值并使内存空间为 16 位,那么处理器如何计算当前代码段的偏移量?

必须忽略 IP 中的某些位,否则您将超出覆盖指定的 16 位内存空间。

那么,处理器是否只是忽略了 IP 寄存器中的 8765 部分?

也就是说,处理器是否只使用 4 个最低有效位而忽略 4 个最高有效位?

与访问数据段相关的地址覆盖如何?

例如,我们在 x86-32 模式下,默认为 32 位内存寻址,我们为此指令使用 67h 前缀:mov eax, [ebx]

现在,ebx 包含一个 32 位数字。

67h 覆盖是否将上述指令更改为:mov eax, [bx]

“常量指针”呢?示例:mov eax, [87654321]

67h 覆盖是否会将其更改为 mov eax, [4321]

内存覆盖是否也会影响数据段的偏移量,还是只影响代码段?

地址覆盖如何影响堆栈指针?

如果堆栈指针包含一个 32 位数字(我们将再次使用 87654321h)并且我 push 或 pop,则引用什么内存?

推送和弹出间接访问内存。

那么,您会只使用 IP 寄存器中的 4321 位而忽略最高有效位吗?

另外,细分基础本身呢?

示例:我们处于 x86-32 模式,默认为 32 位内存空间,但我们使用 67h 覆盖。

CS 寄存器指向 GDT 中的一个描述符,它的段基数同样是 87654321h。

我们直接超出了 16 位内存范围,甚至没有添加偏移量。

处理器做什么?忽略 4 个最高有效位?同样的问题可以应用于数据段和堆栈段的段描述符。

【问题讨论】:

  • 地址大小前缀对获取未来指令没有影响。有关详细描述前缀的英特尔手册的链接,请参阅其他问题的答案 (stackoverflow.com/questions/46188388/…)。如果您在阅读后仍有不明白的内容,请编辑此问题。
  • @Peter 我编辑了原始问题
  • 这就像 10 个问题。您应该通过组装db 0x67 / mov eax, [ebx] 然后拆卸来尝试其中的一些。 (并且还组装mov eax, [bx])。也在调试器中单步执行它。然后,您可以编辑英特尔文档已经直接回答的问题。 EIP 永远不会被前缀截断,除了 jmpcall 指令,我忘了。

标签: pointers assembly x86 memory-address prefix


【解决方案1】:

0x67address-size 前缀。它改变了指令中寻址模式的解释。

它确实不会将机器暂时置于 16 位模式或将 EIP 截断为 16 位,或影响任何其他未明确来自 [addressing mode] 的地址说明。

对于推送/弹出,instruction reference manual entry for push 说:

地址大小仅在引用内存中的源操作数时使用。

所以在 32 位模式下,a16 push eax 仍然会设置 esp-=4,然后存储 [esp] = eax。它不会将 ESP 截断为 16 位。前缀没有效果,因为唯一的内存操作数是隐式的而不是显式的。

push [ebx]67 前缀的影响。

db  0x67
push dword [ebx]

将解码为push dword [bp+di],并从该 16 位地址加载 32 位(忽略这些寄存器的高 16 位)。 (16 位寻址模式使用与 32/64 不同的编码(没有可选的 SIB 字节)。

但是,它仍然会更新完整的esp,并存储到[esp]

(有关有效地址编码的详细信息,请参阅Intel's volume 2 PDF,第 2 章:指令格式,表 2-1(16 位)与表 2-2(32 位)。)

在 64 位模式下,地址大小前缀会将 push [rbx] 转换为 push [ebx]


由于某些形式的push 可能会受到地址大小前缀的影响,因此这可能不属于无意义前缀的类别,它的使用是保留的,并且可能会在未来的 CPU 中产生不可预测的行为。 (What happens when you use a memory override prefix but all the operands are registers?)。 OTOH,这可能仅适用于用于推送的 push r/m32 操作码,不适用于不能采用内存操作数的 push r32 短格式。

我认为它的措辞方式,英特尔的手册确实不能保证即使 push ebxpush r/m32 更长的编码在未来带有 67 前缀的 CPU 中也不会解码为不同的东西。

【讨论】:

  • 67 ff 33 是​​ push dword [bp+di],而不是 push dword [bx]
  • @prl:谢谢,我通常会使用 64 -> 32 个示例,但是 OP 询问的是 32->16,我忘记考虑 16 中 ModR/M 字节的不同编码-位寻址模式:P
  • 我只记得它,因为它是另一种方式:它允许在16位模式下使用[eax],这很方便。
  • @prl:是的,如果是[eax],我肯定会注意到。我记得当时在想“好吧,是的,[bx] 是一个有效的 16 位寻址模式,所以这看起来是对的”。德普。 >.
  • 你能解释一下这两种地址方案有什么不同吗?什么是 16 位方案?和 32 位方案?它们似乎完全无关。还有,你们是怎么记住机器码的?
【解决方案2】:

例如,我们在 x86-32 模式下,默认为 32 位内存寻址,我们为此指令使用 67h 前缀:mov eax, [ebx]
现在,ebx 包含一个 32 位数字。
67h覆盖是否将上述指令更改为:mov eax, [bx]
那么“常量指针”呢?示例:mov eax, [87654321].
67h 覆盖是否会将其更改为 mov eax, [4321]

地址大小覆盖不只是改变地址的大小,它实际上改变了寻址方案。
mov eax, [ebx] 上的 67 覆盖将其更改为 mov eax, [bp+di]
mov eax, [87654321] 上的 67 覆盖将其更改为 mov eax, [di](后跟 and [ebx+65], eax 和一些 xchg 指令)。

【讨论】:

  • @matrix:[disp16] 的 16 位 ModR/M 编码与 [disp32] 的 32 位编码不同,因此位移字节最终被解码为单独的指令,而不是作为寻址模式的一部分进行解码。 (长度变化的前缀很有趣......实际上它们是英特尔 CPU 的潜在解码瓶颈,在预解码阶段,在将指令边界提供给解码器之前找到指令边界。)
  • @matrix:通常什么都没有。在 32 位模式下它几乎没有用处。在 16 位模式下,它允许您使用 32 位地址,或使用缩放索引,或 16 位寻址模式无法使用的寄存器。
  • 在具有 32 位指针的 64 位模式下(例如 Linux 的 x32 ABI),编译器有时最终会使用地址大小前缀而不是额外的符号/零扩展指令:godbolt.org/g/q7VYJR(因为它如果索引是 32 位负数,则必须确保地址环绕,而不是使用 64 位寻址模式超过 4GB)。另一种选择是将 32 位 int 符号扩展为 64 位寄存器并使用 64 位寻址模式。 (ABI 已经要求指向函数的指针参数从零扩展到 64 位。)
  • @matrix:显然您可以使用保护模式设置段限制 > 64k,然后切换回实模式。这称为"big/huge unreal mode"en.wikipedia.org/wiki/Unreal_modewiki.osdev.org/Unreal_Mode。或者只是将它用于 16 位模式下的 32 位 LEA。或者在 16 位保护模式下使用它,我认为这是一回事。 (实模式并不是唯一默认为 16 位操作数大小的模式)。
  • @matrix。对。切换模式不会清除段描述符缓存。没有 32 位段寄存器之类的东西。它要么直接使用(seg << 4 + offset 在实模式下),要么是描述符表的索引。我想知道这是否是英特尔考虑的地址大小前缀的主要用例?否则似乎浪费了操作码编码空间。除非他们考虑在 32 位模式下使用 16 位指针来节省内存/缓存占用空间?不太可能,因为 16 位寻址模式太糟糕了。
猜你喜欢
  • 2011-02-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-09-11
相关资源
最近更新 更多