供参考:英特尔call
instruction文档的HTML摘录。
我知道第3.1.1.3节解释了这一点,但我无法理解本手册,可能是因为它过于技术化了。
/2
和/3
内的FF /2
和FF /3
是什么?
r/m32
和m16:32
之间有什么区别? r/m32
涵盖了32位寄存器和内存操作数,因此不会使m16:32
冗余吗?
根据手册call ptr16:32
是“在操作数中给出的远程,绝对,地址”。如果我理解正确,这将允许我在给定的绝对32位地址处调用函数。如何在绝对地址0x717A60
调用该函数(我在MSVC中使用内联汇编程序)? call 0x717A60
给了我一个错误,call ds:0x717A60
已汇总到call [0x717A60]
。
以下机器代码对应哪个操作码? FF 15 90 98 76 70
答案 0 :(得分:2)
/2
与/3
的对比:请参阅指令摘要表中的 3.1.1.1O操作码列:
/ digit - 0到7之间的数字表示指令的ModR / M字节仅使用r / m(寄存器) 或内存)操作数。 reg字段包含为指令的操作码提供扩展的数字。
使用单操作数指令的mod / rm字节的/r
字段与inc r/m32
(FF /0
)等指令相同。
x86以这种方式使用多个单操作数指令重载一些操作码字节。 3位/r
字段变为另外3个操作码位而不是操作数。
r/m32
与m16:32
对比ptr16:32
A"远" call
加载CS
段注册以及IP
/ EIP
/ RIP
。正常"近" call只需要一个32位(或64位)地址,并且不会修改CS
。
far call
从未用于"正常"普通操作系统上的32或64位用户空间代码,因为它们都使用平面内存模型。
ptr16:32
是立即数,16位段值和32位绝对地址编码到指令中(小端,首先是32位偏移,然后是新的CS值)。 这是far call
。见this Q&A。汇编器将为far call some_symbol
生成此指令编码,并定义段some_symbol
中的段值。再次,您实际上不太可能在16位代码之外使用此值。但如果你这样做,请参阅this Q&A for how to get MASM to emit it。
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,因为它仍然是近距离呼叫。)
如何在绝对地址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作为退货地址。您还必须知道要在细分字段中放入什么内容。
FF 15 90 98 76 70
反汇编为call DWORD PTR ds:0x70769890
(在GNU objdump -Mintel
语法中),这是call r/m32
,其中操作数是[disp32]
绝对寻址模式。
(或者在64位代码中,它是与rel32
相关的RIP相对寻址模式,但仍然是从静态位置加载函数指针的内存间接call
。)
另请参阅x86代码wiki以获取更多文档链接。