如何用英特尔机器语言指定寄存器?



我一直在研究英特尔机器语言,无论是在程序集列表中显示的生成代码中,还是在可执行文件本身的转储中,如从用 MASM 编写的程序生成的。我无法弄清楚机器说明中如何引用寄存器。我的 PC(显然还有许多其他(有 16 个寄存器,因此需要 4 位来引用所有这些寄存器,从 0 到 15。作为一个例子,我查看了lea指令,因为它有一个1个字节的操作码,并且只有一种格式。这是汇编程序源代码:

lea     rax, data2
lea     rcx, data2
lea     rdx, data2

Data2 位于程序数据部分的偏移量 5。以下是生成的机器语言:

488D05FE 1F0000 
488D0DF7 1F0000
488D15F0 1F0000

我知道十六进制 48 表示 64 位寄存器操作数和 8D是操作码,但其余的仍然是个谜。1F0000有什么用途?它是否引用了所有三个指令中相同的存储位置?如果是这样,那么 05FE、0DF7 和 15F0 必须表示三个寄存器,但用什么表示法?

我花了很多时间阅读 https://software.intel.com/en-us/download/intel-64-and-ia-32-architectures-sdm-combined-volumes-1-2a-2b-2c-2d-3a-3b-3c-3d-and-4,但我发现它不是很有帮助。例如,它从不对位和字节进行编号,以描述指令的哪些位服务于哪个功能,以及根据什么方案。它充满了细节,但基本上没有解释。

让我们看一下第一条指令:48 8D 05 FE 1F 00 00

第一个字节:48h0100 1000 b(我插入一个空格以使其更易于阅读(。 这是一个 REX 前缀字节;请参阅您链接的手册的第 535 页(又名第 2A 卷第 2-9 页,但为方便起见,我将使用绝对页码(。 它由前 4 位0100标识。 其余四个位分别称为W,R,X,B。 因此仅设置 W,表示操作数大小为 64 位。 我们稍后再来讨论其他问题。

如你所知,8DLEA的操作码;见第1149页。 自雷克斯以来。W被设置,它将在其第一个操作数r64中存储其第二个操作数m的有效地址。 查看第 1149 页上的第二个表,第一个操作数在 ModRM 字节的 reg 字段中编码,第二个操作数在 r/m 字段中编码。

050000 0101 b,是一个ModRM字节。 请参阅第 530 页上的图 2-2。这与 REX 前缀字节中的位相结合,如图 535 页图 2-4 所示(因为我们没有 SIB 字节(。 此编码是为了向后兼容 32 位指令。

  • mod 是 ModRM 字节的前两位:对我们来说00

  • reg 是接下来的三位:000。 REX 字节的 X 位被固定为高位,产生X.REG = 0.000

  • r/m 是低三位:101。 REX 字节的 B 位被固定为高位,产生B.R/M = 0.101

现在,英特尔的手册似乎只是真正解释了这些字段对于 32 位模式的含义;我在那里找不到关于 64 位模式情况的好解释。 因此,让我们看看其他地方,例如 https://wiki.osdev.org/X86-64_Instruction_Encoding。

X.REG的含义解释如下:0.000,对于需要通用寄存器的64位模式指令,RAX

对于 Mod 和 B.R/M,请参阅此表。 Mod 是00所以我们看第一行。 B.R/M =0.101标记为 "[RIP/EIP + disp32]"。 这意味着接下来的四个字节是指令指针RIP的32位位移;即与以下指令地址的偏移量。 请参阅英特尔手册第 538 页。 因此,这说明了最后四个字节,它们构成了小端序 32 位数字00001FFEh。 换句话说,内存操作数在下一条指令的地址之后1FFEh字节。 那大概就是data2所在的地方;汇编程序或链接器为您计算偏移量。

因此,第一个操作数是 RAX,第二个操作数是[RIP+00001FFEh],所以总体指令是

LEA RAX, [RIP+00001FFEh]

请注意,1F0000本身没有任何意义;它只是内存操作数位移的前三个字节。

现在下一个是类似的:48 8D 0D F7 1F 00 00. ModRM 现在00 001 101,所以X.REG0001,它编码RCX。 Mod和B.R/M再次编码RIP+disp32,现在位移00001FF7h。 请注意,这正好比上一条指令少 7 个;我们刚刚执行了一个 7 字节的指令,所以 RIP 增加了 7,所以一个少 7 的位移最终指向与以前完全相同的地方,即data2.

最后一个你可以做的:)

最新更新