基座与位移之间的差异

时间:2018-09-29 00:33:05

标签: assembly x86 intel addressing-mode

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

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等于索引*标度吗?
-0x80497e8是位移吗?

因此要在方括号内获取地址,这是我们应该遵循的顺序吗?

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

在我的理解中,[base + index * scales]用于获取内部和数组中的值。 基数是指向数组中第一个元素的指针。 索引实际上是我们想要的值存储在其中的索引 比例尺是数组包含的日期的大小

我的问题是,当您在方程式中添加位移时,位移用于什么目的? 当位移为负值时,这意味着什么?

1 个答案:

答案 0 :(得分:2)

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

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

  

当位移为负值时是什么意思?

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

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


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

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

    人们经常使用“偏移量”来描述位移,并且通常没有混淆,因为从上下文中很明显,他们在谈论恒定偏移量(使用x86 seg:off以外的英语单词offset) ,但是我喜欢使用“位移”来描述位移。

  • 基:寻址模式的非索引寄存器组件(如果有的话)。 (“无基址寄存器”的编码表示有一个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]解码为两个指令:一个脉冲ALU操作和一个加载。 ALU操作必须等待加载结果。 (有趣的事实:除了无序调度程序外,对于大多数管道,两个微操作实际上都微融合为单个uop。有关更多信息,请参见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或32 64位操作系统。因此,地址计算会为您提供线性地址。

我还假设像在主流OS上正常启用分页一样,因此这是一个虚拟地址,必须通过TLB缓存的页表将其转换为物理地址。