@(Android研究)[android|linker]android
[TOC]c++
可执行文件名:the_exeshell
假设执行the_exe时,Android linker加载库会失败,那么一般会输出下面的信息:app
01-30 20:36:10.376 2078-2078/? A/libc: Fatal signal 11 (SIGSEGV), code 1, fault addr 0xc0011c80 in tid 2078 (the_exe) 01-30 20:36:10.377 197-197/? I/DEBUG: property debug.db.uid not set; NOT waiting for gdb. 01-30 20:36:10.377 197-197/? I/DEBUG: HINT: adb shell setprop debug.db.uid 100000 01-30 20:36:10.377 197-197/? I/DEBUG: HINT: adb forward tcp:5039 tcp:5039 01-30 20:36:10.482 197-197/? I/DEBUG: *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 01-30 20:36:10.482 197-197/? I/DEBUG: Build fingerprint: 'google/hammerhead/hammerhead:5.1.1/LMY48M/2167285:user/release-keys' 01-30 20:36:10.482 197-197/? I/DEBUG: Revision: '11' 01-30 20:36:10.482 197-197/? I/DEBUG: ABI: 'arm' 01-30 20:36:10.483 197-197/? I/DEBUG: pid: 2078, tid: 2078, name: the_exe >>> ./the_exe <<< 01-30 20:36:10.483 197-197/? I/DEBUG: signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0xc0011c80 01-30 20:36:10.491 26449-26682/system_process W/NativeCrashListener: Couldn't find ProcessRecord for pid 2078 01-30 20:36:10.558 197-197/? I/DEBUG: r0 00000000 r1 b6fa83bf r2 c0011c81 r3 b6fad714 01-30 20:36:10.558 197-197/? E/DEBUG: AM write failure (32 / Broken pipe) 01-30 20:36:10.558 197-197/? I/DEBUG: r4 c0011c80 r5 b6fa83bf r6 b6f9b004 r7 b6fa8486 01-30 20:36:10.558 197-197/? I/DEBUG: r8 00000010 r9 b6fad714 sl 00000001 fp 6011fdb8 01-30 20:36:10.558 197-197/? I/DEBUG: ip b6fa8486 sp bec6d7a8 lr b6f9f5b7 pc c0011c80 cpsr 800f0010 01-30 20:36:10.559 197-197/? I/DEBUG: #00 pc c0011c80 <unknown> 01-30 20:36:10.559 197-197/? I/DEBUG: #01 pc 000015b5 /system/bin/linker (__dl__ZN6soinfo12CallFunctionEPKcPFvvE+44) 01-30 20:36:10.559 197-197/? I/DEBUG: #02 pc 00001689 /system/bin/linker (__dl__ZN6soinfo9CallArrayEPKcPPFvvEjb+140) 01-30 20:36:10.559 197-197/? I/DEBUG: #03 pc 0000185f /system/bin/linker (__dl__ZN6soinfo16CallConstructorsEv+142) 01-30 20:36:10.559 197-197/? I/DEBUG: #04 pc 00003ae7 /system/bin/linker (__dl___linker_init+1594) 01-30 20:36:10.559 197-197/? I/DEBUG: #05 pc 00000a98 /system/bin/linker (_start+4) 01-30 20:36:10.559 197-197/? I/DEBUG: #06 pc 00000a98 /system/bin/linker (_start+4) 01-30 20:36:10.559 197-197/? I/DEBUG: #07 pc 00000a98 /system/bin/linker (_start+4) ......
查看上面的信息,能够发现出错点在"#01 pc 000015b5 /system/bin/linker (__dl__ZN6soinfo12CallFunctionEPKcPFvvE+44)","/system/bin/linker"代表这个出错点在linker中。又能够发现"#03 pc 0000185f /system/bin/linker (__dl__ZN6soinfo16CallConstructorsEv+142)"这行错误信息,经过这个错误信息可知当linker调用so中的构造函数时发生了错误。但是一个可执行文件会加载不少动态库,而一个动态库中又能够有多个构造函数,究竟是哪一个库的哪一个构造函数执行时出了问题哪?经过上面的错误信息并不能给出缘由所在。tcp
按照下面的方法能够得到错误的详细信息,设置LD_DEBUG环境变量:ionic
export LD_DEBUG=10
按照上面设置完LD_DEBUG环境变量后,再执行"./the_exe"时将会输出详细的错误信息。函数
下面以Android5.1.1源码为基础进行解析。post
Android linker代码在"android/system/bionic/linker/"目录下。soinfo::CallFunction函数在"android/system/bionic/linker/linker.cpp"文件中,下面是该函数的源码:ui
void soinfo::CallFunction(const char* function_name __unused, linker_function_t function) { if (function == nullptr || reinterpret_cast<uintptr_t>(function) == static_cast<uintptr_t>(-1)) { return; } TRACE("[ Calling %s @ %p for '%s' ]", function_name, function, name); function(); TRACE("[ Done calling %s @ %p for '%s' ]", function_name, function, name); // The function may have called dlopen(3) or dlclose(3), so we need to ensure our data structures // are still writable. This happens with our debug malloc (see http://b/7941716). protect_data(PROT_READ | PROT_WRITE); }
能够发如今函数代码中有TRACE(...)语句,这个语句用来输出一些信息,如调用的函数名、函数地址等。那么为何上面的日志中没有输出这些信息哪?google
找到TRACE宏定义的地方,"android/system/bionic/linker/linker_debug.h"文件,下面是这个宏的定义:
#define TRACE(x...) _PRINTVF(1, x)
_PRINTVF宏定义在"android/system/bionic/linker/linker_debug.h"文件中,下面是_PRINTVF宏的定义:
#if LINKER_DEBUG_TO_LOG #define _PRINTVF(v, x...) \ do { \ if (g_ld_debug_verbosity > (v)) __libc_format_log(5-(v), "linker", x); \ } while (0) #else /* !LINKER_DEBUG_TO_LOG */ #define _PRINTVF(v, x...) \ do { \ if (g_ld_debug_verbosity > (v)) { __libc_format_fd(1, x); write(1, "\n", 1); } \ } while (0) #endif /* !LINKER_DEBUG_TO_LOG */
依据对"if (g_ld_debug_verbosity > (v)) ..."语句的分析,我认为_PRINTVF宏的第一个参数"v"表明的是输出级别,当g_ld_debug_verbosity比"v"大时,便会执行后面的语句进行输出。
g_ld_debug_verbosity是个全局变量,它在"android/system/bionic/linker/linker.cpp"文件的ElfW函数中被赋值,下面是ElfW函数的代码片断:
static ElfW(Addr) __linker_init_post_relocation(KernelArgumentBlock& args, ElfW(Addr) linker_base) { ...... debuggerd_init(); // Get a few environment variables. const char* LD_DEBUG = linker_env_get("LD_DEBUG"); if (LD_DEBUG != nullptr) { g_ld_debug_verbosity = atoi(LD_DEBUG); } ...... }
经过上面代码可知g_ld_debug_verbosity变量的值与环境变量"LD_DEBUG"有关,若是定义了"LD_DEBUG"环境变量那么会将这个环境变量的值赋给g_ld_debug_verbosity。
回到TRACE宏,能够发现当g_ld_debug_verbosity的值大于1,即"LD_DEBUG"环境变量的值大于1时,TRACE宏所表明的代码就会输出日志。
一样,要想让PRINT、INFO、DEBUG这些宏输出日志,作法和原理与TRACE相同。
连接器会将日志输出到logcat中,可是不知为什么有时它会丢掉一些连接日志
,若是想要不丢日志那么须要修改Android源码,打开"android/system/bionic/linker/linker_debug.h"文件,将LINKER_DEBUG_TO_LOG宏的值从1改成0,而后使用"mmm"命令从新编译linker再将编译好的linker覆盖Android设备上的/system/bin/linker。
新linker的连接日志将会直接输出到终端上,这部分的日志是完整
的;不过错误堆栈(“设定场景”一节中列出的日志内容)仍是会输出到logcat中。