栈回溯方案

注:栈回溯无法很好的定位到未调优化的函数,需要编译前使用 -fno-optimize-sibling-calls 选项禁止尾调优化。

基于unwind的栈回溯

  在 arm 架构下,不少32位系统用的是 unwind 形式的栈回溯,这种栈回溯要复杂很多。首先需要程序有一个特殊的段 .ARM.unwind_idx 或者.ARM.unwind_tab,在连接文件中增加 __start_unwind_idx,这就是 ARM.unwind_idx 段的起始地址。这个unwind段中存储着跟函数入栈相关的关键数据。当函数执行入栈指令后,在 unwind 段会保存跟入栈指令一一对应的编码数据,根据这些编码数据,就能计算出当前函数栈大小和 cpu 的哪些寄存器入栈了,已经在栈中什么位置。
  当栈回溯时,首先根据当前函数中的指令地址,就可以计算出函数 unwind 段的地址,然后从 unwind 段取出跟入栈有关的编码数据,根据这些编码数据就能计算出当前函数栈的大小以及入栈时 lr 寄存器数据在栈中的存储地址。这样就可以找到 lr 寄存器数据,就是当前函数返回地址,也就是上一级函数的指令地址。此时sp一般指向的函数栈顶,SP+函数栈大小 就是上一级函数的栈顶。这样就完成了一次栈回溯,并且知道了上一级函数的指令地址和栈顶地址,按照同样的方法就能对上一级函数栈回溯,类推就能实现整个栈回溯流程。
  编译时需要加入 -funwind-tables 选项,此选项在编译时会依赖 标准glibc 库,要求不能使用 -nostdlib 选项,否则会报某些函数缺失错误。

.ARM.exidx : {

    __exidx_start = .;

    *(.ARM.exidx* .gnu.linkonce.armexidx.*)

    __exidx_end = .;

}

优缺点分析

  • 缺点
    需要在代码的连接脚本中增加新的 unwind 专用段,对资源要求较高,且修改连接脚本容易引发未知问题。

  • 优点
    暂未详细分析。

基于FP寄存器的栈回溯

  APCS 规范在ARM架构上定义了程序函数调用和栈帧定义以及寄存器的使用的规范,中定义了 FP 和 IP 寄存器的作用,目前这个规范已经被 AAPCS 规范所取代,此种方式基本已经不在使用。
在这里插入图片描述

核心思想

  通过当前获取异常时的 FP 寄存器,找到调用者的栈帧开始地址,再通过栈帧下的相对偏移找到调用栈下的LR,从而确定子函数跳转地址 PC = LR - 跳转指令大小。

回溯过程

  1. 异常处理函数中获取线程异常时的堆栈指针 SP(获取MSP) 和 FP;

  2. 通过FP寄存器找到当前栈的栈帧开始位置,并通过偏移和LR的指令特征找到 fun_B 栈帧下存放的 LR

  3. 通过 (PC = *LR-跳转指令大小) 确定父函数 fun_A 调用异常函数 fun_B 的地址

  4. 通过获取 PC 地址下的指令,通过BL/BLX解码 找到 fun_B 函数的地址

  5. 通过相对偏移找到函数 fun_B 栈帧下的FP,从而找到函数 fun_A 的栈帧开始地址,执行LR搜索和指令解码

  6. 最后会找到 main 函数栈帧的开始地址,通过main栈帧偏移找到LR地址,发现 *LR 的地址是线程 main 函数退出收尾函数时停止栈回溯。

优缺点分析

  • 优点
  1. 实现方案简单,栈回溯效率高,能够直接确定调用者的栈帧开始地址,快速定位到调用者栈帧下的 LR;

  2. 栈的遍历中无需检查每个 LR 的特征,通用语Thumb 和 ARM 指令集;

  • 缺点
  1. 目前基本不在使用APCS规范,高于gcc 5.0 版本的编译器不在支持 FP寄存器的压栈;

  2. 需要修改链接脚本,在编译选项中加入-fomit-frame-pointer -mapcs-frame;

  3. 在每个函数下都增加了 FP 等寄存器的压栈指令,调试时与实际运行程序有差别;

基于SP遍历 LR 的栈回溯

在这里插入图片描述

核心思想

  获取异常发生时线程函数的 SP(MSP),然后逐个从栈上取出内容进行判断,在 Thumb 指令集下栈上保存的 LR 是父函数进入子函数位置的下一条指令位置 +1,这里的+1表明了栈上 LR 位置存放的一定是一个奇数,再判断这个奇数-1 是否在 .text 段范围内,筛选出奇数后判断 *LR - 4 和 *LR - 2 位置是否满足 BL/BLX 指令特征,对于 ARM 指令集下栈上保存的 LR 是父函数进入子函数位置的下一条指令位置,这个值是4字节对齐的,再判断这个奇数-1 是否在 .text 段范围内,筛选出 4 字节对齐的内容后判断 *LR - 4 和 *LR - 2 位置是否满足 BL/BLX 指令特征,然后计算出跳转指令大小 PC=(*LR-指令大小) 就是 子函数调用位置,再根据获取 PC 下的指令,对指令进行地址解码就可以找到函数开始地址了。

回溯过程

  1. 异常处理函数中获取线程异常时的堆栈指针 SP(获取MSP),中断/内核栈下的PC(指向异常发生时的执行指令);
  2. 从异常时 SP 向上遍历栈帧找到 Thumb 指令集下 LR1 位置下的内容为 0x60256b93 这是一个奇数,然后取出 0x60256b93 - 1 - 4 = 0x60256b8e ,判断这个地址是否在 .text 段范围内,再判断指令是否为 bl/blx中的一种,如果是则说明找到了父函数 fun_A 调用 fun_B 的位置,记录下来;
  3. 继续向上遍历栈,找到 Thumb 指令集下 LR2 位置下的内容为 0x602561e9 这是一个奇数,取出0x602561e9 - 1 -4 = 0x602561e4 地址下的内容,判断是否是 b/bl/blx中的一种,如果是则说明找到了父函数 main 调用fun_A 的位置,记录下来;
  4. 继续向上遍历栈,找到 Thumb 指令集下 LR3 位置,*LR3-1-4 等于线程退出收尾函数时停止栈回溯;

优缺点分析

  • 优点
  1. 栈回溯效率相对较高,只需遍历栈找特征LR值即可;
  2. 无需修改连接脚本,对原始SDK侵入性较小;
  • 缺点
  1. 严重依赖 LR 特征值,可能出现错误解析;
  2. 不同架构,以及Thumb 和 ARM 指令集中BL/BLX 指令格式不同,兼容较为繁琐;

基于SP 代码遍历的栈回溯

在这里插入图片描述

核心思想

  获取异常发生时线程函数的SP 和 PC ,通过 PC 位置在 .text 上寻找函数压栈操作指令 push/stmdb 和栈内存申请指令 sub/sub.w (SUB SP minus immediate) ,计算出栈帧大小,然后确定 LR 位置,确定调用者栈底位置 SP+framesize,然后在确定调用者调用子函数的位置 PC = LR - 跳转指令大小,之后根据 PC 位置继续从调用者函数的 .text 代码段遍历栈帧操作指令。

回溯过程

  1. 异常中断中获取线程异常时的堆栈指针 SP(获取MSP),中断/内核栈下的PC(指向异常发生时的执行指令);
  2. 向上遍历异常函数 fun_B 的 .text 段内容寻找 push 指令,解析 6026d17a 处压栈的寄存器个数4个寄存器包括 lr 寄存器,以此处 push 指令特征值为 0xb500,继续遍历 6026d178 处压栈寄存器个数为 4,则相对于 lr 寄存器的偏移 offsetsize = 4,此时栈帧大小为 8;
  3. 从 push 指令向下搜索栈扩展指令 sub/sub.w,6026d180 处在栈上申请了 386 *4 个空间;所以栈帧总大小为 framesize = 386+8;
  4. 确定 LR 位置 LR = SP + framesize - offsetsize;
  5. 确定调用者 fun_A 函数栈帧(调用者栈)的栈帧底部位置 SP = SP + framesize;
  6. 再通过 (*LR - 跳转指令大小) = PC 确定 fun_A 中调用 fun_B 的位置;
  7. 之后继续从 fun_A 下的 .text 段的 PC 位置向上遍历,如此循环,直到找到的 *LR 是线程退出收尾函数为止;

优缺点分析

  • 优点
  1. 对栈上内容的依赖性较小,完全通过 .text 代码节进行遍历;
  2. 栈的遍历中无需检查每个 LR 的特征,适用于 Thumb 和 RAM 指令集;
  • 缺点
  1. 效率较低,需要对从函数开始到子函数跳转位置进行遍历,如果函数很长则影响效率;
  2. 复杂性高,需要解析栈操作指令来获取压栈和栈扩展的大小,从而确定栈帧大小;

内存泄露定位

打印栈帧还有一个应用,就是检查谁引起内存泄露:

// s_array 为全局数组
static int alloc_en(void *addr, unsigned int size);
 
void* malloc_wrapper(unsigned int size)
{
	void *ptr = (void*)malloc(size);
	 alloc_en(ptr, size);
	 return ptr;
}
 
static int alloc_en(void *addr, unsigned int size)
{
 
     for(i = 0; i < MAX_CALL; ++i){
          if(NULL == s_array[i].addr)
              break;
     }
 
     if(i >= MAX_CALL){
          printf("no free slot");
          return -1;
     }
     
     s_array[i].addr = (U32)addr;
     s_array[i].size = size;
     s_array[i].caller = backtrace(3);// 三级调用者是谁?
}

🌀路西法 的个人博客拥有更多美文等你来读。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/971953.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【存储中间件API】MySQL、Redis、MongoDB、ES常见api操作及性能比较

常见中间件api操作及性能比较 ☝️ MySQL crud操作✌️ maven依赖✌️ 配置✌️ 定义实体类✌️ 常用api ☝️ Redis crud操作✌️ maven依赖✌️ 配置✌️ 常用api ☝️ MongoDB crud操作✌️ maven依赖✌️ 配置文件✌️ 定义实体类✌️ MongoDB常用api ☝️ ES crud操作 ⭐️…

解锁D3.js与PlantUML的交互奥秘:探索知识图谱数据可视化新领域

解锁D3.js与PlantUML的交互魔法&#xff1a;数据可视化新征程 在前端开发的广袤天地里&#xff0c;数据可视化一直是一颗璀璨的明珠&#xff0c;吸引着无数开发者探索其奥秘。而当D3.js这一强大的JavaScript库&#xff0c;遇上专注于创建UML图的PlantUML&#xff0c;一场奇妙的…

DeepSeek24小时写作机器人,持续创作高质量文案

内容创作已成为企业、自媒体和创作者的核心竞争力。面对海量的内容需求&#xff0c;人工创作效率低、成本高、质量参差不齐等问题日益凸显。如何在有限时间内产出高质量内容&#xff1f;DeepSeek写作机器人&#xff0c;一款24小时持续创作的智能工具&#xff0c;为企业和个人提…

使用html css js 来实现一个服装行业的企业站源码-静态网站模板

最近在练习 前端基础&#xff0c;html css 和js 为了加强 代码的 熟悉程序&#xff0c;就使用 前端 写了一个个服装行业的企业站。把使用的技术 和 页面效果分享给大家。 应用场景 该制衣服装工厂官网前端静态网站模板主要用于前端练习和编程练习&#xff0c;适合初学者进行 HT…

使用html css js 开发一个 教育机构前端静态网站模板

这个教育机构网站模板是专为前端开发初学者设计的练习项目&#xff0c;适合正在学习前端的学生或自学者使用。网站内容包括首页、课程体系、师资力量、关于我们和联系我们等基础页面&#xff0c;帮助学习者熟悉网页布局、样式设计和交互功能的实现。 静态页面 简单截图 应用…

(蓝桥杯——10. 小郑做志愿者)洛斯里克城志愿者问题详解

题目背景 小郑是一名大学生,她决定通过做志愿者来增加自己的综合分。她的任务是帮助游客解决交通困难的问题。洛斯里克城是一个六朝古都,拥有 N 个区域和古老的地铁系统。地铁线路覆盖了树形结构上的某些路径,游客会询问两个区域是否可以通过某条地铁线路直达,以及有多少条…

React 低代码项目:网络请求与问卷基础实现

&#x1f35e;吐司问卷&#xff1a;网络请求与问卷基础实现 Date: February 10, 2025 Log 技术要点&#xff1a; HTTP协议XMLHttpRequest、fetch、axiosmock.js、postmanWebpack devServer 代理、craco.js 扩展 webpackRestful API 开发要点&#xff1a; 搭建 mock 服务 …

大流量汽(柴)油机泵,抗洪抢险的可靠选择|深圳鼎跃

近年来&#xff0c;全球范围内极端天气频发&#xff0c;洪涝灾害成为危及人民生命财产安全的重要因素。在抗洪抢险行动中&#xff0c;如何迅速、高效地排除积水&#xff0c;保障救援通道和安全区域成为关键。汽柴油机泵凭借其动力强、移动灵活、环境适应性强等特点&#xff0c;…

游戏开发微信小程序--工具箱之父

小程序工具箱之父已更新 Page({data: {score: 0,lives: 3,gameOver: false,playerVisible: true,level: 1,petType: cat,speedBuff: 1,coins: 0,friends: [],achievements: [],currentPetFrame: 0, // 当前宠物动画帧scoreMultiplier: 1, // 得分倍率gameSpeed: 1, // …

一.数据治理理论架构

1、数据治理核心思想&#xff1a; 数据治理理论架构图描绘了一个由顶层设计、管控机制、核心领域和管理系统四个主要部分组成的数据治理框架。它旨在通过系统化的方法&#xff0c;解决数据治理机制缺失引发的业务和技术问题&#xff0c;并最终提升企业的数据管理水平。 数据治…

一键安装教程

有需要的可以私信 亮点&#xff1a; 不再需要安装完去配置环境变量&#xff0c;下载完程序&#xff0c;解压后&#xff0c;右键进行管理员安装&#xff0c;安装完毕自动配置环境变量&#xff0c;即可使用 Maven 安装 右键 以管理员身份运行点击 下一步安装完成后会同步配置环境…

crud项目分析(2)

JWT令牌验证是否登录成功 简单的验证账号密码是否正确(存在) 全局异常处理器 过滤器 因为login下只有这一个网页 唯一一种操作 package com.itheima.filter;import ch.qos.logback.core.util.StringUtil; import com.alibaba.fastjson.JSONObject; import com.itheima.pojo.R…

深入解析iOS视频录制(二):自定义UI的实现

深入解析 iOS 视频录制&#xff08;一&#xff09;&#xff1a;录制管理核心MWRecordingController 类的设计与实现 深入解析iOS视频录制&#xff08;二&#xff09;&#xff1a;自定义UI的实现​​​​​​​ 深入解析 iOS 视频录制&#xff08;三&#xff09;&#xff1a;完…

【Linux系统】生产者消费者模型:基于环形队列(信号量机制)

理论层面 1、环形队列的特性认识 环形队列采用数组模拟&#xff0c;用模运算来模拟环状特性 环形结构起始状态和结束状态都是⼀样的&#xff0c;不好判断为空或者为满&#xff0c;所以可以通过加计数器或者标记位来判断满或者空。另外也可以预留⼀个空的位置&#xff0c;作为…

【笔记】LLM|Ubuntu22服务器极简本地部署DeepSeek+API使用方式

2025/02/18说明&#xff1a;2月18日~2月20日是2024年度博客之星投票时间&#xff0c;走过路过可以帮忙点点投票吗&#xff1f;我想要前一百的实体证书&#xff0c;经过我严密的计算只要再拿到60票就稳了。一人可能会有多票&#xff0c;Thanks♪(&#xff65;ω&#xff65;)&am…

leetcode-414.第三大的数

leetcode-414.第三大的数 code review! 文章目录 leetcode-414.第三大的数一.题目描述二.代码提交 一.题目描述 二.代码提交 class Solution { public:int thirdMax(vector<int>& nums) {set<int> set_v(nums.begin(), nums.end());auto it set_v.rbegin()…

【设计模式】 代理模式(静态代理、动态代理{JDK动态代理、JDK动态代理与CGLIB动态代理的区别})

代理模式 代理模式是一种结构型设计模式&#xff0c;它提供了一种替代访问的方法&#xff0c;即通过代理对象来间接访问目标对象。代理模式可以在不改变原始类代码的情况下&#xff0c;增加额外的功能&#xff0c;如权限控制、日志记录等。 静态代理 静态代理是指创建的或特…

深度学习之图像回归(二)

前言 这篇文章主要是在图像回归&#xff08;一&#xff09;的基础上对该项目进行的优化。&#xff08;一&#xff09;主要是帮助迅速入门 理清一个深度学习项目的逻辑 这篇文章则主要注重在此基础上对于数据预处理和模型训练进行优化前者会通过涉及PCA主成分分析 特征选择 后…

利用分治策略优化快速排序

1. 基本思想 分治快速排序&#xff08;Quick Sort&#xff09;是一种基于分治法的排序算法&#xff0c;采用递归的方式将一个数组分割成小的子数组&#xff0c;并通过交换元素来使得每个子数组元素按照特定顺序排列&#xff0c;最终将整个数组排序。 快速排序的基本步骤&#…

照片模糊怎么变清晰?图生生AI修图-一键清晰放大

当打开手机相册时&#xff0c;那些泛着噪点的合影、细节模糊的风景照、像素化的证件图片&#xff0c;让珍贵时刻蒙上遗憾的面纱。而专业级图像修复工具的门槛&#xff0c;让多数人只能无奈接受这些"不完美的记忆"。AI技术的发展&#xff0c;让普通用户也能轻松拥有专…