研究的是V4版本ARM内核的函数调用规则。
MDK4.2、J-Link armV4.34、J-Link硬件仿真器V8版本。
ATPCS规定寄存器的使用规则如下:
1.子程序通过R0~R3来传递参数
2.子程序使用R4~R11来保存局部变量
3.寄存器R12用作scratch寄存器,记为ip(发现Linux内核中的汇编直接使用ip这个
符号)
4.R13为SP
5.R14为LR
6.R15为PC
截取自己工程中的代码片段:
这是在函数中调用uartInit()。
可以发现在正式跳转到子函数
BL uartInit
之前还执行了三步操作。
MOV R2,#_...
LDR R1,[PC,#...]
MOV R0,R2
首先第一条执行第一条命令可以获得uartInit函数的第一个参数UART1,但暂时将
参数保存在R2中,最后通过
MOV R0,R2
将第一个参数保存在R0寄存器中。
然后通过LDR执行第二条执行,将第二个参数保存在R1寄存器中。
通过计算可以知道R1中的值是0x4000009c
这个就是程序中定义的cUartArg变量的内存地址
第三个参数为NULL,所以不用执行。参数保存到R0、R1后开始跳转到子函数执行
可以发现函数在执行子函数代码之前显示将R4~R6以及R14寄存器保存在栈中,然后再将
R0、R1、R2的值赋值给R4~R6,因为在函数中形参是局部变量,ATPCS规则规定子程序
要使用R4~R11寄存器来保存局部变量,所以这里进行了赋值操作。并且在跳转的时候也将
LR寄存器进行了设置。
所有这些操作完毕然后执行子程序代码。
最后函数的返回值通过R0寄存器进行传递。
当参数大于4个时通过堆栈来传递参数。
更加具体的解释参考杜春雷<ARM体系结构与编程>