初学者汇编语言C-减法查找添加的CPU

问题描述

000000000040050f <oranges>:
...
40053f: 89 cf             mov %eax,%edi
400541: e8 a7 ff ff ff    callq 4004ed<apples>
400546: 0f af c3          imul %ebx,%eax
...

桔子两次叫苹果。苹果从0x00000000004004ed开始。但是,在机器代码中第二次调用苹果时,函数调用中的数字为0xFFFFFFA7

我需要弄清楚cpu加上0xFFFFFFA7多少个数字才能获得apple()0x00000000004004ed的地址

本质上,我需要做一个减法问题。我如何减去0xFFFFFFA7和0x00000000004004ed以查找添加cpu内容?进行转换以使其有意义的正确方法是什么?

解决方法

call rel32相对于调用指令的 end

小尾数rel32是2的补码整数,因此0xFFFFFFA7是一个小的负数。 0xFFFFFFA7 - 2^32 = -89 (十进制),即向后跳89个字节。

您已将4字节的Little-endian rel32位移正确解码为二进制整数,但没有将其重新解释为有符号2的补码。 (它不会被减去,而是被添加。这就是为什么它是负数的原因。)

e8 a7 ff ff ff callq rel32在地址0x400546(下一条指令的开始)处结束,因此在执行期间将是RIP。执行后的新RIP
0x400546 - 89 = 0x4004ed ,与打印的objdump -d相同。
objdump当然会以与我相同的方式计算该地址。

(尽管objdump可能在添加到64位代码地址之前将位移符号扩展为64位。计算出位模式0xFFFFFFA7表示-89十进制作为2的补码整数基本上就像读取那些将4个字节放入int32_t中并将其添加到uint64_t中。(https://www.felixcloutier.com/x86/call的英特尔手册还将该过程描述为对rel32进行符号扩展以进行二进制加法,但这只是另一种表达方式相同的数学运算以更机器友好的方式显示,除了符号扩展以外,所有这些都在任何模式下对直接相对calljmp指令都有效,jmp rel8使用8位2的补码分支位移。)


半相关:How does $ work in NASM,exactly?的示例是将call手动编码为给定的目标地址。

,

一目了然[...]

您正在以64位模式使用x86。

64位模式具有一种特殊的寻址模式,称为“ RIP相对”寻址。

编辑:尽管偏移量的计算方法相同,但从Peter看来,寻址方式实际上是call rel32,而不是RIP relative

%rip寄存器是程序计数器。每条指令都会改变。

因此,当使用此模式时,偏移量是目标地址(例如apples)与当前指令的地址(与指令的%rip中的地址)之间的距离。

由于您有两条 callq指令(根据您的描述,但代码中未显示 ),它们每个都有不同的地址,因此偏移量到apples的地方会有所不同。

这允许“位置无关代码”。它还允许使用偏移量,该偏移量通常小于完整的64位绝对地址。这就是callq指令(操作码+偏移量/地址)只有5个字节(相对于9个字节)的原因,因为偏移量是32位有符号数。


更新:

我认为可能涉及撕裂。在这种情况下,您能帮助我解释如何找到rip%或如何解决该特定问题吗?

您可以执行以下操作:objdump --disassemble myprogram进行反汇编并查看反汇编。或者,您可以使用gdb命令通过调试器(例如disassemble)进行此操作。

在您的列表中,callq的地址为0x400541,并且[您提到] apples的地址为0x4004ed。

因此,与callq指令的 start 的偏移是:

-84 FFFFFFFFFFFFFFAC

但是,指令的偏移量为:

0xFFFFFFFA7

(请记住,反汇编只是输出字节,因此我们必须手动反转字节,因为偏移量是little-endian)。

因此,这意味着使用的%rip值不是指令的 start ,而是 end 指令。

因此,我们必须按照指令的长度[5]来调整偏移量,以获得0xFFFFFFA7。也就是说,%rip指令[使用的callq值是指令的地址+5。在伪代码中,计算为:

offset = apples - (&callq + 5)