arm函数调用中的堆栈变化
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
arm函数调用中的堆栈变化
在计算机的运行过程中,函数调用是一种常见的操作。
当程序调用一个函数时,需要先将当前的运行状态(例如当前指令的地址、堆栈指针等)保存在堆栈中,然后跳转到函数中执行。
函数执行完毕后,再从堆栈中恢复之前的运行状态,继续执行原来的程序。
这个过程中,堆栈扮演了一个非常重要的角色。
本文将介绍在ARM架构中,函数调用时堆栈的变化。
1. 堆栈的基本概念
在程序中,有一片内存区域被用来存放函数的局部变量和一些临时变量,称为堆栈。
堆栈是一个先进后出的数据结构,即最后存入的数据最先弹出。
当程序调用一个函数时,会在堆栈中分配一段空间来存放函数的参数、局部变量、返回地址等信息。
当函数返回时,这些信息会从堆栈中弹出,恢复程序之前的状态。
在ARM架构中,堆栈的地址是4字节对齐的,即堆栈指针(SP)的值必须是4的倍数。
这是因为ARM指令集中的大多数指令都是以4字节为单位的,如果SP不是4字节对齐的,那么执行指令时会出错。
2. 函数调用时堆栈的变化
当程序调用一个函数时,堆栈的变化可以分为以下几个步骤:
(1)保存寄存器
在ARM架构中,函数调用过程中一些重要的状态信息通常保存在寄存器中。
为了不影响原程序的运行,需要在堆栈中保存这些寄存器的值。
这些寄存器包括:R0~R3(函数参数)、R14(LR,返回地址)、R13(SP,堆栈指针)、R11(FP,帧指针)等。
在进入函数之前,需要将这些寄存器的值压入堆栈中。
具体操作为:
``` PUSH {R0-R3, R11, LR} ```
以上指令将R0~R3、R11、LR的值压入堆栈中。
其中,LR保存的是返回地址,R11保存的是帧指针(Frame Pointer,FP)。
FP是一个指针,指向当前函数的栈帧,用于访问局部变量。
堆栈指针SP也需要被保存,但不是在这里保存,而是在进入函数之前保存。
(2)分配空间
在堆栈中为当前函数分配空间,用于存放参数、局部变量和其它临时变量。
分配的空间的大小由当前函数所需的局部变量和参数决定。
在分配空间之前,要先保存堆栈指针SP的值。
具体操作为:
``` SUB SP, SP, #8 ```
以上指令将SP减去8,表示在堆栈中为当前函数分配8字节的空间。
这个空间可以用来存放函数的参数和临时变量。
注意,这里分配的空间大小必须是4字节的倍数,以
保证堆栈是4字节对齐的。
(3)传递参数
将函数的参数传递给被调用的函数。
ARM中的前4个
参数可以直接存放在R0~R3中,超过4个参数的部分需要
压入堆栈中。
如果有浮点类型的参数,需要使用寄存器
S0~S15和D0~D7来传递。
具体操作为:
``` MOV R0, #1 MOV R1, #2 MOV R2, #3 MOV R3, #4 ```
以上指令将4个整型参数传递给被调用的函数。
如果有超过4个参数的部分,需要使用STMFD(Store Multiple with Decrement)指令将多余的参数压入堆栈中。
例如:``` STMFD SP!, {R4-R7} ```
以上指令将R4~R7的值压入堆栈中,并将堆栈指针SP 的值减去16(4个寄存器,每个寄存器4字节),以指向
下一个空闲的位置。
(4)调用函数
跳转到被调用函数的入口地址,并将返回地址(函数调用后要返回的地址)保存在LR寄存器中。
具体操作为:``` BL subfunction ```
以上指令调用名为subfunction的子函数,并将跳转前的下一条指令地址保存在LR寄存器中。
BL指令实际上是一个伪指令,它会将当前指令的地址加上8(因为ARM指令的长度是4字节),然后将计算出的地址保存在LR寄存器中。
返回地址的值等于LR寄存器的值,因此LR寄存器中的值是调用函数前的指令地址。
(5)执行被调用函数
被调用函数开始执行,并分配自己的局部变量和临时变量。
这些变量存放在被调用函数的栈帧中,由帧指针FP 来访问。
堆栈指针SP也会变化,指向当前函数的栈顶。
函数执行过程中,需要访问局部变量和参数,可以使用FP和SP来计算变量的地址。
例如:
``` MOV R4, R0 ADD R4, R4, R1 STR R4, [FP, #-4] ```
以上指令将函数的前两个参数相加,并将结果存放在局部变量中。
注意,这里使用FP来访问局部变量,使用SP 来访问栈空间中存放的参数。
(6)返回函数
被调用函数执行结束后,需要将结果返回给调用函数。
在ARM中,函数的返回值通常存放在R0中。
返回函数的指令为:
``` MOV R0, #0 POP {R0-R3, R11, PC} ```
以上指令将R0中的值作为函数的返回值,并从堆栈中弹出寄存器的值。
注意,返回地址保存在LR寄存器中,可以使用POP指令一并弹出,并跳转到返回地址。
POP指令的参数格式和PUSH指令相反,用于弹出堆栈中保存的寄存器值。
3. 总结
在ARM架构中,函数调用时堆栈的变化涉及到寄存器的保存、空间的分配、参数的传递、函数的调用和返回,以及局部变量和临时变量的使用。
了解堆栈的变化对于理解ARM汇编语言和优化函数调用非常有帮助。
可以通过调试器或者汇编代码来观察堆栈的变化,加深对堆栈的理解。