示例代码
| 1 | struct foo {int a; int b; int c; int d; int e;} g_stTest; | 
反汇编
用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
27volatile void testFun1(void)
{
// 0002C74C   PUSH           {R4-R5, R7, LR}
// 0002C74E   SUB            SP, SP, #32
// 0002C750   ADD            R7, SP, #8
    g_stTest = testFun2(1, 2, 3, 4, 5);
    // 0002C752   LDR            R4, =g_stTest                 ; [PC, #40] [0x0002C77C] =0x20002318
    // 0002C754   MOV            R2, R7
    // 0002C756   MOVS           R3, #4
    // 0002C758   STR            R3, [SP, #0]
    // 0002C75A   MOVS           R3, #5
    // 0002C75C   STR            R3, [SP, #4]
    // 0002C75E   MOV            R0, R2
    // 0002C760   MOVS           R1, #1
    // 0002C762   MOVS           R2, #2
    // 0002C764   MOVS           R3, #3
    // 0002C766   BL             testFun2                      ; 0x0002C710
    // 0002C76A   MOV            R5, R4
    // 0002C76C   MOV            R4, R7
    // 0002C76E   LDM            R4!, {R0-R3}
    // 0002C770   STM            R5!, {R0-R3}
    // 0002C772   LDR            R3, [R4]
    // 0002C774   STR            R3, [R5]
}
// 0002C776   ADDS           R7, #24
// 0002C778   MOV            SP, R7
// 0002C77A   POP            {R4-R5, R7, PC}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
39volatile struct foo testFun2(int a, int b, int c, int d, int e)
{
// 0002C710   PUSH           {R4-R5, R7}
// 0002C712   SUB            SP, SP, #44
// 0002C714   ADD            R7, SP, #0
// 0002C716   STR            R0, [R7, #12]
// 0002C718   STR            R1, [R7, #8]
// 0002C71A   STR            R2, [R7, #4]
// 0002C71C   STR            R3, [R7]
    struct foo stTest;
    stTest.a = a;
    // 0002C71E   LDR            R3, [R7, #8]
    // 0002C720   STR            R3, [R7, #20]
    stTest.b = b;
    // 0002C722   LDR            R3, [R7, #4]
    // 0002C724   STR            R3, [R7, #24]
    stTest.c = c;
    // 0002C726   LDR            R3, [R7]
    // 0002C728   STR            R3, [R7, #28]
    stTest.d = d;
    // 0002C72A   LDR            R3, [R7, #56]
    // 0002C72C   STR            R3, [R7, #32]
    stTest.e = e;
    // 0002C72E   LDR            R3, [R7, #60]
    // 0002C730   STR            R3, [R7, #36]
    return stTest;
    // 0002C732   LDR            R3, [R7, #12]
    // 0002C734   MOV            R5, R3
    // 0002C736   ADD.W          R4, R7, #20
    // 0002C73A   LDM            R4!, {R0-R3}
    // 0002C73C   STM            R5!, {R0-R3}
    // 0002C73E   LDR            R3, [R4]
    // 0002C740   STR            R3, [R5]
};
// 0002C742   LDR            R0, [R7, #12]
// 0002C744   ADDS           R7, #44
// 0002C746   MOV            SP, R7
// 0002C748   POP            {R4-R5, R7}
// 0002C74A   BX             LR
分析
- 在进入函数后,首先会将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跳转)。