Android linker可执行程序加载库失败时输出更详细调试信息

Android linker可执行程序加载库失败时输出更详细调试信息

@(Android研究)[android|linker]


[TOC]


设定场景

可执行文件名:the_exe

假设执行the_exe时,Android linker加载库会失败,那么通常会输出下面的信息:

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中的构造函数时发生了错误。可是一个可执行文件会加载很多动态库,而一个动态库中又可以有多个构造函数,到底是哪个库的哪个构造函数执行时出了问题哪?通过上面的错误信息并不能给出原因所在。

获得错误详细信息的方法

按照下面的方法可以获得错误的详细信息,设置LD_DEBUG环境变量:

export LD_DEBUG=10

按照上面设置完LD_DEBUG环境变量后,再执行"./the_exe"时将会输出详细的错误信息。

原理

下面以Android5.1.1源码为基础进行解析。

Android linker代码在"android/system/bionic/linker/"目录下。soinfo::CallFunction函数在"android/system/bionic/linker/linker.cpp"文件中,下面是该函数的源码:

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(...)语句,这个语句用来输出一些信息,如调用的函数名、函数地址等。那么为什么上面的日志中没有输出这些信息哪?

找到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中。

转载于:https://my.oschina.net/ibuwai/blog/650994

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值