今天写了一个hook函数,执行出错,不得其解,错误信息无法定位原因,使用gdb很快解决。
Ubuntu 16.04.6 LTS
一、背景
写了一个hook函数,放在LD_PRELOAD下,用来监控malloc和free行为,代码如下:
#define _GNU_SOURCE
#include <stdio.h>
#include <stdint.h>
#include <dlfcn.h>
#include <stdlib.h>
#include <sys/syscall.h>
#include <syslog.h>
#include <unistd.h>
#define gettid() syscall(__NR_gettid)
#define unlikely(x) __builtin_expect(!!(x), 0)
#define TRY_LOAD_HOOK_FUNC(name) if (unlikely(!g_sys_##name)) {g_sys_##name = (sys_##name##_t)dlsym(RTLD_NEXT,#name);}
typedef void* (*sys_malloc_t)(size_t size);
static sys_malloc_t g_sys_malloc = NULL;
void* malloc(size_t size)
{
printf("start");
TRY_LOAD_HOOK_FUNC(malloc);
void *p = g_sys_malloc(size);
printf ("%lu malloc (%u) == 0x%08x\n", gettid(), (unsigned int) size, p);
return p;
}
编译文件命令如下:
gcc -o myhook.so -fPIC -shared myhook.c -ldl
测试程序的内容如下malloc.c:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(void) {
printf("enter\n");
void *p = malloc(1);
free(p);
printf("left,%p\n", p);
}
编译这个文件:
gcc -o malloc ./malloc.c
现在使用前面的自定义hook函数输出错误原因:
bingsanlang@ubuntu:~$ LD_PRELOAD=./myhook.so ./malloc
Segmentation fault (core dumped)
从Segmentation fault (core dumped)这个错误信息 根本不知道哪儿出问题了,这时候可以借助下gdb查看下具体错误原因和调用栈。
二、gdb调试问题代码
输入如下命令,对malloc执行文件进行调试:
gdb ./mallco
进入后设置环境变量并r运行:
(gdb) set environment LD_PRELOAD=./hook.so
(gdb) r
Program received signal SIGSEGV, Segmentation fault.
0x00007ffff785819e in _IO_vfprintf_internal (s=0x7ffff7bd0620 <_IO_2_1_stdout_>,
format=0x7ffff7bd57dd "my malloc: %zu\n", ap=ap@entry=0x7fffff7ff428)
at vfprintf.c:1267
1267 vfprintf.c: No such file or directory.
输入bt能看到详细信息:
(gdb) bt
#0 0x00007ffff785819e in _IO_vfprintf_internal (s=0x7ffff7bd0620 <_IO_2_1_stdout_>,
format=0x7ffff7bd57dd "my malloc: %zu\n", ap=ap@entry=0x7fffff7ff428)
at vfprintf.c:1267
#1 0x00007ffff78608a9 in __printf (format=<optimized out>) at printf.c:33
#2 0x00007ffff7bd5754 in malloc () from ./hook.so
#3 0x00007ffff78781e5 in __GI__IO_file_doallocate (
fp=0x7ffff7bd0620 <_IO_2_1_stdout_>) at filedoalloc.c:127
#4 0x00007ffff78865a4 in __GI__IO_doallocbuf (
fp=fp@entry=0x7ffff7bd0620 <_IO_2_1_stdout_>) at genops.c:398
#5 0x00007ffff7885908 in _IO_new_file_overflow (f=0x7ffff7bd0620 <_IO_2_1_stdout_>,
ch=-1) at fileops.c:820
#6 0x00007ffff788429d in _IO_new_file_xsputn (f=0x7ffff7bd0620 <_IO_2_1_stdout_>,
data=0x7ffff7bd57dd, n=11) at fileops.c:1331
#7 0x00007ffff7858251 in _IO_vfprintf_internal (s=0x7ffff7bd0620 <_IO_2_1_stdout_>,
format=0x7ffff7bd57dd "my malloc: %zu\n", ap=ap@entry=0x7fffff7ffbd8)
at vfprintf.c:1320
#8 0x00007ffff78608a9 in __printf (format=<optimized out>) at printf.c:33
#9 0x00007ffff7bd5754 in malloc () from ./hook.so
#10 0x00007ffff78781e5 in __GI__IO_file_doallocate (
fp=0x7ffff7bd0620 <_IO_2_1_stdout_>) at filedoalloc.c:127
#11 0x00007ffff78865a4 in __GI__IO_doallocbuf (
fp=fp@entry=0x7ffff7bd0620 <_IO_2_1_stdout_>) at genops.c:398
#12 0x00007ffff7885908 in _IO_new_file_overflow (f=0x7ffff7bd0620 <_IO_2_1_stdout_>,
ch=-1) at fileops.c:820
堆栈 挺多,有经验的人大概能立马看出来是printf调用 malloc递归调用的问题。
如果没有相关经验 的人,可以根据r运行 的错误 信息 :
1267 vfprintf.c: No such file or directory.
再加上一些关键字:printf malloc等搜索,可以从stackoverflow等 网站上查到问题原因。
三、解决办法
从2可以得到问题的原因是malloc调用printf,printf初始的情况下会调用malloc申请内存,形成递归调用,从而堆栈溢出,程序终止,解决办法就是使用write方法代替printf方法。
问题解决的链接 如下:
https://stackoverflow.com/questions/22196097/ld-preload-causing-segmentation-fault-for-linux-commands
https://docs.oracle.com/cd/E19253-01/819-7050/chapter3-24/
解决之后的代码如
Linux的hook机制:自定义动态链接库hook
文章中所示。