ARM函数调用规则

本文详细介绍了ARM体系中使用ATPCS标准进行C函数调用时的参数传递规则及返回流程,特别指出ATPCS对于参数数量限制和堆栈使用的策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

对于ARM体系来说,不同语言撰写的函数之间相互调用(mix calls)遵循的是 ATPCS(ARM-Thumb Procedure Call Standard),ATPCS主要是定义了函数呼叫时参数的传递规则以及如何从函数返回,关于ATPCS的详细内容可以查看ADS1.2 Online Books ——Developer Guide的2.1节。这篇文档要讲的是 汇编代码中对C函数调用时如何进行参数的传递以及如何从C函数正确返回.

    不同于x86的参数传递规则,ATPCS建议函数的形参不超过4个,如果形参个数少于或等于4,则形参由R0,R1,R2,R3四个寄存器进行传递;若形参个数大于4,大于4的部分必须通过堆栈进行传递,即压栈.
<think>我们正在讨论ARM架构下函数调用栈的工作过程。根据ARM架构的特点,函数调用栈是通过栈指针(SP)和链接寄存器(LR)来管理的。当一个函数调用另一个函数时,它会将返回地址存入LR,同时调整栈指针来为局部变量和保存的寄存器分配空间。 在ARM中,函数调用遵循一定的约定,称为AAPCS(ARM Architecture Procedure Call Standard)。根据这个标准,函数调用时,参数通过寄存器R0-R3传递,如果参数多于4个,则多余的参数通过栈传递。函数返回时,返回值通常存放在R0中。 函数调用栈的关键操作包括: 1. 调用函数时(使用BL指令),将返回地址(当前PC+4)存入LR(R14)。 2. 被调用函数负责保存它将要修改的寄存器(包括LR,因为LR在函数执行过程中可能会被覆盖),以及为局部变量分配栈空间。 3. 被调用函数通常以“POP {PC}”或类似的指令返回,这将从栈中恢复LR到PC,实现返回。 具体步骤: a) 函数入口(prologue): - 将LR和需要保存的寄存器压入栈(通常使用PUSH指令)。 - 调整栈指针(SP)为局部变量分配空间(如果需要)。 b) 函数体执行。 c) 函数出口(epilogue): - 释放局部变量占用的栈空间(如果需要)。 - 从栈中恢复保存的寄存器,包括将返回地址从栈弹出到PC(通常使用POP指令,或者直接恢复LR然后使用MOV PC, LR)。 需要注意的是,在ARM中,栈通常是从高地址向低地址增长的(满递减栈)。 下面我们用一个简单的例子来说明: 假设有两个函数:func1和func2,func1调用func2。 func1的代码片段: ... BL func2 ; 调用func2,LR设置为返回地址(下一条指令地址) ... ; 返回点 func2的代码片段(假设需要保存R4-R6和LR): PUSH {R4-R6, LR} ; 保存寄存器,包括LR SUB SP, SP, #8 ; 为局部变量分配8字节空间 ... ; func2的函数体 ADD SP, SP, #8 ; 释放局部变量空间 POP {R4-R6, PC} ; 恢复寄存器,并将LR的值弹出到PC,实现返回 注意:在POP指令中,我们将LR弹出到PC,这样函数就直接返回到调用处。 另外,在ARM Thumb模式下,POP指令可以直接弹出到PC,这是常见的返回方式。 引用[1]中提到了ARM架构的机器描述结构,其中包含了架构编号、名称、设备树兼容字符串等信息,虽然不直接涉及调用栈,但说明ARM架构有不同的变体,而调用栈机制在核心上是一致的。 引用[2]提到主要针对x86/ARM Linux二进制文件,说明我们的讨论同样适用于ARM Linux环境。 引用[3]和[4]与当前问题关联不大,因此不参考。 因此,ARM架构下函数调用栈的工作过程可以总结为: - 使用栈来保存返回地址和寄存器的值。 - 遵循标准的调用约定(AAPCS)。 - 通过BL指令调用函数并设置LR,通过POP {PC}或等效指令返回。 相关问题: 1. ARM架构中,当函数参数多于4个时,如何传递额外的参数? 2. 在ARM函数调用中,哪些寄存器是调用者保存的(caller-saved),哪些是被调用者保存的(callee-saved)? 3. ARM的栈帧中通常包含哪些内容? 4. 在异常处理(如中断)时,ARM的栈操作有何不同?</think>### ARM架构函数调用栈工作过程详解 在ARM架构中,函数调用栈通过寄存器协作和栈内存管理实现,核心过程如下: #### 1. **关键寄存器作用** - **SP(Stack Pointer, R13)**:指向当前栈顶位置 - **LR(Link Register, R14)**:保存函数返回地址 - **PC(Program Counter, R15)**:存储下一条指令地址 - **FP(Frame Pointer, R11)**:可选,指向当前栈帧基址(用于调试和回溯) #### 2. **函数调用过程(以函数A调用函数B为例)** **步骤1:调用准备(Caller)** - 参数传递:前4个参数通过$R0-R3$传递,多余参数压入栈 - 返回地址保存:执行`BL func_B`指令时,当前PC+4自动存入$LR$ ```assembly MOV R0, #5 ; 参数1 → R0 MOV R1, #10 ; 参数2 → R1 BL func_B ; 调用func_B (LR = PC+4) ``` **步骤2:被调用函数开场(Callee Prologue)** - 保存上下文:将$LR$和需保留的寄存器压栈 - 栈空间分配:调整$SP$为局部变量预留空间 ```assembly func_B: PUSH {R4-R6, LR} ; 保存寄存器 & 返回地址 SUB SP, SP, #8 ; 分配8字节局部变量空间 ``` **步骤3:函数执行** - 局部变量存储在栈中(通过$SP$偏移访问) - 函数体执行期间可修改$R0-R3$ **步骤4:函数返回(Callee Epilogue)** - 释放栈空间:恢复$SP$到开场前位置 - 恢复上下文:弹出寄存器及返回地址到$PC$ ```assembly ADD SP, SP, #8 ; 释放局部变量空间 POP {R4-R6, PC} ; 恢复寄存器并直接返回 (PC ← LR) ``` #### 3. **栈帧结构示例 ``` 高地址 +-----------------+ | 调用者保存的寄存器 | ← 旧SP +-----------------+ | 参数5~n | (若有) +-----------------+ | 返回地址(PC) | ← 新FP (可选) +-----------------+ | 保存的LR | +-----------------+ | 保存的R4-R6 | +-----------------+ | 局部变量空间 | ← SP 低地址 ``` #### 4. **关键特性** 1. **栈增长方向**:ARM采用**满递减栈**(FD, Full Descending),栈向低地址增长 2. **寄存器保存规则**: - 调用者保存:$R0-R3$(参数/临时寄存器) - 被调用者保存:$R4-R11$(需长期保留的寄存器) 3. **返回机制**:通过`POP {..., PC}`直接将返回地址载入$PC$,等效于`BX LR` #### 5. **异常处理差异** 在中断/异常场景下: - 处理器自动保存$PC$和$CPSR$到栈中 - 使用专用异常栈指针(如ARMv7的$SP_irq$) - 需手动保存其他寄存器[^1][^2] > 此过程遵循ARM AAPCS标准(ARM Architecture Procedure Call Standard),确保二进制兼容性[^1]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值