库打桩
库打桩可以截获对共享库函数的调用,第三个例子 运行时打桩 代码如下:
#ifdef RUNTIME
#define _GNU_SOURCE // RTLD_NEXT 需要 _GNU_SOURCE 才能使用
#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>
void* malloc(size_t size)
{
void* (*mallocptr)(size_t size);
char* error;
// RTLD_NEXT:查找调用者所在共享库之后的下一个库
// 这样调用libc.so中的malloc,而不是直接 malloc( ) 导致调用自身
mallocptr = dlsym(RTLD_NEXT, "malloc");
if ((error = dlerror()) != NULL) {
fputs(error, stderr);
exit(1);
}
char* ptr = mallocptr(size);
printf("malloc(%d) = %p\n", (int)size, ptr);
return ptr;
}
void free(void* ptr)
{
if (!ptr)
return;
void (*freeptr)(void*) = NULL;
char* error;
freeptr = dlsym(RTLD_NEXT, "free");
if ((error = dlerror()) != NULL) {
fputs(error, stderr);
exit(1);
}
freeptr(ptr);
printf("free(%p)\n", ptr);
}
#endif
使用以下命令制作动态库:
gcc -DRUNTIME -shared -fpic -o mymalloc.so mymalloc.c -ldl
示例中运行以下会导致 SIGSEGV 信号的段错误
LD_PRELOAD=./mymalloc.so ./intr
使用core文件调试
使用 gdb 调试需要先将动态链接库和 intr 重新添加 -g 选项编译,调试也是 segment fault,需要核心转储文件才能查看调用堆栈详情
- 首先检查是否允许生成 core 文件,若为 0 则不允许,设置为 unlimited 则不限制大小,
ulimit的设置只会对当前终端有效,新开 shell 还会是默认值
ulimit -a | grep core
ulimit -c unlimited
- 检查 core 生成目录,下面生成 core 文件模式:
%e表示程序名,%p表示进程号,%t表示时间戳
# 若为 core 表示会在当前目录下生成名为 core 文件
cat /proc/sys/kernel/core_pattern
# 若自定义 core 生成目录,需要有写权限
sudo sysctl -w kernel.core_pattern=/tmp/corefiles/core_%e_%p_%t
mkdir -p /tmp/corefiles
chmod 777 /tmp/corefiles
- gdb 调试,将
core文件作为第二个参数传入,使用backtrace命令查看函数调用堆栈,堆栈帧编号表示:编号大者调用编号小者
gdb ./intr core
# in gdb
set environment LD_PRELOAD=./mymalloc.so
bt
堆栈反复出现以下,malloc 即为打桩后 mymalloc.c 中的包装函数,其中调用了 printf 打印输出,如 #36 ,但是 printf 中 __GI__IO_doallocbuf 可能需要使用到 malloc 分配缓冲,可以看到 #28 再次调用 malloc 分配 size=1024 大小,于是循环调用导致栈溢出
#27 0x00007606d146079f in __printf (format=<optimized out>) at ./stdio-common/printf.c:33
#28 0x00007606d181022a in malloc (size=1024) at mymalloc.c:87
#29 0x00007606d147eba4 in __GI__IO_file_doallocate (fp=0x7606d161b780 <_IO_2_1_stdout_>) at ./libio/filedoalloc.c:101
#30 0x00007606d148dce0 in __GI__IO_doallocbuf (fp=fp@entry=0x7606d161b780 <_IO_2_1_stdout_>) at ./libio/libioP.h:947
#31 0x00007606d148cf60 in _IO_new_file_overflow (f=0x7606d161b780 <_IO_2_1_stdout_>, ch=-1) at ./libio/fileops.c:744
#32 0x00007606d148b6d5 in _IO_new_file_xsputn (n=7, data=<optimized out>, f=<optimized out>) at ./libio/libioP.h:947
#33 _IO_new_file_xsputn (f=0x7606d161b780 <_IO_2_1_stdout_>, data=<optimized out>, n=7) at ./libio/fileops.c:1196
#34 0x00007606d147514d in outstring_func (done=0, length=7, string=0x7606d1811007 "malloc(%d) = %p\n",
s=0x7606d161b780 <_IO_2_1_stdout_>) at ../libio/libioP.h:947
--Type <RET> for more, q to quit, c to continue without paging--
#35 __vfprintf_internal (s=0x7606d161b780 <_IO_2_1_stdout_>, format=0x7606d1811007 "malloc(%d) = %p\n", ap=ap@entry=0x7ffdf957bb30,
mode_flags=mode_flags@entry=0) at ./stdio-common/vfprintf-internal.c:1263
#36 0x00007606d146079f in __printf (format=<optimized out>) at ./stdio-common/printf.c:33
#37 0x00007606d181022a in malloc (size=1024) at mymalloc.c:87
解决方式
在 malloc 包装的实现中不应该调用任何依赖于 malloc 的函数,如 printf,可以使用 write 系统调用进行替换,可以正常打桩:
#ifdef RUNTIME
#define _GNU_SOURCE // RTLD_NEXT 需要 _GNU_SOURCE 才能使用
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <dlfcn.h>
#include <string.h>
void* malloc(size_t size)
{
void* (*mallocptr)(size_t size);
char* error;
// RTLD_NEXT:查找调用者所在共享库之后的下一个库
// 这样调用libc.so中的malloc,而不是直接 malloc( ) 导致调用自身
mallocptr = dlsym(RTLD_NEXT, "malloc");
if ((error = dlerror()) != NULL) {
// fputs(error, stderr);
write(STDERR_FILENO, error, strlen(error));
exit(1);
}
char* ptr = mallocptr(size);
// printf("malloc(%d) = %p\n", (int)size, ptr);
char buffer[64];
int len = snprintf(buffer, sizeof(buffer), "malloc(%d) = %p\n", (int)size, ptr);
write(STDOUT_FILENO, buffer, len);
return ptr;
}
void free(void* ptr)
{
if (!ptr)
return;
void (*freeptr)(void*) = NULL;
char* error;
freeptr = dlsym(RTLD_NEXT, "free");
if ((error = dlerror()) != NULL) {
// fputs(error, stderr) // fputs好像没事 不会调用malloc
write(STDERR_FILENO, error, strlen(error));
exit(1);
}
freeptr(ptr);
// printf("free(%p)\n", ptr);
char buffer[64];
int len = snprintf(buffer, sizeof(buffer), "free(%p)\n", ptr);
write(STDOUT_FILENO, buffer, len);
}
#endif
1万+

被折叠的 条评论
为什么被折叠?



