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