今天,我们来看一下基于ARM汇编,如何实现函数的调用。
基础知识
在ARM汇编中,函数的前四个参数存放于 R0~R3寄存器中, 剩余的参数存放于栈中,返回值存放于r0。在栈中存放数值,可以避免在调用过程中,数据的丢失,因为寄存器有限,并且经常被更新。
Function names in C are like labels in assembly.
The PC(program counter register) holds the address of the currently executing instruction.
函数实现
函数原型
首先,通常函数的C代码,我们基本理解我们需要干嘛——实现加法。
int add_nums(int num1, int num2)
{
return num1 + num2;
}
int main()
{
add_nums(1,2};
return 0;
}
汇编实现
LR (Link Register) 用于存放函数调用后,接下来的指令的地址。
.global _start
_start:
mov r0, #1 //arg1
mov r1, #2 //arg2
push {r0, r1}
bl add_nums
mov r2, r0
pop {r0,r1}
add_nums:
add r0,r0,r1
bx lr
将r0, r1 存入栈中。这里可以观察到,sp指向的位置从0变成了一个很大的数字,在内存中查看对应的地址,可以看到,就是1,2!
在此前,我们提到了b可以作为无条件分支的关键词,此外,还有bl(branch with link)。bl除了可以进行分支之外,还可以存储PC的状态,保证在函数调用之后,回到调用之前的状态(PC地址)。
然后,通过step into,我们进入add_nums label,然后,对应的可以看到r0被更新为3. 通过bx指令,我们可以访问lr寄存器中的地址,对应的就是 mov r2, r0.
然后通过pop,我们基于栈中存储的元素更新了r0,r1,r0从3变成了1。可以观察到sp指向的地址变为0,可以理解为栈为空,但是,此前的地址中的数字并没有改变,还是1和2.
Take Away
bx lr指令用于访问存放于
Link Register (LR) 的地址。
push 和pop用于将数字存放于栈中,或者从栈中获取。
PUSH {R0, R1} // Push R0 and R1 onto the stack