示例代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| struct foo {int a; int b; int c; int d; int e;} g_stTest;
volatile struct foo testFun2(int a, int b, int c, int d, int e) { struct foo stTest; stTest.a = a; stTest.b = b; stTest.c = c; stTest.d = d; stTest.e = e; return stTest; };
volatile void testFun1(void) { g_stTest = testFun2(1, 2, 3, 4, 5); }
|
反汇编
用gcc工具链编译s32k144平台的代码,通过ozone分析elf得到反汇编如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| volatile void testFun1(void) {
g_stTest = testFun2(1, 2, 3, 4, 5); }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| volatile struct foo testFun2(int a, int b, int c, int d, int e) {
struct foo stTest; stTest.a = a; stTest.b = b; stTest.c = c; stTest.d = d; stTest.e = e; return stTest; };
|
分析
- 在进入函数后,首先会将R4, R5, R7寄存器压栈,使用PUSH指令后SP会自动减去压栈空间的大小。
- 然后是SP减去一个直接数,是在给局部变量、形参和参数传递开辟栈区空间。
- R7 = SP + 偏移,R7是局部变量所在栈区的地址,偏移的大小是函数内调用函数时,参数传递所需要的空间大小。
g_stTest = testFun2(1, 2, 3, 4, 5);
反汇编中,参数4, 5通过栈传递,参数1, 2, 3通过R1~R3寄存器传递。
- 在
testFun2
函数进入后,STR R0, [R7, #12]
等指令,会将R0~R3寄存器传递的参数存入(局部变量)栈中,为什么传参的还有R0?
- 重新分析
g_stTest = testFun2(1, 2, 3, 4, 5);
反汇编,R0传入的是R7(局部变量地址),在testFun2
函数return stTest;
时,将stTest写入R0传入的地址区域。
- 在退出函数时,如果函数有return参数,会将return参数写入R0,若大于4字节会将进入函数时R0传入的地址返回。
- 在退出函数时,会将SP改回进入函数前的SP,并使用POP指令出栈(POP指令会增加SP),最后跳转出函数(还可以直接POP取出PC跳转)。