我是ARM编程的新手,我不知道在我的函数开始时哪些寄存器要推送到堆栈。另外,我在哪里可以找到传递给函数的参数?谢谢!
答案 0 :(得分:0)
为什么不试试这些工具很容易实现,它们符合调用约定,可以更容易地将您看到的内容与约定语言进行比较。
extern unsigned int more_fun ( unsigned int a, unsigned int b );
unsigned int fun0 ( unsigned int a, unsigned int b )
{
return(more_fun(a+1,b+2)+3);
}
构建,反汇编和检查(最好是反汇编而不是试图通过编译器生成的汇编语言,但是如果你把-save-temps放在gcc命令行上,你就会得到它们。
arm-none-eabi-gcc -O2 -c so.c -o so.o
arm-none-eabi-objdump -D so.o
so.o: file format elf32-littlearm
Disassembly of section .text:
00000000 <fun0>:
0: e92d4010 push {r4, lr}
4: e2811002 add r1, r1, #2
8: e2800001 add r0, r0, #1
c: ebfffffe bl 0 <more_fun>
10: e8bd4010 pop {r4, lr}
14: e2800003 add r0, r0, #3
18: e12fff1e bx lr
当然,如果要调用其他函数,则必须保存返回地址。这表明r0是第二个参数r1,r0是返回值所在的寄存器。与r4有什么关系?这是因为约定要求堆栈64位对齐,以便在调用或返回之前推送或弹出偶数个寄存器(编译器不会始终保持堆栈64位对齐,BTW)。
extern unsigned int more_fun ( unsigned int a, unsigned int b );
unsigned int fun0 ( unsigned int a, unsigned long long b )
{
return(more_fun(a+1,b+2)+3);
}
32位量很容易,64位他们做这样的事情
00000000 <fun0>:
0: e92d4010 push {r4, lr}
4: e2821002 add r1, r2, #2
8: e2800001 add r0, r0, #1
c: ebfffffe bl 0 <more_fun>
10: e8bd4010 pop {r4, lr}
14: e2800003 add r0, r0, #3
18: e12fff1e bx lr
第一个参数取r0,第二个是64位,所以它们跳过r1并使用r2 / r3,但由于这是从64位调整到32位以调用下一个函数,所以它们只使用r2。作为下一个函数的32位参数,需要在r1中,因此移动。
extern unsigned int more_fun ( unsigned int a, unsigned long long b );
unsigned int fun0 ( unsigned int a, unsigned long long b )
{
return(more_fun(a+1,b+2)+3);
}
00000000 <fun0>:
0: e2922002 adds r2, r2, #2
4: e92d4010 push {r4, lr}
8: e2a33000 adc r3, r3, #0
c: e2800001 add r0, r0, #1
10: ebfffffe bl 0 <more_fun>
14: e8bd4010 pop {r4, lr}
18: e2800003 add r0, r0, #3
1c: e12fff1e bx lr
r0-r3用于第一个参数,直到它们被消耗,之后它使用堆栈作为参数。如图所示,可以轻松地对函数进行原型设计,以确定您正在处理的编译器正在做什么,然后从中查看规范以查看编译器正在执行的操作。