浅析ARM架构下的函数的调用过程
ARM函数调用基本流程
ARM函数调用的基本流程如下:
-
调用者保存寄存器(Callee saved registers):在调用函数之前,调用者需要保存被调用者需要用到的寄存器,否则这些值会被调用函数所覆盖,导致逻辑错误。在ARM架构中,callee saved registers 都是 r4-r11,他们将被保存在当前堆栈帧中。
-
传递参数:函数调用需要传递参数给函数,ARM架构中使用寄存器 r0-r3 来传递前4个参数,超出4个参数时需要通过栈来传递。
-
跳转到被调用函数:使用 bl 指令跳转到被调用函数入口地址。
-
被调用函数处理:被调用函数处理时需要处理参数,所以需要使用 r0-r3 从调用函数中读取参数,然后在处理过程中需要保存callee 保存寄存器,然后完成处理。
-
返回结果:函数返回时将在r0中放置返回值,然后使用 bx lr指令跳转回调用函数的地址,同时恢复callee saved registers。
例子1
下面是一个简单的C函数,实现对传入参数x和y的加法运算。它的伪代码如下:
void add(int x, int y){
return x + y;
}
我们编写如下的ARM汇编代码来实现它:
// Definition of function 'add'
add:
//保存callee saved registers
push {r4-r7, lr}
//将参数保存到r4和r5中
mov r4, r0
mov r5, r1
//调用add函数进行加法运算
add r0, r4, r5
//恢复callee saved registers
pop {r4-r7, lr}
//返回
bx lr
在这个例子中,我们使用了 r4 和 r5 寄存器来保存传入的参数,使用了 r0 来存储函数的返回值。
例子2
下面是一个更加复杂的函数调用例子。我们假设有两个函数 foo 和 bar,其中 foo 调用了 bar 函数。 伪代码如下:
int bar(int x, int y) {
return x * y;
}
int foo(int x, int y, int z) {
int val = bar(x, y);
return val + z;
}
我们编写如下的ARM汇编代码来实现这两个函数:
// Definition of function 'bar'
bar:
//保存callee saved registers
push {r4-r7, lr}
//参数保存
mov r4, r0
mov r5, r1
//运算
mul r0, r4, r5
//callee saved registers 恢复
pop {r4-r7, lr}
bx lr
// Definition of function 'foo'
foo:
//callee saved registers 保存
push {r4-r7, lr}
//bar函数的第一个参数
mov r4, r0
mov r5, r1
//bar函数的调用
bl bar
//bar函数的返回值的存在的地址
mov r6, r0
//将第三个参数保存到r0中
mov r0, r2
//callee saved registers 恢复
pop {r4-r7, lr}
//返回最终值,r6 存储了 bar(x,y) 的返回值
add r0, r6, r0
bx lr
在这个例子中,我们使用了 r4 - r7 的寄存器来保存callee saved registers,使用了 r0 - r5 来传递参数,使用了 r6 来保存 bar 函数的返回值。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:浅析ARM架构下的函数的调用过程 - Python技术站