CSAPP 7.13 库打桩机制:运行时打桩例子导致段错误

库打桩

库打桩可以截获对共享库函数的调用,第三个例子 运行时打桩 代码如下:

#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,需要核心转储文件才能查看调用堆栈详情

  1. 首先检查是否允许生成 core 文件,若为 0 则不允许,设置为 unlimited 则不限制大小,ulimit 的设置只会对当前终端有效,新开 shell 还会是默认值
ulimit -a | grep core
ulimit -c unlimited
  1. 检查 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
  1. 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
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值