在 C 中使用 LD_PRELOAD 拦截克隆,Linux

Intercept clone using LD_PRELOAD in C, Linux

我想拦截由一个简单的 pthread 程序发出的 clone 调用。我正在尝试使用 LD_PRELOAD 来实现这一点。但是无法处理 clone 调用。当我在 pthread 程序上 运行 strace 时,我可以看到

clone(child_stack=0x7f15b7fa3ef0, flags=CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_SETTLS|CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID, parent_tid=[8895], tls=0x7f15b7fa4640, child_tidptr=0x7f15b7fa4910) = 8895

但是 linux 的克隆签名以及我在共享对象中使用的签名是

int clone(int (*fn)(void *), void *stack, int flags, void *arg, ...
                 /* pid_t *parent_tid, void *tls, pid_t *child_tid */ );

无法获取克隆句柄的原因是由于 straced 和 linux 手动克隆签名之间的差异吗?

LD_PRELOAD 不是截取 pthread 程序进行的 clone 系统调用的正确方法吗?

共享对象代码

#define _GNU_SOURCE
#include <dlfcn.h>
#define _FCNTL_H
#include <sys/types.h>
#include <bits/fcntl.h>
#include <stddef.h>
#include <stdarg.h>
#include <stdio.h>

typedef int (*_clone_f_t)(int (*fn)(void *), void *stack, int flags, void *arg, ...);
static _clone_f_t _clone_f_ = NULL;
void __attribute__((constructor)) my_init();
void __attribute__((destructor)) my_fini();

void my_init(){
    _clone_f_ = (_clone_f_t)dlsym(RTLD_NEXT,"clone");
}

void my_fini(){}


int clone(int (*fn)(void *), void *stack, int flags, void *arg, ...){
    printf("called my clone\n");
    va_list arglist;
    pid_t * parent_tid;
    void * tls;
    pid_t * child_tid;
    va_start(arglist,arg);
    parent_tid = va_arg(arglist, pid_t*);
    tls = va_arg(arglist, void*);
    child_tid = va_arg(arglist, pid_t*);
    va_end(arglist);
//removed rest of the code
}

使用

编译它

gcc -Wall -fPIC -shared -o myclone.so myclone.c -ldl

I want to intercept clone calls made by a simple pthread program.

如果你反汇编libpthread.so.0do_clone()函数),你会发现它不调用clone(),而是调用__clone().

您还可以在 nm -D libpthread.so.0 输出中观察到这一点:

nm -D /lib/x86_64-linux-gnu/libpthread.so.0 | grep clone
                 U __clone@GLIBC_2.2.5

更改 myclone.c 以在具有 GLIBC 2.31 的系统上提供 int __clone(...) 结果:

LD_PRELOAD=./myclone.so ./a.out
called my clone

注意你注释掉的代码:

//removed rest of the code

它的功能受到严格限制 -- 此代码在持有多个 GLIBC 内部锁时被调用,新创建的线程尚未完全初始化,等等。

如果您此时调用 GLIBC,您应该会遇到死锁、崩溃 and/or 神秘错误。