开发者在分析系统稳定性的时候通常需要知道进程发生异常时的调用栈来分析问题,确认发生异常时进程正在做什么。这样才能根据进程所处场景或者调用栈、寄存器信息分析异常发生的原因。而在Android中,当Native进程发生崩溃时,会在/data/tombstones/目录下生成tombstones_xxx的文件,里面记录了某一个进程在发生崩溃时候的所有信息,包括调用栈、寄存器信息等等。这极大的有助于开发者来定位问题,本篇文章来分析下TombStone文件到底是如何生成的。
首先我们要知道,Native进程发生崩溃的时候,是因为接收到了Kernel发送的异常信号。这些信号是如何发送给当前进程呢?在Android平台上Native进程通过fork创建,然后由exec族的函数将elf文件替换原本的程序段,加载对应的程序指令,接着用通过linker32或者linker64去加载动态共享库,例如libc.so等等。而在Android平台中,linker会注册异常信号到kernel,然后通过回调函数来处理异常信号,在回调的过程就生成了TombStone文件,接下来看一下注册和回调的过程。
首先用objdump对linker64可执行程序进行反汇编,通过readelf找到elf文件执行的入口地址0x4cbb0,然后在反汇编中找到对应的地址就找到了对应的入口函数(类似于main函数),linker中__dl__start就是入口函数。
aarch64-linux-android-readelf -h linker64
ELF Header:
...
Machine: AArch64
Version: 0x1
Entry point address: 0x4cbb0
Start of program headers: 64 (bytes into file)
Start of section headers: 1440120 (bytes into file)
...
000000000004cbb0 <__dl__start>:
4cbb0: 910003e0 mov x0, sp
4cbb4: 9400cc07 bl 7fbd0 <__dl__linker_init>
4cbb8: d61f0000 br x0
4cbbc: 00000000 .inst 0x00000000 ; undefined
由于Android.bp中默认添加了前缀"_dl",prefix_symbols: “_dl”,所以入口地址就是_start。此处是用汇编写的,主要就跳转到__linker_init去执行。
android/bionic/linker/arch/arm64/begin.S
#include <private/bionic_asm.h>
ENTRY(_start)
// Force unwinds to end in this function.
.cfi_undefined x30
mov x0, sp
bl __linker_init
/* linker init returns the _entry address in the main image */
br x0
END(_start)
extern "C" ElfW(Addr) __linker_init(void* raw_args) {
...
return __linker_init_post_relocation(args, tmp_linker_so);
}
static ElfW(Addr) __attribute__((noinline))
__linker_init_post_relocation(KernelArgumentBlock& args, soinfo& tmp_linker_so) {
...
sonext = solist = solinker = get_libdl_info(kLinkerPath, tmp_linker_so);
g_default_namespace.add_soinfo(solinker);
init_link_map_head(*solinker, kLinkerPath);
ElfW(Addr) start_address = linker_main(args, exe_to_load);
INFO("[ Jumping to _start (%p)... ]", reinterpret_cast<void*>(start_address));
// Return the address that the calling assembly stub should jump to.
return start_address;
}
从代码逻辑来看,最后返回的是起始地址,接下来调用linker_main,函数中初始化了系统属性,并且调用debuggerd_init,并且传入了一个callback,但是此处并不是处理异常信号的回调。异常信号的注册是在debuggerd_init中。其中设置了接受异常信号的回调函数debuggerd_signal_handler以及要接收哪些异常信号。
static ElfW(Addr) linker_main(KernelArgumentBlock& args, const char* exe_to_load) <