汇编调用c函数设置栈的原因

一.栈的整体作用
(1)保存现场/上下文
(2)传递参数:汇编代码调用c函数时,需传递参数

(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的代码的最后一行:
33d009f8:e1a0f00e mov pc, lr把lr的值,即之前保存的函数调用时候的PC值,赋值给现在的PC,这样就实现了函数的正确的返回,即返回到了函数调用时候下一个指令的位置。

(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的寄存器的使用的约定而设计的,你最好不要随便改变使用方式,最好还是按照其约定的来处理,这样程序更加符合规范。








你可能感兴趣的:(ARM裸机)