目录
一、C语言函数
本文用到ARM汇编指令,请参考:
本文用到编译器为在线编译器(ARM-GCC):
1、函数本质
函数的本质是一个代码块,这个代码块完成指定的逻辑,执行入口即为函数地址,当函数调用发生时,将函数的起始地址放到PC寄存器中,完成函数跳转。
函数示例如下:
int test_func1(int num) {
int ret = num + 5;
return ret;
}
int test_func2(int num) {
test_func1(6);
return 1;
}
定义两个函数,test_func1和test_func2。
test_func2调用test_func1,且传入参数。
2、函数调用、传参、以及返回值
将如上代码编译为汇编函数,如下
test_func1(int):
push {r7}
sub sp, sp, #20
add r7, sp, #0
str r0, [r7, #4]
ldr r3, [r7, #4]
adds r3, r3, #5
str r3, [r7, #12]
ldr r3, [r7, #12]
mov r0, r3
adds r7, r7, #20
mov sp, r7
ldr r7, [sp], #4
bx lr
test_func2(int):
push {r7, lr}
sub sp, sp, #8
add r7, sp, #0
str r0, [r7, #4]
movs r0, #6
bl test_func1(int)
movs r3, #1
mov r0, r3
adds r7, r7, #8
mov sp, r7
pop {r7, pc}
分析test_func2函数调用test_func1过程
movs r0, #6; 将函数入参保存到寄存器R0中
bl test_func1(int); 调用函数,本质是修改PC寄存器。
分析test_func1函数执行过程
test_func1(int):
push {r7} ;将寄存器R7压入栈中
sub sp, sp, #20; 栈指针自减20Byte,4个字,此长度为编译器根据函数中所需栈空间的大小计算而来
add r7, sp, #0; 将栈顶保存到寄存器R7中
str r0, [r7, #4]; R0寄存器此时为函数入参,所以将R0寄存器中的值保存到栈中
ldr r3, [r7, #4]; 将栈中的入参读到寄存器R3中
adds r3, r3, #5; R3中的值加5,再将结果保存到R3寄存器
str r3, [r7, #12]; 计算后的值保存到栈中
ldr r3, [r7, #12]; 读取结算后的结果到R3寄存器中
mov r0, r3; 将R3中的值保存到R0寄存器中
adds r7, r7, #20; 栈顶指针回退20Byte,4个字
mov sp, r7; 修改栈顶指针
ldr r7, [sp], #4;恢复R7寄存器 ,等同于POP操作
bx lr; 跳转到LR寄存器,LR寄存器为链接寄存器,保存函数调用后要返回的地址
3、传参以及返回值分析
函数传参是通过R0寄存器完成的(单参数情况,多参数情况一般会用到R1,R2,R3等,大空间参数通过指针传递地址来完成),在调用函数之前,先将参数保存到R0寄存器中,进入被调用的函数中后,从R0处读取参数,保存到栈中,至此完成函数参数传递。
返回值传参是通过R0寄存器完成的,在函数返回之前,将返回值保存到R0寄存器中,函数返回后,从R0寄存器读取返回结果,至此完成函数返回值传递。
二、函数应用
1、静态函数
使用static关键字修饰函数,表示此函数只能在本源码文件中调用。