ARM的栈回溯(一):函数调用栈的简介
本系列文章共三篇。本文是第一篇,讲一些栈回溯的背景,不涉及技术细节。关键词:arm unwind,ehabi,arm stacktrace。
起因
故事要从几个月前的一个 arm crash 说起,把 crash 交给新来的小朋友看,他说 IDA 里显示的栈回溯和logcat里显示的栈回溯是不一致的,问我为什么。
我说一直是logcat里看的,是正确的;那么 ida 里只有一层栈回溯肯定是错的,但我却解释不来原因,于是有了本文和一系列研究。
平时讨论的函数调用栈结构
从小老师就教育我们,函数开头一般是这三句话,用于保存堆栈,开辟新的栈空间:
| 1 | push ebp | 
在这种设定下,栈回溯变得非常简单,ebp 就是栈帧,ebp 附近是上一个栈帧,再附近是返回地址。网上相关的文章一搜一大把,这里就不多讲了,找一张网图凑合一下。
 
arm 的栈结构
我们随便找个 /system/lib/libc.so,再随便编译一个 so,随便找几个函数看一下,发现和x86的不大一样。
| 1 | .text:000233C0 PUSH.W {R4-R8,LR} | 
| 1 | .text:00023A94 PUSH.W {R4-R9,LR} | 
| 1 | .text:0003477C PUSH {R7,LR} | 
| 1 | .text:000138C4 PUSH {R4,R5,R7,LR} | 
观察这几组汇编,前两段 sp 的内容并没有被保存到任意一个寄存器里,但它可以被正确栈回溯,暗示栈回溯信息不在这段汇编里;后两段,把 sp 放到 r7 里,把 sp+8 放到 r7 里,有点像栈帧的感觉,并且函数内也没有覆盖掉 r7 的内容,有点 x86 的感觉。
查阅资料,随着时代发展,arm 有两种 unwind 方式:
- 一种是古老的,和 x86 类似的(目前没有找到样例,可能在某种编译选项下存在),使用专用的 fp寄存器保存原先的sp,fp在函数内禁止被改写,thumb 模式下使用r7作为fp,arm 模式下使用r11作为fp。
- 另一种是流行的, arm 特有的(目前绝大部分都使用这种方式),遵从 eabi 里的 ehabi 标准,即 exception handler abi,定义了一套专属的标准。简而言之就是对每个函数分配自己专用的字节码,解释执行,从而实现栈回溯。
使用readelf -u可以查看,字节码长这样:
| 1 | 0x9a8c <__cxa_end_cleanup_impl>: @0x14f28 | 
arm ehabi
讲了这么多,终于引出本系列的重点:arm ehabi。
官方文档,复杂但权威:https://developer.arm.com/documentation/ihi0038/b/
看雪有篇不错的文档:原创andorid native栈回溯原理分析与思考
- 使用 readelf -u可以打印相关信息,也可以使用pyelftools里的readelf.py -au打印出来(而且这个功能是我写的)。
- 千万不要使用 llvm-readelf -u,因为它有 bug,只支持.o文件。
名词解释
| 名词 | 解释 | 
|---|---|
| stack unwind | 意思就是栈回溯。 | 
| abi | (application binary interface)二进制应用接口,相当于标准和规范 | 
| arm eabi | arm 很多规范的合集,包括 AADWARF、AAELF等,也包括 CLIBABI、CPPABI、【EHABI】 | 
| arm ehabi | arm 的 exception handler abi | 
| exception handler | 异常处理,既包括 crash 时的栈回溯,也包括 c++ 里的异常处理。 | 
| arm exception handler index table | 指 .ARM.exidx,存放函数offset、简单的handler 的数据、复杂handler的索引。 | 
| arm exception handler table | 指 .ARM.extab,存放复杂 handler 的数据。 | 
和平时逆向相关的,有两部分内容,有个大致认知就行:
- 数据存放。在 ELF 文件里肯定存放了信息,指导如何进行栈回溯,.ARM.exidx和.ARM.extab就是做这件事情的;
- 数据使用。每个函数 unwind 时需要解析 exception handler table,需要解释执行字节码,这个功能有时会由操作系统完成(例如 crash 的时候),有时会由应用程序自己完成(例如使用写代码主动进行栈回溯)。
总结
本文讲的是背景,没什么技术细节,第二篇讲文件格式。
第一篇指路:https://leadroyal.cn/p/1125
第二篇指路:https://leadroyal.cn/p/1131
第三篇指路:https://leadroyal.cn/p/1135