对于上面的解释的堆栈的作用显得有些抽象,此处再用例子来简单说明一下,就容易明白了:
用:
arm-inux-objdump –d u-boot > dump_u-boot.txt
可以得到dump_u-boot.txt文件。该文件就是中,包含了u-boot中的程序的可执行的汇编代码,其中我们可以看到C语言的函数的源代码,到底对应着那些汇编代码。
下面贴出两个函数的汇编代码,
一个是clock_init,另一个是与clock_init在同一C源文件中的,另外一个函数CopyCode2Ram
33d0091c <CopyCode2Ram>: 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 <bBootFrmNORFlash> ... ... 33d00984: ebffff14 bl 33d005dc <nand_read_ll> ... ... 33d009a8: e3a00000 mov r0, #0 ; 0x0 33d009ac: e8bd8070 pop {r4, r5, r6, pc} 33d009b0 <clock_init>: 33d009b0: e3a02313 mov r2, #1275068416 ; 0x4c000000 33d009b4: e3a03005 mov r3, #5 ; 0x5 33d009b8: e5823014 str r3, [r2, #20] ... ... 33d009f8: e1a0f00e mov pc, lr
此处就是我们所期望的,用push指令,保存了r4,r5,r以及lr。 用push去保存r4,r5,r6,那是因为所谓的保存现场,以后后续函数返回时候再恢复现场, | |
上述用push去保存lr,那是因为函数CopyCode2Ram里面在此处调用了bBootFrmNORFlash 以及也调用了nand_read_ll: 33d00984: ebffff14 bl 33d005dc <nand_read_ll> 也用到了bl指令,会改变我们最开始进入clock_init时候的lr的值,所以我们要用push也暂时保存起来。 | |
把0赋值给r0寄存器,这个就是我们所谓返回值的传递,是通过r0寄存器的。 此处的返回值是0,也对应着C语言的源码中的“return 0”. | |
把之前push的值,给pop出来,还给对应的寄存器,其中最后一个是将开始push的lr的值,pop出来给赋给PC,因为实现了函数的返回。 | |
可以看到此处是该函数第一行 其中没有我们所期望的push指令,没有去将一些寄存器的值放到堆栈中。这是因为,我们clock_init这部分的内容,所用到的r2,r3等寄存器,和前面调用clock_init之前所用到的寄存器r0,没有冲突,所以此处可以不用push去保存这类寄存器的值,不过有个寄存器要注意,那就是r14,即lr,其是在前面调用clock_init的时候,用的是bl指令,所以会自动把跳转时候的pc的值赋值给lr,所以也不需要push指令去将PC的值保存到堆栈中。 | |
而此处是clock_init的代码的最后一行 就是我们常见的mov pc, lr,把lr的值,即之前保存的函数调用时候的PC值,赋值给现在的PC,这样就实现了函数的正确的返回,即返回到了函数调用时候下一个指令的位置。 这样CPU就可以继续执行原先函数内剩下那部分的代码了。 |
对于使用哪个寄存器来传递返回值 | |
---|---|
当然你也可以用其他暂时空闲没有用到的寄存器来传递返回值,但是这些处理方式,本身是根据ARM的APCS的寄存器的使用的约定而设计的,你最好不要随便改变使用方式,最好还是按照其约定的来处理,这样程序更加符合规范。 |