armv7指令集 C函数调用 堆栈指针SP的变化

本文深入探讨了ARM架构下不同编译器(arm-linux-gnueabihf-gcc与arm-linux-gnueabi-gcc)在函数调用过程中的实现差异,包括栈帧寄存器的选择、栈空间分配策略及参数传递方式。通过对比两种编译器生成的反汇编代码,详细分析了函数调用前后栈空间的管理,以及如何利用fp和lr寄存器确保正确调用和返回。

首先说明一下,arm中函数调用不同的编译器可能差别很大,即使都是arm-linux的交叉编译器,也有差别,有的编译器把r7寄存器作为栈帧寄存器(fp),有的把r11寄存器作为栈帧指针(fp),例如arm-linux-gnueabihf-gcc用的r7和arm-linux-gnueabi-gcc用的r11,另外在函数执行开头的处理也不一样

1. arm-linux-gnueabihf-gcc编译器先给函数中变量分配栈空间,然后将fp和sp指向栈顶

2. arm-linux-gnueabi-gcc编译器先让fp和sp指向栈顶(栈底),然后再给函数中的变量分配栈空间,之后sp指向新的栈顶,而fp指向的老的栈顶变成了函数的栈底。

个人感觉第二种比较好理解,但是总而言之,每次函数开始执行都需要保存fp指针到栈空间,这里的被保存fp指针是上一个函数的数据(一般是栈底数据),然后立即将本函数的栈底数据保存到fp指针, 函数调用结束,根据fp的数据恢复sp指针,从而释放结束函数的栈空间。

这么看来,函数调用需要入栈的数据只有前一个函数的fp指针,然后跳转的时候会将跳转指令bl的下一条指令地址保存到 lr寄存器,方便函数调用结束后返回。当然如果函数入参很多,寄存器不够用,这些参数同样需要入栈。

下面贴出两种编译器的C代码以及反汇编:

下面我们看C程序:

int m = 8;
int fun(int a,int b)
{
    int c = 0;
    c = a + b;
    return c;
}
int main()
{
    int i = 4;
    int j = 5;
    m = fun(i, j);
    return 0;
}

使用的编译器是arm-linux-gnueabihf-gcc,针对的芯片是I.Mx6ull,对应的反汇编代码:

Disassembly of section .text:

00010094 <fun>:
   10094:	b480      	push	{r7}
   10096:	b085      	sub
### A64指令集下的堆栈分析与函数堆栈大小计算 在A64指令集ARMv8架构)中,堆栈分析和计算每个函数的堆栈大小是确保程序正确性和性能优化的重要步骤。以下是关于如何进行堆栈分析以及计算堆栈大小的相关信息。 #### 1. 堆栈的基本概念 在A64指令集中,堆栈通常通过寄存器`SP`(Stack Pointer)来管理。堆栈的操作主要包括压栈(push)和出栈(pop),这些操作可以通过特定的指令完成。例如,`STR`指令用于将数据存储到堆栈中,而`LDR`指令则用于从堆栈中加载数据[^3]。 #### 2. 函数调用中的堆栈分配 在函数调用过程中,堆栈被用来保存局部变量、返回地址以及其他必要的信息。为了计算函数的堆栈大小,需要考虑以下几个方面: - **局部变量**:每个函数可能包含多个局部变量,这些变量占用的内存空间需要预先分配。 - **调用参数**:如果函数调用涉及大量的参数传递,这些参数可能会被压入堆栈。 - **返回地址**:当函数调用发生时,返回地址通常会被保存在堆栈中。 - **寄存器保存**:某些寄存器的内容可能需要保存到堆栈中以防止被覆盖。 #### 3. 计算堆栈大小的方法 要计算函数的堆栈大小,可以按照以下方式分析代码: - **静态分析**:通过阅读或解析汇编代码,统计所有局部变量、参数和寄存器保存所需的堆栈空间。例如,假设一个函数需要保存两个64位寄存器(每个占用8字节),并且有三个局部变量(每个占用4字节),那么该函数的堆栈大小为`2 * 8 + 3 * 4 = 28字节`[^4]。 - **动态分析**:使用调试工具或性能分析工具(如`gdb`或`perf`)跟踪程序运行时的堆栈使用情况。这种方法能够捕捉到实际运行中的最大堆栈需求。 #### 4. 示例代码 以下是一个简单的A64汇编代码示例,展示了一个函数如何分配堆栈空间: ```asm .text .global my_function my_function: // 保存链接寄存器(LR)到堆栈 STR X30, [SP, #-16]! // 调整堆栈指针并保存LR // 分配局部变量空间 SUB SP, SP, #32 // 为局部变量分配32字节 // 函数体逻辑... MOV X0, #1 // 示例操作 // 恢复堆栈指针 ADD SP, SP, #32 // 释放局部变量空间 // 恢复链接寄存器并返回 LDR X30, [SP], #16 // 恢复LR并调整堆栈指针 RET // 返回调用者 ``` 在这个例子中,函数`my_function`分配了`16字节(保存LR)+ 32字节(局部变量)= 48字节`的堆栈空间[^5]。 #### 5. 注意事项 - 在多线程环境中,每个线程都有独立的堆栈空间,因此需要确保每个线程的堆栈大小足够大以避免溢出。 - 使用递归函数时,堆栈深度可能会迅速增加,应特别注意避免堆栈溢出问题。
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值