调用c++_ARM汇编程序调用 C 程序

本文详细介绍了在ARM汇编程序调用C程序时遵循的ATPCS规则,包括寄存器使用、数据栈管理和参数传递。讨论了如何通过寄存器和栈传递参数,以及C函数如何返回结果。并提供了一个实例分析,帮助理解实际操作过程。
摘要由CSDN通过智能技术生成

在ARM 汇编程序和C 程序之间相互调用时必须遵守 ATPCS 规则,ATPCS 规定了一些函数间调用的基本规则。

一、ATPCS 规则

ATPCS 即 ARM-THUMB procedure call standard(ARM-Thumb过程调用标准)的简称,是基于ARM指令集和THUMB指令集过程调用的规范,规定了调用函数如何传递参数,被调用函数如何获取参数,以何种方式传递函数返回值。基本的ATPCS规则包括寄存器使用规则、数据栈使用规则、参数传递规则。

1. 寄存器使用规则

ATPCS中ARM寄存器的使用规则及其名称:

寄存器别名使用规则
R15pc程序计数器
R14lr链接寄存器
R13sp数据栈寄存器
R12ip子程序内部调用的scratch寄存器
R11v8、fpARM状态局部变量寄存器8、帧指针
R10v7、slARM状态局部变量寄存器7、栈限制
R9v6ARM状态局部变量寄存器6
R8v5ARM状态局部变量寄存器5
R7v4ARM状态局部变量寄存器4
R6v3ARM状态局部变量寄存器3
R5v2ARM状态局部变量寄存器2
R4v1ARM状态局部变量寄存器1
R3a4参数/结果/scratch寄存器4
R2a3参数/结果/scratch寄存器3
R1a2参数/结果/scratch寄存器2
R0a1参数/结果/scratch寄存器1

寄存器 R0~R15 在 ATPCS 规则中的使用总结如下:

  • 在函数中,通过寄存器R0~R3来传递参数,被调用的函数在返回前无需恢复寄存器R0~R3的内容

  • 在函数中,通过寄存器R4~R11来保存局部变量

  • 寄存器R12用作函数间scratch寄存器(别名ip,过程调用中间临时寄存器)

  • 寄存器R13用作栈指针,记作SP,在函数中寄存器R13不能用做其他用途,寄存器SP在进入函数时的值和退出函数时的值必须相等

  • 寄存器R14用作链接寄存器,记作LR,它用于保存函数的返回地址,如果在函数中保存了返回地址,则 R14 可用作其它的用途

  • 寄存器R15是程序计数器,记作PC,它不能用作其他用途

2. 数据栈使用规则

数据栈有两个增长方向:向内存地址减小的方向增长时,称为DESCENDING栈;向内地址增加的方向增长时,称为ASCENDING栈。

所谓数据栈的增长就是移动栈指针。当栈指针指向栈顶元素(最后一个入栈的数据)时,称为FULL栈;当栈指针指向栈顶元素(最后一个入栈的数据)相邻的一个空的数据单元时,称为EMPTY栈。

综合以上这两个特点,数据栈可以分为以下4种:

  • FD Full Descending,满递减

  • ED Empty Descending,空递减

  • FA Full Ascending,满递增

  • EA Empty Ascending,空递增

缩写名称指令含义
FD满递减stmfd/ldmfd堆栈通过减小存储器的地址向下增长,堆栈指针指向内含有效数据项的最低地址
ED空递减stmed/ldmed堆栈通过减小存储器的地址向下增长,堆栈指针指向堆栈下的第一个空位置
FA满递增stmfa/ldmfa堆栈通过增大存储器的地址向上增长,堆栈指针指向内含有效数据项的最高地址
EA空递增stmea/ldmea堆栈通过增大村吃器的地址向上增长,堆栈指针指向堆栈上的第一个空位置

ATPCS规定数据栈为FD类型,并且对数据栈的操作是8字节对齐的。使用stmdb/ldmia批量内存访问指令来操作FD数据栈。

使用stmdb命令往数据栈中保存内容时,"先递减sp指针,再保存数据";

使用ldmia命令从数据栈中恢复数据时,"先获得数据,再递增sp指针";

sp指针总是指向栈顶元素,这刚好是FD栈的定义。

3. 参数传递规则

一般来说,当参数个数不超过4个时,使用r0~r3这4个寄存器来传递参数;如果参数个数超过4个,剩余的参数通过数据栈来传递。

二、汇编程序如何向 C 程序的函数传递参数

  • 当参数个数 <= 4,使用寄存器R0~R3来进行参数传递

  • 当参数个数 > 4,前四个参数按照上面方法传递,剩余参数传送到栈中,入栈的顺序与参数顺序相反,即最后一个参数先入栈

三、C 程序如何返回结果给汇编程序

  • 结果为一个32位的整数时,通过寄存器R0返回

  • 结果为一个64位整数时,通过R0和R1返回,依此类推.

  • 结果为一个浮点数时,通过浮点运算部件的寄存器f0,d0或s0返回 

  • 结果为一个复合的浮点数时,通过寄存器f0-fN或者d0~dN返回

  • 对于位数更多的结果,通过调用内存来传递

四、C 函数为何要用栈

总的来说,栈的作用就是: 保存现场/上下文,传递参数。

1. 保存现场/上下文

保存现场,也叫保存上下文。

现场,相当于案发现场,总有一些现场的情况,要记录下来的,否则被别人破坏掉之后,你就无法恢复现场了。而此处说的现场,就是指 CPU 运行的时候,用到了一些寄存器,比如 R0~R3,LR 等等,对于这些寄存器的值,如果你不保存而直接跳转到函数中去执行,那么很可能会被破坏了,因为函数执行需要用到这些寄存器。

因此在函数调用之前,应该将这些寄存器等现场,暂时保存起来,等调用函数执行完毕返回后,再恢复现场,这样 CPU 就可以正确的继续执行了。

保存寄存器的值,一般用的是 push 指令,将对应的某些寄存器的值,一个个放到栈中,即所谓的入栈。

然后待被调用的子函数执行完毕的时候,再调用 pop,把栈中的一个个的值,赋值给对应的入栈的寄存器,即所谓的出栈。

2. 传递参数
当函数被调用并且参数大于 4 个时,(不包括第 4 个参数)第 4 个参数后面的参数就保存在栈中。

五、分析一个实例

汇编程序:

.text.global _start_start:        ldr sp, =4096        /* 调用main */        bl mainhalt:        b halt

C程序:

int main(){  unsigned int *pGPFCON = (unsigned int *)0x56000050;  unsigned int *pGPFDAT = (unsigned int *)0x56000054;  *pGPFCON = 0x100;  *pGPFDAT = 0;  return 0;}

将上面程序进行反汇编:

Disassembly of section .text:00000000 <_start>:   0:  e3a0da01   mov  sp, #4096  ; 0x1000   4:  eb000000   bl  c 00000008 :   8:  eafffffe   b  8 0000000c :   c:  e1a0c00d   mov  ip, sp  10:  e92dd800   stmdb  sp!, {fp, ip, lr, pc}  14:  e24cb004   sub  fp, ip, #4  ; 0x4  18:  e24dd008   sub  sp, sp, #8  ; 0x8  1c:  e3a03456   mov  r3, #1442840576  ; 0x56000000  20:  e2833050   add  r3, r3, #80  ; 0x50  24:  e50b3010   str  r3, [fp, #-16]  28:  e3a03456   mov  r3, #1442840576  ; 0x56000000  2c:  e2833054   add  r3, r3, #84  ; 0x54  30:  e50b3014   str  r3, [fp, #-20]  34:  e51b2010   ldr  r2, [fp, #-16]  38:  e3a03c01   mov  r3, #256  ; 0x100  3c:  e5823000   str  r3, [r2]  40:  e51b2014   ldr  r2, [fp, #-20]  44:  e3a03000   mov  r3, #0  ; 0x0  48:  e5823000   str  r3, [r2]  4c:  e3a03000   mov  r3, #0  ; 0x0  50:  e1a00003   mov  r0, r3  54:  e24bd00c   sub  sp, fp, #12  ; 0xc  58:  e89da800   ldmia  sp, {fp, sp, pc}

简单分析下上面的反汇编:

mov  sp, #4096:设置栈地址在4k RAM的最高处,sp=4096;bl    c :调到c地址处的main函数,并保存下一行代码地址到lr,即lr=8;mov  ip, sp:给ip赋值sp的值,ip=sp=4096stmdb  sp!, {fp, ip, lr, pc}:按高编号寄存器存在高地址,依次将pc、lr、ip、fp存入sp-4中;sub  fp, ip, #4:fp的值为ip-4=4096-4=4092;sub  sp, sp, #8:sp的值为sp-8=(4096-4x4)-8=4072;mov  r3, #1442840576:r3赋值0x5600 0000; add  r3, r3, #80:r3的值加0x50,即r3=0x5600 0050;str  r3, [fp, #-16]:r3存入[fp-16]所在的地址,即地址4076处存放0x5600 0050;mov  r3, #1442840576:r3赋值0x5600 0000; add  r3, r3, #84:r3的值加0x54,即r3=0x5600 0054;str  r3, [fp, #-20]:r3存入[fp-20]所在的地址,即地址4072处存放0x5600 0054;ldr  r2, [fp, #-16]:r2取[fp-16]地址处的值,即[4076]地址的值,r2=0x5600 0050;mov  r3, #256:r3赋值为0x100;str  r3, [r2]:将r3写到r2内容所对应的地址,即0x5600 0050地址处的值为0x100;;对应c语言*pGPFCON = 0x100;;ldr  r2, [fp, #-20]:r2取[fp-20]地址处的值,即[4072]地址的值,r2=0x5600 0054;mov  r3, #0:r3赋值为0x00;str  r3, [r2]:将r3写到r2内容所对应的地址,即0x5600 0054地址处的值为0x00;对应c语言*pGPFDAT = 0;mov  r3, #0:r3赋值为0x00;mov  r0, r3:r0=r3=0x00;sub  sp, fp, #12:sp=fp-12=4092-12=4080;ldmia  sp, {fp, sp, pc}:从栈中恢复寄存器,fp=4080地址处的值=原来的fp,sp=4084地址处的值=4096,pc=4088地址处的值=8,随后调到0x08地址处继续执行。

内存数据情况:

fbe6e1637ff50f9bb44110a83d737c4e.png

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值