了解gcc将
*.c/cpp
编译成*.o
,再将其链接为可执行程序或/lib库的过程,有助于我们将native从编译/加载/执行到崩溃一条路贯通起来。Android的Makefile只需要将source file填入LOCAL_SRC_FILES
,然后include $(BUILD_SHARED_LIBRARY)
或$(BUILD_EXECUTABLE)
就可以将*.c/cpp/s
编译为动态库或可执行程序。
native编译
编译为obj
在build/core/definitions.mk有定义transform-c-or-s-to-o-no-deps和transform-cpp-to-o,分别将每个.c/s和.cpp编译成*.o,里面传了很多参数给gcc
-fpic -fPIE
- PIC是Position-Independent Code的缩写,经常被用在共享库中,这样就能将相同的库代码为每个程序映射到一个位置,不用担心覆盖掉其他程序或共享库。
- PIE是Position-Independent-Executable的缩写,只能应用在可执行程序中。PIE和PIC很像,但做了一些调整(不用PLT,使用PC相关的重定位)。-fPIE给编译用,-pie给链接(ld)用。
例如,一个程序没有使用PIC被链接到0地址,那么系统将其加载到0地址。
-fstack-protector
顾名思义就是保护堆栈,每一个函数在运行时都有自己的栈帧,如果代码没有写好,很可能将自己甚至是其他的栈帧踩坏,那如何防护呢?简单的方法就是在栈帧头部也就是在局部变量开始之前多存储一个stack_chk_guard值,用于在函数返回前取出来和_stack_chk_guard做对比,失败则调用stack_chk_fail函数,这个就是该参数完成的行为。
静态链接
build/core/combo/TARGET_linux-arm.mk
里有定义transform-o-to-static-executable-inner
,将*.o链接成静态可执行程序,静态可执行程序是一个完整的程序,不需要额外的共享库即可执行,比如/init,/sbin/adbd等。
链接器用的是arm-linux-androideabi-g++
动态链接
build/core/combo/TARGET_linux-arm.mk
里有定义transform-o-to-executable-inner
和transform-o-to-shared-lib-inner
,分别将*.o链接为动态可执行程序和共享库。动态可执行程序需要linker才能进一步运行的。
链接器也是用arm-linux-androideabi-g++
tombstone定位错误方法
signum
一般debuggerd关注的是SIGILL,SIGBUS,SIGABRT,SIGFPE,SIGSEGV,SIGPIPE等。而这里,估计九成都是SIGSEGV (即signal 11),段错误,和非法内存访问等。
*sigcode
- SEGV_MAPERR:访问一个没有映射到任何内容的地址,这种情况通常就是野指针,或者越界访问,访问空指针也是属于这类
- SEGV_ACCERR:试图访问您无权访问的地址。说明访问出错地址,被map到地址空间来了,但是没访问权限。基本上是指针越界或野指针,比如写只读map的内存地址
例如:
1 | signal 11 (SIGSEGV), code 2 (SEGV_ACCERR), fault addr 0x7ecbf6a000 //SEGV_ACCERR表示试图访问您无权访问的地址 |
tombstone日志当中也提供了出错时寄存器地址里面的临近内存信息,信息量同样很丰富。查看0000007e44001c30
附近的内存情况。
1 | memory near x0: |
参考
- Android Native/Tombstone Crash Log 详细分析:https://blog.csdn.net/u011006622/article/details/51496693
- Android Native程序crash的一些定位方法简介:https://msd.misuland.com/pd/300217191876268032
- ARM64-memcpy.S 汇编源码分析:https://blog.csdn.net/ffmxnjm/article/details/68065090
- android bionic memcpy 汇编源码解析:https://blog.csdn.net/qq_28637193/article/details/103681746
- PIC和PIE:https://www.cnblogs.com/sword03/p/9385660.html