【问题标题】:Difference betwen Base and Displacement基础和位移之间的差异
【发布时间】:2019-03-04 23:12:14
【问题描述】:

我在理解遇到的两条指令时遇到了一些问题。
第一个如下:

imul   eax,DWORD PTR [esi+ebx*4-0x4]

这条指令是否意味着 => 将你在括号中计算的地址处的值乘以 eax 并将其存储在同一个寄存器 (eax) 中? 如果是这样,我们是否像这样计算括号之间的地址?

  1. ebx * 4
  2. esi + 运算1的结果
  3. 从结果中减去 4
  4. 转到地址(结果)并获取其中的值。

我有解码问题的第二条指令是这条

jmp    DWORD PTR [eax*4+0x80497e8]

-eax *4 是否等同于 index * scale ?
- 0x80497e8 是位移吗?

那么获取括号内的地址是我们应该遵循的顺序吗?

  1. eax * 4
  2. 将1.中的结果加到地址0x80497e8中
  3. 跳转到那个地址

据我了解,[base + index * scales] 用于获取数组内部的值。 base 是指向数组中第一个元素的指针。 索引实际上是存储我们想要的值的索引 而刻度是数组包含的日期的大小

我的问题是当你在方程中添加位移时,位移是做什么用的? 当位移为负值时是什么意思?

【问题讨论】:

    标签: assembly x86 intel addressing-mode


    【解决方案1】:

    不要被术语所迷惑。 “base”具有特定的技术含义,寻址模式的“base”组件必须是数组的开头。例如[esp + 16 + esi*4] 可能正在索引从 esp+16 开始的本地数组,即使 esp 是基址寄存器。

    同样,[esi+ebx*4-0x4] 最明显的解释是array[i-1],在 EBX 中 i 和 esi 保存数组的起始地址。编译器将-1 折叠到寻址模式而不是在另一个寄存器中计算ebx-1 并将其用作索引是一个明显的优化。

    位移为负值是什么意思?

    它并不“意味着”任何东西。硬件只是进行二进制加法并使用结果。由程序员(或编译器)使用访问所需字节的寻址模式。

    我在Referencing the contents of a memory location. (x86 addressing modes) 上的回答提供了一些示例,说明您何时可以使用每个可能的寻址模式进行数组索引,使用指向数组或静态数组的指针(因此您可以将数组起始地址硬编码为绝对位移)。


    在技术 x86 寻址模式术语中:

    • 位移:地址的 +- 常量部分,以 2 的补码符号扩展 disp8 或 disp32 编码。 (在 64 位寻址模式中,disp32 符号扩展为 64 位)。
    • 偏移量:esi+ebx*4-0x4 计算的结果:相对于段基的偏移量。 (在 base=0 的普通平面内存模型中,偏移量 = 整个地址)。

      人们经常使用“offset”来描述位移,通常不会造成混淆,因为从上下文中可以清楚地看出他们在谈论恒定的偏移量(在 x86 seg:off 以外的意义上使用英文单词 offset),但是我喜欢用“位移”来形容位移。

    • base:寻址方式的非索引寄存器组件,如果有的话。 (“无基址寄存器”的编码意味着有一个disp32,您可以将其视为基址。它暗示了DS段。)

      这包括只有索引而没有基址寄存器的情况:[esi*4] 只能编码为[dword 0 + esi*4]。


    imul   eax,DWORD PTR [esi+ebx*4-0x4]
    

    是的,eax *= memory source operand。

    是的,您的地址计算是正确的。基数 + 缩放索引 + 有符号位移,产生一个虚拟地址1。

    “转到地址(结果)并获取其中的值”是一种奇怪的描述方式。 “转到”通常意味着控制传输,将字节作为代码获取。但事实并非如此,这只是从该地址加载数据,完全由硬件处理。

    现代 x86 CPU(例如 Intel Skylake)将 imul eax, [esi+ebx*4 - 4] 解码为两个微指令:一个 imul ALU 操作和一个负载。 ALU 操作必须等待加载结果。 (有趣的事实:对于大多数管道,这两个微操作实际上被微融合到一个微操作中,除了在乱序调度器中。请参阅https://agner.org/optimize/ 了解更多信息。)

    当加载 uop 运行时,地址生成单元 (AGU) 获取 2 个寄存器输入、索引比例因子(左移 2)和立即位移 (-4)。 AGU 中的移位加硬件计算加载地址。

    加载执行单元内部的下一步是使用该地址从 L1d 缓存(它具有基本内置的第一级 L1dTLB 虚拟->物理缓存。L1d 是虚拟索引的,因此可以进行 TLB 查找与从 L1d 缓存的那种方式获取 8 个标签+数据的集合并行)。假设命中 L1dTLB 和 L1d 缓存,加载执行单元会在大约 5 个周期后收到加载结果。

    该加载结果作为源操作数转发到 ALU 执行单元。 ALU 不关心它是imul eax, ebx 还是内存源操作数;一旦两个输入操作数都准备好,乘法 uop 就会立即分派到 ALU。


    jmp    DWORD PTR [eax*4+0x80497e8]
    

    是的,eax *4 是缩放索引。

    是的,0x80497e8 是 disp32 位移。在这种情况下,寻址方式的位移分量很可能被用作静态跳转表的地址。您可以将其视为这种寻址方式的基础。

    跳转到那个地址

    不,从该地址加载一个新的 EIP 值。由于方括号,这是一个内存间接跳转。

    你描述的应该是

    lea   eax, [eax*4+0x80497e8]       ; address calc
    jmp   eax                          ; jump to code at that address
    

    没有办法在一条指令中进行计算跳转,您总是需要将新的 EIP 值保存在寄存器中或作为数据从内存中获取。


    脚注 1:我们假设一个平面内存模型(段基数 = 0),因此我们可以忽略分段,就像在 Linux、Windows、OS X 或几乎任何 32 或64 位操作系统。所以地址计算给你一个线性地址。

    我还假设分页已启用,就像在主流操作系统下正常一样,所以它是一个虚拟地址,必须由 TLB 缓存的页表转换为物理地址。

    【讨论】:

    • 非常感谢您的解释!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-12-18
    • 1970-01-01
    • 1970-01-01
    • 2021-03-08
    • 1970-01-01
    • 2011-05-15
    相关资源
    最近更新 更多