13

的编码

call qword ptr [rax]
call qword ptr [rcx]

FF 10
FF 11

我可以看到最后一个数字 (0/1) 的来源(寄存器号),但我试图找出倒数第二个数字 (1) 的来源。根据AMD64 Architecture Programmer's Manual Volume 3: General-Purpose and System Instructions page 56,

"/digit - 表示 ModRM 字节仅指定一个寄存器或内存 (r/m) 操作数。该数字由 ModRM reg 字段指定,用作指令操作码扩展。有效数字值范围为 0 到 7。 "

等效的英特尔文档说类似的内容,并call通过寄存器指定编码为

FF /2

并且...我不知道这意味着什么,也不知道规范中的 2 如何连接到最终结果中的高 1 位。在任何地方都有不同措辞的解释吗?

4

3 回答 3

14

ModR/M 字节有 3 个字段:

bit 7 & bit 6 = mod
bit 5 through bit 3 = reg = /digit
bit 2 through bit 0 = r/m

这在Figure 2-1. Intel 64 and IA-32 Architectures Instruction Format卷中有所描述。2A 的Intel® 64 and IA-32 Architectures Software Developer’s Manual

所以,那里:

  0x10= 00.010.000(mod=0, reg/digit=2, r/m=0)

  0x11= 00.010.001(mod=0, reg/digit=2, r/m=1)。

于 2013-03-19T22:28:49.537 回答
9

我想您想查看英特尔® 64 和 IA-32 架构开发人员手册中的表 2-2:组合卷、卷 2:指令参考集、第 2 章:指令格式、2.1.5 ModR/M 的寻址模式编码和SIB 字节:

表 2-2。带有 ModR/M 字节的 32 位寻址形式

r8(/r) AL CL DL BL AH CH DH BH
r16(/r) AX CX DX BX SP BP SI DI
r32(/r) EAX ECX EDX EBX ESP EBP ESI EDI
毫米(/r) MM0 MM1 MM2 MM3 MM4 MM5 MM6 MM7
xmm(/r) XMM0 XMM1 XMM2 XMM3 XMM4 XMM5 XMM6 XMM7
(十进制) /digit (操作码) 0 1 2 3 4 5 6 7
(二进制) REG = 000 001 010 011 100 101 110 111
ModR/M 字节的有效地址 Mod R/M 值(十六进制)
[EAX] 00 000 00 08 10 18 20 28 30 38
[ECX] 001 01 09 11 19 21 29 31 39
[EDX] 010 02 0A 12 1A 22 2A 32 3A
[EBX] 011 03 0B 13 1B 23 2B 33 3B
[--][--] *1 100 04 0C 14 1C 24 2C 34 3C
显示32 *2 101 05 0D 15 1D 25 2D 35 3D
[ESI] 110 06 0E 16 1E 26 2E 36 3E
[EDI] 111 07 0F 17 1F 27 2F 37 3F
[EAX]+disp8 *3 01 000 40 48 50 58 60 68 70 78
[ECX]+disp8 001 41 49 51 59 61 69 71 79
[EDX]+disp8 010 42 4A 52 5A 62 6A 72 7A
[EBX]+disp8 011 43 4B 53 5B 63 6B 73 7B
[--][--]+disp8 100 44 4C 54 5C 64 6C 74 7C
[EBP]+disp8 101 45 4D 55 5D 65 6D 75 7D
[ESI]+disp8 110 46 4E 56 5E 66 6E 76 7E
[EDI]+disp8 111 47 4F 57 5F 67 6F 77 7F
[EAX]+disp32 10 000 80 88 90 98 A0 A8 B0 B8
[ECX]+disp32 001 81 89 91 99 A1 A9 B1 B9
[EDX]+disp32 010 82 8A 92 9A A2 AA B2 BA
[EBX]+disp32 011 83 8B 93 9B A3 AB B3 BB
[--][--]+disp32 100 84 8C 94 9C A4 AC B4 BC
[EBP]+disp32 101 85 8D 95 9D A5 AD B5 BD
[ESI]+disp32 110 86 8E 96 9E A6 AE B6 BE
[EDI]+disp32 111 87 8F 97 9F A7 AF B7 BF
EAX/AX/AL/MM0/XMM0 11 000 C0 C8 D0 D8 E0 E8 F0 F8
ECX/CX/CL/MM/XMM1 001 C1 C9 D1 D9 E1 E9 F1 F9
EDX/DX/DL/MM2/XMM2 010 C2 CA D2 DA E2 EA F2 FA
EBX/BX/BL/MM3/XMM3 011 C3 CB D3 DB E3 EB F3 FB
ESP/SP/AH/MM4/XMM4 100 C4 CC D4 DC E4 EC F4 FC
EBP/BP/CH/MM5/XMM5 101 C5 CD D5 DD E5 ED F5 FD
ESI/SI/DH/MM6/XMM6 110 C6 CE D6 DE E6 EE F6 FE
EDI/DI/BH/MM7/XMM7 111 C7 CF D7 DF E7 EF F7 FF
笔记:
1. [--][--] 命名法表示 SIB 在 ModR/M 字节之后。
2. disp32 命名法表示在 ModR/M 字节(或 SIB
字节(如果存在))并将其添加到索引中。
3. disp8 命名法表示在 ModR/M 字节(或 SIB
字节(如果存在))并且符号扩展并添加到索引中。
于 2013-03-19T22:25:38.643 回答
5

/2英特尔文档第 2A 卷中表 2-2 中的平均值查找(表和卷中的 2 与/2那里没有关系)。在左上角的那个表中有/digit. 因此,转到右侧的列并找到/2. 我们会回到那个。

现在,如果您查看call指令定义,您将看到Op/En“操作数编码”。

Op/En       Operand 1       Operand 2       Operand 3       Operand 4
D           Offset          NA              NA              NA
M           ModRM:r/m (r)   NA              NA              NA

还要注意call第一个表中的签名,例如这个,它是 64 位对应的rax用法:

Opcode    Instruction   Op/En
FF /2     CALL r/m64    M

M告诉我们M在下面的“操作数编码”(Op/En)表中查找,即:

Op/En       Operand 1       Operand 2     Operand 3       Operand 4
M           ModRM:r/m (r)   NA            NA              NA

所以操作数 1 是ModRM:r/m (r)。这(r)意味着读取(不写入)操作数。ModRM:r/m 表示操作数有一个 ModRM 字节,带有一个 r/m 值。rin表示“r/m寄存器”,m表示“内存”。

所以回到/2表 2-2 中的列,我们有010,就在说 的那一行REG。这是指 ModRM 中间“reg”段。

据此,我们有:

mod     description (relevant to us)
00      register indirect addressing mode
01      one-byte signed displacement follows addressing mode byte(s)
10      four-byte signed displacement follows addressing mode byte(s)
11      register addressing mode

由于我们使用[rax]的是寄存器间接寻址方式,所以00

所以我们有了 mod 和 reg,现在我们需要 r/m 来完成 ModRM 字节。

从网络上的其他地方r/m 字段编码使用哪个寄存器。如果我们回到表 2-2 和该列,并将其与左侧/2的 Mod 框匹配,并且我们使用该行(与您在 中使用的相同),我们将在. 同样,如果我们按照行(与您的相同),我们得到. 这给了我们:00EAXRAXcall [rax]10ECXRCXcall [rcx]11

FF 10       call [rax]
FF 11       call [rcx]

请注意,该表也显示了 r/m 值:000forrax001for rcx。这给了我们最终的 ModRM 字节。

ModRM         for        hex
00.010.000    rax        10
00.010.001    rcx        11

还要注意,如果你这样做call [eax],它的前缀67是十六进制:

67 FF 01

这对应于“地址大小覆盖前缀”。

于 2021-01-29T07:15:12.667 回答