好吧,我正在为正在开发的操作系统编写汇编程序。它进展得很好,我已经掌握了所有的mov指令,现在我想实现call和jmp之类的指令。我真的没有好的文档,所以我正在查看NASM生成的机器代码,以找出操作码等等。我想看看调用的操作码是什么,所以我编译了一些以标签开头的代码。我原以为调用操作码后的地址是00 00 00 00,但它是FB FF FF FF。我认为这与符号有关,所以我用调用0x000000编译了代码,看看发生了什么,地址完全相同(0xFBFFFFFF)。有人能解释一下吗?我很困惑。
显示正在反汇编的实际代码会很有用。这个数字很可能是一个小的endian负偏移。0xFFFFFFFB=-2s补码中的-5。你写了吗:
Label: call Label
如果调用是一个具有4字节相对偏移量的1字节操作码,这将是有意义的。
在32位用户模式x86代码中,CALL最常见的形式是CALL rel32
,它"调用"操作数处的一个点加上下一条指令的地址。这是一个近乎相对的呼叫。
作为参考,可以使用绝对调用,但编码是6字节而不是5字节;编码将是CCD_ 2。额外字节(14
)告诉指令读取将用作立即数的立即数位移。然而,这将需要根据加载程序的模块库进行重写;在重新定位时,相对呼叫不需要注意。
为了可视化这是如何工作的,在执行指令时,会发生以下情况:
- CCD_ 4(指令指针)被递增以指向下一指令
- 该地址被推送到堆栈上(以提供返回地址)
- 立即数(例如rel32值)被添加到
EIP
,并且 - 正常地从(新的)
[EIP]
读取下一条指令
当对该指令进行编码时,它看起来是这样的:E8 XX XX XX XX
。从中可以看出,指令的长度是5个字节。
由于EIP
按指令的长度递增,因此调用将相对于指令开始后5字节的点。因此,如果您的CALL 0x00000000指令的相对地址是0x00000000,则需要从EIP
中减去5;汇编程序已将绝对地址转换为相对地址。
偏移量可以为负。另外,请记住x86地址是小端序。因此,该指令是E8 FB FF FF FF
。
有趣的是,这个特定指令的结果是EIP+5
将不断被推送到堆栈上,直到生成异常(#SS(0)
)。
call
通常有各种形式,例如,一种使用绝对地址跳转到,另一种相对于当前地址跳转。这可能是相对的,尽管四个字节可能不是直接的偏移量。
如果有疑问,尤其是在实现汇编程序时,您可能需要查看手册或数据表。