看到一篇文章《冬之焱:谈谈Linux内核的栈回溯与妙用》,来自微信公众号"Linux阅码场"。文章主要写了Linux Backtrace的方法,里面提到ARM栈时,有这么一个图:

文章认为除了unwind模式,arm函数调用后都会压入PC,LR,SP,FP(即R15,R14,R13,R11)几个寄存器;但是,在平常ARM汇编代码中,很少能看到函数调用会压栈这么多寄存器。

实际上,压栈哪些寄存器,很大程度上是由编译选项决定的,下面是相关验证。代码很简单,就是在main 函数中调用了zperf_main进行测试:

1. gcc默认编译,无任何选项:

 arm-linux-gnueabi-gcc -o test test.c

压栈了寄存器R4,R11和R14,R4为zperf_main函数中会改变的通用寄存器,R11作为FP指针使用(程序中不会改变),R14作为LR。

2.  加编译选项 -O0

与不加选项完全一致,说明不加选项默认就是O0优化

3.  加编译选项 -O1 或者编译选项-O(两者一致)

arm-linux-gnueabi-gcc -O1 -o test1 test.c

压栈了寄存器R3-R11和R14,此时R14作为LR保存,R3-R11都是作为通用寄存器保存,R11并不作为FP,可以看到后面程序会将它作为通用寄存器使用。

4.  加编译选项 -O2

arm-linux-gnueabi-gcc -O2 -o test2 test.c

压栈了寄存器R3-R10和R14,此时R14作为LR保存,R3-R10都是作为通用寄存器保存,相比O1优化了R11的保存恢复。

5.  加编译选项 -O3

arm-linux-gnueabi-gcc -O3 -o test3 test.c

由于程序比较简单,编译后与O2完全一致。

6.  加编译选项 -fomit-frame-pointer

该选项的作用,在gcc手册中是这么描述的:

Don't keep the frame pointer in a register for functions that don't need one. This avoids the instructions to save, set up and restore frame pointers; it also makes an extra register available in many functions. It also makes debugging impossible on some machines。

简单来说就是通过不保存FP来优化程序性能。

arm-linux-gnueabi-gcc -fomit-frame-pointer -o testf test.c

与不开优化选项的程序相比,可以看到这段代码已不再保存FP。

事实上gcc的所有级别的优化(-O1, -O2, -O3等)都会打开-fomit-frame-pointer,该选项的功能是函数调用时不保存frame指针,在ARM上就是fp,故我们无法按照APCS中的约定来回溯调用栈。但是GDB中仍然可以使用bt命令看到调用栈,为什么?得知GDB v6之后都是支持DWARF2的,也就意味着它可以不依赖fp来回溯调用栈(详见http://gcc.gnu.org/ml/gcc/2003-10/msg00322.html)。

7.  加编译选项 -mapcs

arm-linux-gnueabi-gcc -mapcs -o testm test.c

这个选项使程序严格遵守ARM Procedure Call Standard(ARM过程调用标准规范)中关于arm寄存器的使用、过程调用时出栈和入栈的约定。

可以看到,此时程序才严格按照图1的规律,每个函数调用都会压栈PC,LR,SP,FP作为寄存器栈帧进行保存。

转载于:https://www.cnblogs.com/DF11G/p/9706063.html

ARM栈帧与编译选项相关推荐

  1. arm gcc栈帧结构(1)

    2019独角兽企业重金招聘Python工程师标准>>> 摘要:先看个例子: void test2(int a,int b,int c) { int k=a,j=b,m=c; } GC ...

  2. 计算机科学基础知识(六)理解栈帧

    一.前言 本文以一个简单的例子来描述ARM linux下的stack frame. 本文也是对tigger网友问题的回复. 二.源代码 #include <stdio.h> static ...

  3. X86-64寄存器和栈帧--牛掰降解汇编函数寄存器相关操作

    X86-64寄存器和栈帧 概要 说到x86-64,总不免要说说AMD的牛逼,x86-64是x86系列中集大成者,继承了向后兼容的优良传统,最早由AMD公司提出,代号AMD64:正是由于能向后兼容,AM ...

  4. x86-64寄存器与栈帧(转载)

    概要 说到x86-64,总不免要说说AMD的牛逼,x86-64是x86系列中集大成者,继承了向后兼容的优良传统,最早由AMD公司提出,代号AMD64:正是由于能向后兼容,AMD公司打了一场漂亮翻身战. ...

  5. C++ 反汇编/栈帧

    文章目录 查看 C++ ASM 在线 C++ 反汇编 g++ -S VS 调试时断点查看反汇编信息 VS 在项目属性的文件输出源码+汇编 Debug 下的:[ProjectName].asm Rele ...

  6. java 参数类型不确定_详细解析Java虚拟机的栈帧结构

    什么是栈帧? 正如大家所了解的,Java虚拟机的内存区域被划分为程序计数器.虚拟机栈.本地方法栈.堆和方法区.(什么?你还不知道,赶紧去看看<Java虚拟机内存结构及编码实战>)这次要介绍 ...

  7. java虚拟机栈帧_Java虚拟机,运行时栈帧结构

    业余生活要有意义,不要越轨.--华盛顿 引导语 "虚拟机"是一个相对于"物理机"的概念,这两种机器都有代码执行能力,其区别是物理机的执行引擎是直接建立在处理器. ...

  8. 函数调用过程,栈帧的一点理解

    栈帧图例一张 寄存器理解 程序寄存器组是唯一能被所有函数共享的资源.虽然某一时刻只有一个函数在执行,但需保证当某个函数调用其他函数时,被调函数不会修改或覆盖主调函数稍后会使用到的寄存器值.因此,IA3 ...

  9. 函数调用过程详解:函数栈帧的创建与销毁

    前言:我们在学习C语言的过程中,可以会产生很多疑问,比如: 局部变量是怎么创建的 为什么局部变量的值不做初始化就是随机值 函数是怎么传参的?传参的顺序是怎么样的? 形参和实参是什么关系? 函数调用是怎 ...

最新文章

  1. 二叉树的最小高度,最大高度(深度)和宽度
  2. RabbitMQ之TTL(Time-To-Live 过期时间)
  3. [概率论]如何通俗地理解“最大似然估计法”?
  4. c语言与java负数补码,详解原码、反码与补码存储与大小
  5. 项目升级-加密的参数传递到后台然后解密(相当于重新封装下request)
  6. 使用 IntraWeb (2) - Hello IntraWeb
  7. URLClassLoader使用方法及事例程序
  8. 【水果识别】基于matalb GUI灰度、二值化、滤波水果分级【含Matlab源码 1848期】
  9. PHP表单省市县三级联动,用php做省份的三级联动 附带数据库
  10. 一位平凡毕业生的大学四年
  11. 吴恩达深度学习第三周
  12. UE4中实现PBKDF2加密验证
  13. doe五步法_DOE方法介绍
  14. Arduino相关语法和函数
  15. 程序猿头头(防抖节流)
  16. NOIWC2018滚粗记
  17. 深度强化学习中的对抗攻击和防御
  18. MYSQL 判断一个时间段是否在另一个时间段内。
  19. SimMatch 论文分享
  20. Caché程序员必须知道符号与缩写 第二章 ObjectScript中使用的缩写

热门文章

  1. Jmeter+jenkins+ant自动化测试环境搭建
  2. 真机上装不上测试应用,Installation error: INSTALL_FAILED_INSUFFICIENT_STORAGE
  3. 前端面试汇总(Bootstrap框架)
  4. 闩锁电流_IGBT——闩锁(Lanchup)效应
  5. linux rpm 查找,Linux下 rpm 命令查询方法
  6. windows采集音频
  7. Linux下coredump调试1:使用
  8. 我参与的一个项目的继续总结:经验篇
  9. shell学习笔记二则:统计空间
  10. charles 代理手机连不上网_「技巧」不想接电话?这样可以让手机变成空号,还不影响上网...