x86 asm 中调用指令的编码有什么区别?
What is the difference between the encodings for the call instruction in x86 asm?
供参考:英特尔 call
instruction 文档的 HTML 摘录。
我知道第 3.1.1.3 节对此进行了解释,但我无法理解该手册,可能是因为它太技术化了。
FF /2
和FF /3
里面的/2
和/3
是什么?
r/m32
和m16:32
有什么区别? r/m32
涵盖了 32 位寄存器和内存操作数,所以这不会使 m16:32
变得多余吗?
根据手册call ptr16:32
是一个"Call far, absolute, address given in operand"。如果我理解正确,这将允许我在给定的绝对 32 位地址调用函数。如何调用绝对地址 0x717A60
处的函数(我在 MSVC 中使用内联汇编器)? call 0x717A60
给我一个错误,call ds:0x717A60
被组装成 call [0x717A60]
。
下面的机器码对应哪个操作码? FF 15 90 98 76 70
/2
vs. /3
:见3.1.1.1O Opcode Column in the Instruction Summary Table:
/digit — A digit between 0 and 7 indicates that the ModR/M byte of the instruction uses only the r/m (register
or memory) operand. The reg field contains the digit that provides an extension to the instruction's opcode.
这里使用单操作数指令的 mod/rm 字节的 /r
字段与 inc r/m32
(FF /0
).
x86 以这种方式用多个单操作数指令重载了几个操作码字节。 3 位 /r
字段成为另外 3 个操作码位而不是操作数。更多详情:
- How to read the Intel Opcode notation
- What does the /4 mean in FF /4?
- x64 instruction encoding and the ModRM byte - 有关 modrm 字节中字段的详细信息。
r/m32
对比 m16:32
对比 ptr16:32
另见 x86 function call types
“far”call
加载 CS
段寄存器以及 IP
/EIP
/RIP
。正常的“near”调用只需要一个 32 位(或 64 位)地址,并且不会修改 CS
.
far call
从未在普通操作系统的“普通”32 位或 64 位用户代码中使用 space,因为它们都使用平面内存模型。
ptr16:32
是一个立即数,16位的段值和32位的绝对地址编码到指令中(little-endian,32位偏移量在前,然后是新的CS值) . 这是一个far call
。参见 this Q&A. Assemblers will generate this instruction encoding for far call some_symbol
, taking the segment value from the segment some_symbol
is defined in. Again, you're really unlikely to ever use this outside of 16-bit code. But if you do, see 。
m16:32
是一个6字节的内存操作数,从ModR/M字节编码的有效地址加载。 这是另一个远程调用。所以 call far [eax]
从 eax
中的地址加载 48 位。只有内存寻址模式对于 ModR/M 字节是合法的,因为指令需要的数据多于寄存器的宽度。 (即 call far eax
不合法)
r/m32
是内存或寄存器操作数 用于近调用。 你从 call eax
得到这个或 call [eax]
。当然任何寻址方式都是合法的,例如call FS:[edi + esi*4 + some_table]
。 FS 段覆盖前缀适用于函数指针加载的位置,而不是它跳转到的段。 (即它不会改变 CS,因为它仍然是一个接近的呼叫。)
How can I call the function at the absolute address 0x717A60
参见 Call an absolute pointer in x86 machine code。
如果您的代码不必与位置无关,那么到目前为止,最好的选择是汇编成 call rel32
并具有正确的相对位移以到达该地址。在 NASM 和 AT&T 语法中,您可以简单地编写 call 0x717A60
,汇编器 + 链接器会处理它。我不确定如何在 MASM 中编写它; MSVC 内联 asm 不接受 call 123456h
.
call
附近没有绝对直接的编码,所以如果你确实需要 PIC,那么你应该做类似 mov eax, 0x717A60
/ call eax
.
的事情
您不想使用 call far
绝对直接调用,因为它可能 慢很多 ,并且推送 CS:EIP 而不是仅仅作为 EIP return 地址。您还必须知道要在段字段中输入什么。
FF 15 90 98 76 70
反汇编为 call DWORD PTR ds:0x70769890
(在 GNU objdump -Mintel
语法中),这是一个 call r/m32
,其中操作数是 [disp32]
绝对寻址模式。
(或者在 64 位代码中,它是一个具有 rel32
的 RIP 相对寻址模式,但仍然是一个从静态位置加载函数指针的内存间接 call
。 )
另请参阅 x86 标签 wiki 以获取更多文档链接。
供参考:英特尔 call
instruction 文档的 HTML 摘录。
我知道第 3.1.1.3 节对此进行了解释,但我无法理解该手册,可能是因为它太技术化了。
FF /2
和FF /3
里面的/2
和/3
是什么?r/m32
和m16:32
有什么区别?r/m32
涵盖了 32 位寄存器和内存操作数,所以这不会使m16:32
变得多余吗?根据手册
call ptr16:32
是一个"Call far, absolute, address given in operand"。如果我理解正确,这将允许我在给定的绝对 32 位地址调用函数。如何调用绝对地址0x717A60
处的函数(我在 MSVC 中使用内联汇编器)?call 0x717A60
给我一个错误,call ds:0x717A60
被组装成call [0x717A60]
。下面的机器码对应哪个操作码?
FF 15 90 98 76 70
/2
vs./3
:见3.1.1.1O Opcode Column in the Instruction Summary Table:
/digit — A digit between 0 and 7 indicates that the ModR/M byte of the instruction uses only the r/m (register or memory) operand. The reg field contains the digit that provides an extension to the instruction's opcode.
这里使用单操作数指令的 mod/rm 字节的 /r
字段与 inc r/m32
(FF /0
).
x86 以这种方式用多个单操作数指令重载了几个操作码字节。 3 位 /r
字段成为另外 3 个操作码位而不是操作数。更多详情:
- How to read the Intel Opcode notation
- What does the /4 mean in FF /4?
- x64 instruction encoding and the ModRM byte - 有关 modrm 字节中字段的详细信息。
r/m32
对比 m16:32
对比 ptr16:32
另见 x86 function call types
“far”call
加载 CS
段寄存器以及 IP
/EIP
/RIP
。正常的“near”调用只需要一个 32 位(或 64 位)地址,并且不会修改 CS
.
far call
从未在普通操作系统的“普通”32 位或 64 位用户代码中使用 space,因为它们都使用平面内存模型。
ptr16:32
是一个立即数,16位的段值和32位的绝对地址编码到指令中(little-endian,32位偏移量在前,然后是新的CS值) . 这是一个far call
。参见 this Q&A. Assemblers will generate this instruction encoding for far call some_symbol
, taking the segment value from the segment some_symbol
is defined in. Again, you're really unlikely to ever use this outside of 16-bit code. But if you do, see
m16:32
是一个6字节的内存操作数,从ModR/M字节编码的有效地址加载。 这是另一个远程调用。所以 call far [eax]
从 eax
中的地址加载 48 位。只有内存寻址模式对于 ModR/M 字节是合法的,因为指令需要的数据多于寄存器的宽度。 (即 call far eax
不合法)
r/m32
是内存或寄存器操作数 用于近调用。 你从 call eax
得到这个或 call [eax]
。当然任何寻址方式都是合法的,例如call FS:[edi + esi*4 + some_table]
。 FS 段覆盖前缀适用于函数指针加载的位置,而不是它跳转到的段。 (即它不会改变 CS,因为它仍然是一个接近的呼叫。)
How can I call the function at the absolute address 0x717A60
参见 Call an absolute pointer in x86 machine code。
如果您的代码不必与位置无关,那么到目前为止,最好的选择是汇编成 call rel32
并具有正确的相对位移以到达该地址。在 NASM 和 AT&T 语法中,您可以简单地编写 call 0x717A60
,汇编器 + 链接器会处理它。我不确定如何在 MASM 中编写它; MSVC 内联 asm 不接受 call 123456h
.
call
附近没有绝对直接的编码,所以如果你确实需要 PIC,那么你应该做类似 mov eax, 0x717A60
/ call eax
.
您不想使用 call far
绝对直接调用,因为它可能 慢很多 ,并且推送 CS:EIP 而不是仅仅作为 EIP return 地址。您还必须知道要在段字段中输入什么。
FF 15 90 98 76 70
反汇编为 call DWORD PTR ds:0x70769890
(在 GNU objdump -Mintel
语法中),这是一个 call r/m32
,其中操作数是 [disp32]
绝对寻址模式。
(或者在 64 位代码中,它是一个具有 rel32
的 RIP 相对寻址模式,但仍然是一个从静态位置加载函数指针的内存间接 call
。 )
另请参阅 x86 标签 wiki 以获取更多文档链接。