C++函数调用栈回溯初探
背景
之前在解决一个Flutter Engine的C++ Crash问题时,就曾受困于一个问题:如何回溯C++的 函数调用栈?最近频繁使用C++的过程中,这个诉求日益突出。 遂下定决心彻底解决之。
过程
在stackoverflow上,有两个比较有参考价值的问题:
- c++ - Android NDK: getting the backtrace - Stack Overflow
- print call stack in C or C++ - Stack Overflow
具体来说,问题是Android平台、ARM64架构下的C++回溯。
其中, Eugene Shapovalov的回答试了下,效果比较接近,但是无法通过addr2line还原。
一番查找后,发现 -funwind-tables
和 -fno-omit-frame-pointer
在构建时必需加上:
此时,仍无法还原,addr2line无法解析。经过一番探索之后,发现是因为unwind直接得到的是实际的运行地址,而 addr2line 的 输入应该是so内部的偏移值,具体的做法下面的文章有提及:
- 深入浅出Android NDK之打印调用堆栈_taohongtaohuyiwei的博客-CSDN博客_android ndk 打印堆栈
- Android 各类环境下打印调用堆栈的方法(jni,lua,java) - 简书
此时,得到的堆栈就可以正常回溯了。
以上方法是直接在代码中获取so基址,然后进行处理。其实也可以通过adb手动获取:
- 首先获取目标应用的进程id
- 通过
run as
的方式获取maps文件内容:adb shell "run-as com.tencent.**** sh -c 'cat /proc/18454/maps'"
- 找到对应so的加载基址,即可计算出偏移值,用addr2line还原
参考文章:
- Android逆向分析——得到SO基址的方法: https://blog.csdn.net/m0_38076341/article/details/118711005
其他参考文章:
- 探索Android平台ARM unwind技术: https://zhuanlan.zhihu.com/p/336916116
- 探索Android平台ARM unwind技术-2: https://zhuanlan.zhihu.com/p/345878125
- https://github.com/iqiyi/xCrash
- https://www.digitalpeer.com/blog/find-where-a-cpp-exception-is-thrown
- https://stackoverflow.com/questions/8115192/android-ndk-getting-the-backtrace
- Android Native 内存泄漏系统化解决方案: https://www.cnblogs.com/amap_tech/p/11199813.html
- /proc/<pid>/maps简要分析: https://www.cnblogs.com/arnoldlu/p/10272466.html
- https://github.com/frida
这个问题,说难也不难,只是需要一些背景知识的积累,这也算是使用C++这门语言进行开发的典型特征了。