2

我不明白为什么我收到一条非法指令,其中一段代码在 C 程序中作为汇编实现。我稍微解释一下。

我在特定地址有一个值,我想将该值移动到 EAX x86 寄存器中,例如

MOV EAX, [address]

在 C 代码中,我有指向该地址的指针。我以这种方式准备了这条指令的二进制代码:

MOV OpCode:

 7 6 5 4 3 2 1 0
|_|_|_|_|_|_|D|S|

D = 1 --> I want REG as destination
S = 1 --> 32 bit operand

                 D S
--> |1|0|0|0|1|0|1|1| --> 0x89


Mod R/M:

 7 6 5 4 3 2 1 0
|_|_|_|_|_|_|_|_|

--> MOD = |7|6|    bits
--> REG = |5|4|3|  bits
--> R/M = |2|1|0|  bits

MOD = Indirect  --> |0|0|
REG = EAX = 0   --> |0|0|0|

地址不在寄存器中,我想将内存数据移动到 EAX 寄存器。我有内存位置的值,所以:

R/M = |1|1|0| --> direct

--> |0|0|0|0|0|1|1|0| --> 0x06

Address = [byte0] [byte1] [byte2] [byte3]

--> 0x8b 0x06 [byte3] [byte2] [byte1] [byte0] 0xc3

通过这种方式,我获得了非法指令,但我不明白为什么。如果我使用 R/M = 101 我得到 0x8b 0x05 [byte3] [byte2] [byte1] [byte0] 0xc3 并且一切正常。

那么,问题出在哪里?有人可以从这张表中解释 MOD=00, R/M=110 的用法吗?直接???

在此处输入图像描述

MOD=00, R/M=101 有效,但在表中我看到从 DI 计算的内存位置,但我有一个直接的内存位置......

注意:使用 -m32 gcc 开关在 Ubuntu 20.04(64 位)上编译的代码

4

2 回答 2

3

关键问题是您在组装 32 位模式时正在查看 16 位 modr/m 表。每种操作模式都有自己的寻址模式,并且 modr/m 和 SIB 字节在内存操作数的编码方式上有所不同。

对于 32 位模式,直接寻址由 mod = 00, r/m = 101 选择。所以正确的 modr/m 字节为:

 00 000 101 XXXXXXXX XXXXXXXX XXXXXXXX XXXXXXXX
mod reg r/m ---------- displacement -----------

其中 reg = 000(编码eax)和 mod = 00,r/m = 101 编码直接地址。这是05您已经观察到正在工作的 modr/m 字节。

在 16 位模式下,直接寻址由 mod = 00, r/m = 110 选择,位移 2 字节,得到 modr/m 字节06.

67请注意,在 16 位和 32 位操作模式下,您可以通过提供地址大小覆盖前缀在 16 位和 32 位 modr/m 字节之间切换。

作为特定用例的替代选项,您还可以将A1操作码用于mov eax, off32. 此操作码直接从 32 位地址加载,没有 modr/m 字节,但寻址模式和目标寄存器都是硬编码的。

于 2021-11-03T12:30:44.067 回答
0

好的,我使用了错误的表。我从英特尔网站上找到了正确的表格。感谢您的快速答复

在此处输入图像描述

于 2021-11-03T12:29:39.890 回答