这是从C编译的反汇编代码:
grid()
子例程调用(799d6e:4798 blx r3)的目标采用64位整数指针参数并返回64位整数。而且该例程是一个库函数,因此我无法对其进行任何修改。 此操作可以覆盖存储lr和r6值的堆栈吗?
答案 0 :(得分:4)
您说分支目标“采用64位整数指针参数并返回64位整数”,但事实并非如此。它使用一个指向64位整数的指针作为唯一参数(并且该指针的长度为32位,除非您使用的是aarch64,我怀疑其余代码是否存在);而且它什么也不会返回,只会覆盖您传入的参数所指向的64位值。我确定这就是您的意思,但是请谨慎使用术语,因为这两者之间的区别很重要!特别是,您调用的函数中都没有传递给我们的64位参数。
问题本身。了解编译器在此处执行的操作的关键是从第一行开始:
push {r0, r1, r4, r5, r6, lr}
ARM calling convention不需要r0
和r1
进行呼叫保留,那么它们在列表中做什么?答案是,编译器添加了这些“虚拟”推送以在堆栈上创建一些空间。上面的push
操作基本上等同于
push {r4, r5, r6, lr}
sub sp, sp, #0x08
除了保存指令外。当然,结果并不完全相同,因为r0
和r1
中的内容最终都被写入了这些位置。但是鉴于无法事先知道其中的内容,而且堆积的值无论如何都将被覆盖,因此没有任何影响。因此,我们有一个堆栈框架
lr
r6
r5
r4
(r1)
sp -> (r0)
,其中堆栈指针指向由r0
和r1
的虚拟推送创建的空间。现在我们有
mov r0, sp
将堆栈指针复制到r0
用作要调用的函数的指针参数,然后它将覆盖此位置的两个单词,从而导致堆栈帧为
lr
r6
r5
r4
(64-bit value, high word)
sp -> (64-bit value, low word)
除了blx r3
之外,您没有显示任何代码,因此无法确切说明函数末尾堆栈发生了什么。但是,如果此函数不返回任何参数,我希望看到一个匹配的
pop {r0, r1, r4, r5, r6, pc}
,这当然会导致您的64位结果保留在r0
和r1
中。但是,根据调用约定,这些寄存器都经过调用处理,所以没有问题。