加入星计划,您可以享受以下权益:

  • 创作内容快速变现
  • 行业影响力扩散
  • 作品版权保护
  • 300W+ 专业用户
  • 1.5W+ 优质创作者
  • 5000+ 长期合作伙伴
立即加入
  • 正文
    • 总结
  • 推荐器件
  • 相关推荐
  • 电子产业图谱
申请入驻 产业图谱

C语言在ARM中函数调用时,栈是如何变化的?

2021/01/05
182
阅读需 9 分钟
加入交流群
扫码加入
获取工程师必备礼包
参与热点资讯讨论

2021 元旦好,祝大家新的一年顺顺顺。

为什么会写篇栈变化的文章?做系统分析的话你肯定遇到过一些 crash, oops 等棘手问题,一般大家都会用 gdb, objdump 或者 addr2line 等工具分析 pc 位置来定位出错的地方。但是这些分析工具背后的本质原理就不见得理解深刻了,而且有的时候面对一系列 backtrace 或者 stack 日志处于懵逼的状态。

今天和大家一起看下面对 crash 日志的时候,如何利用 stack 来分析其变化的来龙去脉。

Arm 指令集介绍

崇尚简单粗暴的介绍方式,我们直接来看各个寄存器的大体用法,详细用法可百度,不,谷歌。

1.    r0-r3 用作传入函数参数,传出函数返回值。在子程序调用之间,可以将 r0-r3 用于任何用途。被调用函数在返回之前不必恢复 r0-r3。--- 如果调用函数需要再次使用 r0-r3 的内容,则它必须保留这些内容。2.    r4-r11 被用来存放函数的局部变量。如果被调用函数使用了这些寄存器,它在返回之前必须恢复这些寄存器的值。r11 是栈帧指针 fp。3.    r12 是内部调用暂时寄存器 ip。它在过程链接胶合代码(例如,交互操作胶合代码)中用于此角色。在过程调用之间,可以将它用于任何用途。被调用函数在返回之前不必恢复 r12。4.    寄存器 r13 是栈指针 sp。它不能用于任何其它用途。sp 中存放的值在退出被调用函数时必须与进入时的值相同。5.    寄存器 r14 是链接寄存器 lr。如果您保存了返回地址,则可以在调用之间将 r14 用于其它用途,程序返回时要恢复 6.    寄存器 r15 是程序计数器 pc。它不能用于任何其它用途。

演示代码

假如现在你已经掌握了 arm 指令的用法,即便没有掌握也没关系,“书到用时回头翻”。这里以一段简单的 c 语言为例:

#include 
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-gnueabi-gcc main.c -o main $ arm-linux-gnueabi-objdump -D -D main

00010400 :   10400:       e52db004        push    {fp}            ; (str fp, [sp, #-4]!)   10404:       e28db000        add     fp, sp, #0   10408:       e24dd014        sub     sp, sp, #20   1040c:       e50b0010        str     r0, [fp, #-16]   10410:       e50b1014        str     r1, [fp, #-20]  ; 0xffffffec   10414:       e3a03000        mov     r3, #0   10418:       e50b3008        str     r3, [fp, #-8]   1041c:       e51b2010        ldr     r2, [fp, #-16]   10420:       e51b3014        ldr     r3, [fp, #-20]  ; 0xffffffec   10424:       e0823003        add     r3, r2, r3   10428:       e50b3008        str     r3, [fp, #-8]   1042c:       e51b3008        ldr     r3, [fp, #-8]   10430:       e1a00003        mov     r0, r3   10434:       e24bd000        sub     sp, fp, #0   10438:       e49db004        pop     {fp}            ; (ldr fp, [sp], #4)   1043c:       e12fff1e        bx      lr
00010440 

: 10440: e92d4800 push {fp, lr} 10444: e28db004 add fp, sp, #4 10448: e24dd008 sub sp, sp, #8 1044c: e3a03004 mov r3, #4 10450: e50b300c str r3, [fp, #-12] 10454: e3a03005 mov r3, #5 10458: e50b3008 str r3, [fp, #-8] 1045c: e51b1008 ldr r1, [fp, #-8] 10460: e51b000c ldr r0, [fp, #-12] 10464: ebffffe5 bl 10400 10468: e1a02000 mov r2, r0 1046c: e59f3010 ldr r3, [pc, #16] ; 10484 10470: e5832000 str r2, [r3] 10474: e3a03000 mov r3, #0 10478: e1a00003 mov r0, r3 1047c: e24bd004 sub sp, fp, #4 10480: e8bd8800 pop {fp, pc} 10484: 00021024 andeq r1, r2, r4, lsr #32

图解栈的变化过程

如何能让读者接受吸收的更快,我一直觉得按照学习效率来讲的话顺序应该是视频,图文,文字。反正我是比较喜欢视频类的教学。这里给大家画下栈变化的过程是什么样子的。这里的图是结合上面的代码来画的,希望有助于读者的理解。

1. 程序在内存分布区域

2. 全局变量 m 赋值

3. 保存进入 main 之前的栈底, fp-sp 之间是当前函数栈

4. 函数 main 的栈已经准备好了

5.i 入栈

6.j 入栈

7. 准备函数 fun 的调用, 形参反向入栈 先形参 b 入栈

8. 形参 a 入栈

9. 留空一个地址作为 fun 返回值, 待后面返回时填入

10.fun 返回地址入栈, 通常是 main 函数当前 pc 指针的下一个

11.main 函数的栈底地址入栈

12.pc 指针跳转 fun 代码

13.c 入栈

14. 可以看到函数 fun 的数据 形参 a,b 在上一层函数的栈中 . 一部分在自己的栈上 . 此步取值到加法器中进行加法运算,再赋值给 c

15.c 赋给返回值,填入上面的留空位置

16. 栈底恢复上一层

17.lr 赋值给 pc, 实现了跳转

18. 返回值赋值给全局变量 m

19. 前面函数调用的形参已经无用,回滚 sp

20. 函数返回,清理 main 的栈空间

总结

这么多图有没有看花?相信到这里你已经了解了栈背后的来龙去脉,下一篇我们一起根据实际的 stack 错误案例剖析错误的可能性。

推荐器件

更多器件
器件型号 数量 器件厂商 器件描述 数据手册 ECAD模型 风险等级 参考价格 更多信息
CRCW0603100KFKEAHP 1 Vishay Intertechnologies Fixed Resistor, Metal Glaze/thick Film, 0.25W, 100000ohm, 75V, 1% +/-Tol, 100ppm/Cel, Surface Mount, 0603, CHIP, HALOGEN FREE AND ROHS COMPLIANT

ECAD模型

下载ECAD模型
$0.21 查看
QXPBCBPXXX68 1 Panduit Corp Cable Assembly,
暂无数据 查看
MURS160T3G 1 onsemi Power Rectifier, Ultra-Fast Recovery, 1 A, 600 V, SMB, 2500-REEL

ECAD模型

下载ECAD模型
$0.71 查看
Arm

Arm

ARM公司是一家知识产权(IP)供应商,主要为国际上其他的电子公司提供高性能RISC处理器、外设和系统芯片技术授权。目前,ARM公司的处理器内核已经成为便携通讯、手持计算设备、多媒体数字消费品等方案的RISC标准。公司1990年11月由Acorn、Apple和VLSI合并而成。

ARM公司是一家知识产权(IP)供应商,主要为国际上其他的电子公司提供高性能RISC处理器、外设和系统芯片技术授权。目前,ARM公司的处理器内核已经成为便携通讯、手持计算设备、多媒体数字消费品等方案的RISC标准。公司1990年11月由Acorn、Apple和VLSI合并而成。收起

查看更多

相关推荐

电子产业图谱