Toggle navigation
首页
问答
文章
积分商城
专家
专区
更多专区...
文档中心
返回主站
搜索
提问
会员
中心
登录
注册
Debug
反汇编
【经验分享】基于Linux命令行编程环境的栈追溯和反汇编技术实践分享
发布于 2022-05-31 21:25:50 浏览:1089
订阅该版
[tocm] **【经验分享】基于Linux命令行编程环境的栈追溯和反汇编技术实践分享** ![image.png](https://oss-club.rt-thread.org/uploads/20220531/38b4caa23f10653a643140e41cd4d341.png.webp) # 1 写在前面 在嵌入式开发中,往往受限于系统(芯片)本身的资源环境,没法像基于PC主机开发那样方便地进行程序debug,尤其当程序出现一些异常的时候,找到有效的方法进行debug,对排查问题非常有帮助。 在现实编程调试环境中,甚至于根本不可能拿到仿真器或者IDE环境来辅助你做异常分析,这个时候你的排查手段更多的是依赖一些命令行工具,结合编译输出的各种文件,配合代码逻辑做异常分析。其中,比较常用的方法有栈帧追溯和反汇编技术。 本文将简要介绍一下这两种方法的基础知识,需要对大家有所帮助;通过本文的阅读,你讲会了解到以下知识点: - **代码构建时可能输出的各种文件介绍** - **N种常见常用的命令行工具** - **栈帧追溯的基本原理和操作方法** - **反汇编的基本操作方法** - **汇编代码的基本阅读技巧** > PS:本次分享源自团队neibu的现场分享,所以部分深入的内容不便公开,部分文字也不可能百分百还原现场的讲解,感兴趣的可以私下交流。 # 2 代码构建系统简介 ## 2.1 常见的构建系统 有传统的Make、CMake,也有新主流的scons、xmake的,我个人观点,构建功能无所谓绝对的优劣,主要看各自团队的掌握情况以及需求程序,大可不必迫切追新,基础原理都是一样,万变不离其中,学精通一个就差不多了,其他的只是语法、命令、函数不一样而已。 ![image-20220531211720422](https://s2.loli.net/2022/05/31/IZneAvVqLS9xPgY.png) ## 2.2 编译构建生成的文件简介 以 **GCC** 编译器为例, 下面是业内常见叫法的文件: - **.d文件** :.d文件的内容是参与编译的C源码文件 - **.i文件** : .i 文件的内容是C源码文件被执行 **预处理** 之后的代码文本(**可读的文本文件**) - **.s文件** : .s 文件的内容是预处理之后的文件(.i文件)经过 **编译** 之后,生成的 **汇编代码** 文件 (**可读的文本文件**) - **.o文件** : .o 文件的内容是编译器对汇编文件进行汇编处理后,生成的 **目标文件**,(**不可读的二进制文件**);在 GCC 编译器中,.o文件实际也是一个 **elf** 文件; - **.map文件**:map文件就是通过编译器编译之后,生成的程序、数据及IO空间信息的一种映射文件,里面包含函数大小,入口地址等一些重要信息; - **.elf文件** :.elf文件(Executable and Linkable Format),是Linux平台是的可执行文件,里面包含代码段和数据段,以及可能包含的调试信息等等; - **.hex文件** :Intel HEX文件是由一行行符合Intel HEX[文件格式](https://baike.baidu.com/item/文件格式)的文本所构成的ASCII文本文件; - **.bin文件** : 纯二进制文件,一般用做烧录flash的固件文件; - **.a文件** :静态库文件,通常是原厂把不希望公开的代码打包成静态库的形式开放给第三方使用; - **.mk文件**:即为Makefile文件,里面遵循Makefile的标准语法。 下面几个文件后缀,是我们自己约定的文件: **.cmd文件** :.cmd是命令文件,里面保存的是一些流程需要用到文本命令;(在构建时直接把相关命令打包好,放在这个文件里) **.dmp文件**:.dmp是反汇编的输出文件,用于保存反汇编得到的汇编的代码。 ## 2.3 几个常用的命令行工具 以下几个**Linux** 命令行工具,常常用于分析上述文件,用好它们一定能让你的工作事半功倍。 - **vim** : Linux下文本编辑神器 - **grep** :关键字检索工具,搜索log,搜索代码的神器 - **find** :查找指定类似文件的神器 - **file** :分析文件类型的神器 - **size** :分析程序大小的神器 - **readelf** :分析elf文件的神器(object文件、so文件、elf可执行文件,本质都是elf文件) - **nm** : 分析静态库(.a)文件的神器 - **objdump** :反汇编神器 (**下文详讲**) - **addr2line** :栈追溯神器 (**下文详讲**) # 3 基于命令行工具的栈追溯 ## 3.1 栈追溯技术简介 栈追溯技术,一般来说,就是利用 **处理器异常** 时的现场信息(主要是寄存器信息),把函数的调用栈关系捋出来,从而还原出代码调用的异常路径,辅助排查异常问题的一种技术。 ## 3.2 栈追溯技术的基础知识 ### 3.2.1 ARM寄存器简介 ARM有37个寄存器,31个通用寄存器,6个状态寄存器: ![这里写图片描述](https://s2.loli.net/2022/05/23/5YcFWq1baROkzKl.png) 主要介绍如下: 不分组通用寄存器R0-R7:这意味着在所有处理器模式下,访问的都是同一个物理寄存器。不分组寄存器没有被系统用于特别的用途,任何可采用通用寄存器的应用场合都可以使用未分组寄存器; - R0-R3:函数调用的入参和出参; - R4-R7:函数执行的中间变量; - 规则由 ATPCS(即ARM-THUMB procedure call standard(ARM-Thumb过程调用标准))描述。 分组寄存器R8-R12:处理器保留做特殊通途; 寄存器R13通常做堆栈指针SP; 寄存器R14用作子程序链接寄存器(Link Register-LR),也称为LR,**指向函数的返回地址**; 寄存器R15被用作 **程序计数器**,也称为PC。 ### 3.2.2 RISC-V寄存器简介 以32位RISC-V处理器为例,它的寄存器描述如下表所示: ![在这里插入图片描述](https://s2.loli.net/2022/05/23/Vxh9BDUSM6KicGo.png) ### 3.2.3 函数调用的栈描述示意图及栈追溯的基本原理 函数调用的示意图如下所示:
在排查栈追溯相关的问题中,我们主要关注的寄存器是:PC寄存器和LR寄存器,核心思路就是把LR地址串起来,本质上就还原了函数调用的层次关系。 ## 3.3 基于命令行addr2line的栈追溯实践 以 **某RISC-V芯片平台** 为例,实践一下: ```shell recan@ubuntu:~/build$ ./build.sh backtrace 23037250 2303660e 23038c2a 2300e45a 2300e070 2300e344 elf file : /home/recan/ccc.elf ================ Backtrace ================= | backtrace[05] 2300e344 protocol_handle_task at protocol.c:416 (discriminator 1) | backtrace[04] 2300e070 protocol_parse at protocol.c:102 | backtrace[03] 2300e45a read_round_buf at roundbuf.c:131 | backtrace[02] 23038c2a mutex_lock at osal.c:91 | backtrace[01] 2303660e xQueueSemaphoreTake at queue.c:1517 V backtrace[00] 23037250 xTaskGetSchedulerState at tasks.c:4039 ============================================ ``` 这个build.sh是我们的构建编译的前端shell脚本,用于接收参数输入,内容不方便公开(你懂的),但是如何调起addr2line可以简单说一下,里面的脚本核心就是调用如下: ```shell addr2line_cmd="$backtrace_cmd 0x$addr -e $backtrace_elf_file -f -C -s -p" ``` 其中addr就填脚本传入的那些栈帧地址,比如23037250,backtrace_elf_file就填你编译固件输出的elf文件。 还有就是这个 **addr2line**,一般交叉编译工具链里面都有,跟xxx-gcc是在一起的,比如我这里是:`riscv64_unkown_elf_gcc10.2.0/Linux64/bin/riscv64-unknown-elf-addr2line` 得到的输出应该是类似于这样:`23037250 xTaskGetSchedulerState at tasks.c:4039` 然后把多个地址的输出组起来,把调用层次关系捋一下,就是是从上往下调用了。 比如有一段异常代码如下: ![image-20220531205019833](https://s2.loli.net/2022/05/31/M4WPnZKiuYScp7Q.png) 运行之后,异常现场出现了: ```shell Exception Entry--->>> mcause 38000005, mepc 23002880, mtval 00000008, tasksp 420242d0 Current task sp data: RA:2300c8b2, mstatus:80007880 A0:00000000 A1:4201d7c0 A2:4201d810 A3:230b1580 A4:4201d811 A5:65707974 A6:65707974 A7:4201d811 T0:00000020 T1:4200f000 T2:00002d25 T3:4200f000 T4:4200f000 T5:00000000 T6:5baba208 S0:42024358 S1:0000000e S2:230b06cc S3:230b1000 S4:42010254 S5:00004007 S6:00002710 S7:230b1000 S8:230b1000 S9:230b1000 S10:4201cd00 S11:230b1000 Exception code: 5 msg: Load access fault !!!maybe crash task name:x-task ========== Task name:x-task Backtrace: 23037e54 23037f0e 42023f88 ========== ``` 我们先试下x-task任务的 栈追溯: ![image-20220531205147912](https://s2.loli.net/2022/05/31/jfFGmegQHlOZ1rJ.png) 我们回过头来,单传入 **PC 指针** 进行栈追溯看看:(**mepc 23002880**) ![image-20220531205243648](https://s2.loli.net/2022/05/31/DdJv3uXVCZBQO76.png) 看样子,有点东西,我们找到这个C文件的对应行数: ![image-20220523190104810](https://s2.loli.net/2022/05/23/tBP7crbOleyq8d4.png) 果然跟我们上面特意写的异常代码是吻合的。 所以啊,也不能太一味地信任栈帧,有时直接对PC指针进行addr2line也很好使。 # 4 基于命令行objdump的反汇编 ## 4.1 反汇编技术简介 所谓的**反汇编技术** 就是执行与编译器编译C源码代码相关的流程,属于逆向分析的一种。 简单来说,如下: **程序汇编**:将C源代码翻译成汇编代码,进而翻译成适用于处理器运行的二进制指令; **程序反汇编**:将目标文件换成会汇编代码,再由汇编代码尽可能地还原出具备可读性的高级语言代码(比如C代码)。 - 基于elf文件的反汇编:如果elf文件中带有调试信息,基本可以做到 汇编代码与C代码一一对应; - 基于bin文件的反汇编:一般来说,不太可能之间翻译成C代码,但可以直接翻译成汇编代码;汇编代码到C代码,需要借助一些工具,比如**IDA工具**。 ## 4.2 反汇编技术的基础知识 要想熟练掌握反汇编技术,除了基本的反汇编操作方法外,剩下的就是了解对应处理器平台的汇编指令集。 由于不同的处理器的汇编指令集都不一样,甚至于一种处理器的不同版本对应的指令集也不尽相同(比如ARM处理器),所以只能是学以致用,一边学习,一边应用。 比如开源的 **RISC-V指令集** : ![image-20220523160718522](https://s2.loli.net/2022/05/23/lYJdz9xf3VWEkrK.png) 一大堆指令规范文档,慢慢啃吧。 ## 4.3 基于objdump命令行的反汇编实践 以 **某ARM架构的处理器** 为例: ```v recan@ubuntu:~/build$ ./build.sh objdump elf file : ./binary/bbb.elf objdump ... please wait some time ... objdump done, please check dump file with the following cmd: [ vi ./binary/bbb.dmp ] ``` 查找一个函数:自己实现的函数 softap_decrypt_password 分析C代码和汇编代码。 ![image-20220531210940752](https://s2.loli.net/2022/05/31/WyFmlNOQLwUIBeE.png) 查找一个 原厂提供的接口函数(**打包在静态库里面的函数**)rwip_aes_encrypt 分析其汇编代码: ![image-20220531211036181](https://s2.loli.net/2022/05/31/TnvG1iwDPmCWAta.png) **如果我的程序不是基于我的构建流程编译出来的,能进行反汇编吗?**答案是肯定可以的,只要芯片平台和编译工具用的是同一个。 下面我把objdump的命令参数公开一下: ```shell arm-none-eabi-objdump -l -d -x -s -S xxx.elf > xxx.dmp ``` 其中xxx.elf为编译输出的elf文件,注意,如果想在反汇编的代码中看到C代码与汇编代码的对应关系,记得把GCC的-g选项打开; 由于objdump是直接输出到控制台的,所以需要重定义输出到某个文件中,随便取一个名字即可。 看固件的代码大小,反汇编的时候可长可短,完成之后,打开xxx.dmp文件就可以看到反汇编之后的汇编代码了。 # 5 Q & A - **map文件中可以看到局部变量的地址吗?** 【答案:看不到】 - **执行backtrace操作的时候,有时候会下面的提示,是不是出什么问题了?** `backtrace[03] 01000000 ?? ??:0` - **既然能反汇编,那岂不是没有源码秘密可言?那我不是就可以看到原厂封装的那些库文件的源代码了?**【你觉得呢?】 - **静态库里面能看到结构体是怎么定义的吗?**【答案:看不到】 - **快速阅读汇编代码及了解其大致的实现逻辑,有什么技巧可言?**【你觉得呢?】 # 6 参考链接 - [RISC-V的汇编](https://recan.blog.csdn.net/article/details/122757491) - [ARM的汇编](https://recan.blog.csdn.net/article/details/122757466) # 7 更多分享 > **[架构师李肯](https://recan.blog.csdn.net/?type=blog)** > > 一个专注于嵌入式IoT领域的架构师。有着近10年的嵌入式一线开发经验,深耕IoT领域多年,熟知IoT领域的业务发展,深度掌握IoT领域的相关技术栈,包括但不限于主流RTOS内核的实现及其移植、硬件驱动移植开发、网络通讯协议开发、编译构建原理及其实现、底层汇编及编译原理、编译优化及代码重构、主流IoT云平台的对接、嵌入式IoT系统的架构设计等等。拥有多项IoT领域的发明专利,热衷于技术分享,有多年撰写技术博客的经验积累,连续多月获得RT-Thread官方技术社区原创技术博文优秀奖,荣获[CSDN博客专家](https://recan.blog.csdn.net/?type=blog)、CSDN物联网领域优质创作者、[2021年度CSDN&RT-Thread技术社区之星](https://blog.csdn.net/szullc/article/details/123860472)、[RT-Thread官方嵌入式开源社区认证专家](https://club.rt-thread.org/ask/experts.html)、[RT-Thread 2021年度论坛之星TOP4](https://club.rt-thread.org/ask/article/3317.html)、[华为云云享专家(嵌入式物联网架构设计师)](https://bbs.huaweicloud.com/community/usersnew/id_1573655458316259)等荣誉。坚信【知识改变命运,技术改变世界】! 欢迎关注我的[github仓库01workstation](https://github.com/recan-li/01workstation/tree/master/workspace/gcc/gc_section),日常分享一些开发笔记和项目实战,欢迎指正问题。 同时也非常欢迎关注我的CSDN主页和专栏: [【CSDN主页:架构师李肯】](http://yyds.recan-li.cn) [【RT-Thread主页:架构师李肯】](https://club.rt-thread.org/u/18001) [【C/C++语言编程专栏】](https://blog.csdn.net/szullc/category_8450784.html) [【GCC专栏】](https://blog.csdn.net/szullc/category_8626555.html) [【信息安全专栏】](https://blog.csdn.net/szullc/category_8452787.html) [【RT-Thread开发笔记】](https://blog.csdn.net/szullc/category_11461616.html) [【freeRTOS开发笔记】](https://blog.csdn.net/szullc/category_11467856.html) 有问题的话,可以跟我讨论,知无不答,谢谢大家。
3
条评论
默认排序
按发布时间排序
登录
注册新账号
关于作者
李肯陪你玩赚嵌入式
2022年度和2023年度RT-Thread社区优秀开源布道师,COC深圳城市开发者社区主理人,专注于嵌入式物联网的架构设计
文章
47
回答
504
被采纳
82
关注TA
发私信
相关文章
1
RTTStudio调试时出现Debugger connection lost错误
2
RTT Studio ST-Link 每次Debug调试前都出错,但下载程序正常
3
使用RTT-Thread studio出现无法调试,且程序下载进去毫无反应?
4
rtt-studio debug模式优化
5
stlink下载程序不成功
6
studio中不能进入debug
7
studio Stlink 调试问题
8
同时接多个JLink,下载程序时需选择2次
9
studio的debug,有现场表达式吗?
10
大神们,rtthread-studio怎么自动刷新DEBUG的变量区的值?
推荐文章
1
RT-Thread应用项目汇总
2
玩转RT-Thread系列教程
3
国产MCU移植系列教程汇总,欢迎查看!
4
机器人操作系统 (ROS2) 和 RT-Thread 通信
5
五分钟玩转RT-Thread新社区
6
【技术三千问】之《玩转ART-Pi》,看这篇就够了!干货汇总
7
关于STM32H7开发板上使用SDIO接口驱动SD卡挂载文件系统的问题总结
8
STM32的“GPU”——DMA2D实例详解
9
RT-Thread隐藏的宝藏之completion
10
【ART-PI】RT-Thread 开启RTC 与 Alarm组件
热门标签
RT-Thread Studio
串口
Env
LWIP
SPI
AT
Bootloader
Hardfault
CAN总线
FinSH
ART-Pi
USB
DMA
文件系统
RT-Thread
SCons
RT-Thread Nano
线程
MQTT
STM32
RTC
FAL
rt-smart
ESP8266
I2C_IIC
UART
WIZnet_W5500
ota在线升级
PWM
cubemx
flash
freemodbus
BSP
packages_软件包
潘多拉开发板_Pandora
定时器
ADC
GD32
flashDB
socket
中断
编译报错
Debug
rt_mq_消息队列_msg_queue
SFUD
msh
keil_MDK
ulog
MicroPython
C++_cpp
本月问答贡献
出出啊
1517
个答案
342
次被采纳
小小李sunny
1444
个答案
290
次被采纳
张世争
812
个答案
177
次被采纳
crystal266
547
个答案
161
次被采纳
whj467467222
1222
个答案
148
次被采纳
本月文章贡献
出出啊
1
篇文章
2
次点赞
小小李sunny
1
篇文章
1
次点赞
张世争
1
篇文章
2
次点赞
crystal266
2
篇文章
2
次点赞
whj467467222
2
篇文章
2
次点赞
回到
顶部
发布
问题
投诉
建议
回到
底部