(3)保存临时变量:包括函数的非静态局部变量以及编译器自动生成的其他临时变量。
1.保存现场/上下文
在函数调用之前,应该将这些寄存器等现场,暂时保持起来(入栈push),等调用函数执行完毕返回后(出栈pop),再恢复现场。这样CPU就可以正确的继续执行了。
保存寄存器的值,一般用的是push指令,将对应的某些寄存器的。
C语言函数调用时通常会传递参数,这里使用二种传递参数的方法:
(1)本身传递的参数不多于4个,就可以通过寄存器传送参数。
(2)参数多于4个时,就得用栈了。
3.临时变量保存在栈中
包括函数的非静态局部变量以及编译器自动生成的其他临时变量。
4.举例分析C语言函数调用是如何使用栈的
arm-inux-objdump –d u-boot >dump_u-boot.txt可以得到dump_u-boot.txt文件。
该文件就是中,包含了u-boot中的程序的可执行的汇编代码.
例子:一个是clock_init,另外一个函数CopyCode2Ram:
33d0091c
33d0091c: e92d4070 push {r4, r5, r6, lr}
33d00920: e1a06000 mov r6, r0
33d00924: e1a05001 mov r5, r1
33d00928: e1a04002 mov r4, r2
33d0092c: ebffffef bl 33d008f0
......
33d00984: ebffff14 bl 33d005dc
......
33d009a8: e3a00000 mov r0, #0 ; 0x0
33d009ac: e8bd8070 pop {r4, r5, r6, pc}
33d009b0
33d009b0: e3a02313 mov r2, #1275068416 ;0x4c000000
33d009b4: e3a03005 mov r3, #5 ; 0x5
33d009b8: e5823014 str r3,
......
33d009f8: e1a0f00e mov pc, lr
(1)clock_init部分的代码可以看到该函数第一行:
33d009b0: e3a02313 mov r2, #1275068416 ;0x4c000000所用到的r2,r3等等寄存器,和前面调用clock_init之前所用到的寄存器r0,没有冲突,所以此处可以不用push去保存这类寄存器的值,不过有个寄存器要注意,那就是r14,即lr,其是在前面调用clock_init的时候,用的是bl指令,所以会自动把跳转时候的pc的值赋值给lr。
而clock_init的代码的最后一行:(2)CopyCode2Ram部分的代码其第一行:
33d0091c:e92d4070 push {r4, r5, r6, lr}用push指令,保存了r4,r5,r以及lr。而用push去保存lr,那是因为此函数里面,还有其他函数调用:
33d0092c: ebffffef bl 33d008f0
......
33d00984: ebffff14 bl 33d005dc
......
也用到了bl指令,会改变我们最开始进入clock_init时候的lr的值,所以我们要用push也暂时保存起来。
而对应地,CopyCode2Ram的最后一行:
33d009ac:e8bd8070 pop {r4, r5, r6,pc}就是把之前push的值,给pop出来,还给对应的寄存器,
其中最后一个是将开始push的lr的值,pop出来给赋给PC,因为实现了函数的返回。
另外,在CopyCode2Ram的倒数第二行是:
33d009a8:e3a00000 mov r0, #0 ;0x0是把0赋值给r0寄存器,这个就是我们所谓返回值的传递,是通过r0寄存器的。此处的返回值是0,也对应着C语言的源码中的“return0”.
对于使用哪个寄存器来传递返回值:
当然你也可以用其他暂时空闲没有用到的寄存器来传递返回值,但是这些处理方式,本身是根据ARM的APCS的寄存器的使用的约定而设计的,你最好不要随便改变使用方式,最好还是按照其约定的来处理,这样程序更加符合规范。