日期:2014-05-16  浏览次数:20796 次

Linux Debugging(六): 动态库注入、ltrace、strace、Valgrind

实际上,Linux的调试方法非常多,针对不同的问题,不同的场景,不同的应用,都有不同的方法。很难去概括。本篇文章主要涉及本专栏还没有涵盖,但是的确有很重要的方法。本文主要包括动态库注入调试;使用ltrace命令处理动态库的调试;使用strace调试系统调用的问题;Valgrind的简要介绍。

1. 动态库注入

      如何排除其他library的调用问题?动态库注入(library injection)有可能会让你事半功倍。

一个大型的软件系统,会用到非常多的动态库。那么如果该动态库的一个api调用出了问题,而调用该api的地方非常非常多,不同的调用都分散的记录在不同的log里。那么,如何快速的找到是哪个调用者出的问题? 当然我们可以通过动态库注入的方式去调试。

     下面的代码hook了两个常见的函数memcpy和socket:

void _init(void)
{
  mtrace();
  printf("HOOKing: hello\n");
}

void _fini(void)
{
  printf("HOOKing: goodbye\n");
}

typedef void* (*real_memcpy)(void*, const void*, size_t);
void *memcpy( void *dest, const void*src, size_t size)
{
  real_memcpy real = dlsym((void*)-1, "memcpy");
  printf("Coping from %p to %p, size %d\n", src,dest,size);

  return real(dest, src, size);
}
typedef int (*real_socket)(int socket_family, int socket_type, int protocol);

int socket(int socket_family, int socket_type, int protocol)
{
  printf(" SOCKET family %d, SOCKET type %d, SOECKT protocol %d", socket_family, socket_type, protocol);

  real_socket sock = dlsym((void*)-1, "socket");

 return sock(socket_family, socket_type, protocol );
}


将上述代码编译成动态库后,需要指定环境变量LD_PRELOAD为上述动态库。它的作用是强制load指定的动态库,即使不需要它。你可以在上面的动态库里添加你想要的任何函数。

2. ltrace

  ltrace能够跟踪进程的库函数调用,它会显现出哪个库函数被调用。

   还是使用hello,world进行简单的了解吧:

#include <stdio.h>
int main ()
{
    printf("Hello world!\n");
    return 0;
}

使用ltrace + 命令可以启动对任何程序的调试,上述hello world的ltrace为:

__libc_start_main(0x8048354, 1, 0xbf869aa4, 0x8048390, 0x8048380 <unfinished ...>
puts("Hello world!"Hello world!
)                                                             = 13
+++ exited (status 0) +++

其实ltrace是一个不用去阅读库的实现代码,而去学习库的整体调用栈的很好的方式。当然了结合代码你可以得到更加详细的实现。

3. strace

   strace会跟踪程序系统调用。所以如果是由于程序的系统调用出问题的话,使用strace可以很快的进行问题定位。上述hello world的strace输出为:

execve("./hello", ["./hello"], [/* 30 vars */]) = 0
brk(0)                                  = 0x83d4000
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7f8a000
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY)      = 3
fstat64(3, {st_mode=S_IFREG|0644, st_size=80846, .