如何在Linux上重新实现(或包装)系统调用函数?

32
假设我想完全接管open()系统调用,可能是为了包装实际的syscall并执行一些日志记录。 一种方法是使用LD_PRELOAD 加载一个(用户制作的)共享对象库,以接管open()入口点。
用户制作的open()例程然后通过dlsym()获取指向glibc函数open()的指针,并调用它。
上述提出的解决方案是一种动态解决方案。但是假设我要静态链接自己的open()包装器。我该怎么办?我猜机制是相同的,但我也猜测用户定义的open()和libc open()之间会发生符号冲突。
请分享任何其他实现相同目标的技术。

你在代码中加一个包装函数/宏怎么样? - Seamus Connor
@Seamus:我更喜欢不使用宏。我并没有真正的问题。我只是想增加一些 SO 知识并学习一些新技巧。 - Stefano Borini
3个回答

63
你可以使用由ld提供的包装(wrap)功能。从 man ld 中可以看到:

--wrap symbol 使用symbol的包装函数。任何未定义的对于 symbol 的引用将被解析为__wrap_symbol

任何未定义的对于__real_symbol的引用将被解析为symbol

因此,你只需要在你的包装函数中添加前缀__wrap_,同时在想要调用真实函数时使用__real_前缀。以下是一个简单的例子: malloc_wrapper.c:
#include <stdio.h>
void *__real_malloc (size_t);

/* This function wraps the real malloc */
void * __wrap_malloc (size_t size)
{
    void *lptr = __real_malloc(size);
    printf("Malloc: %lu bytes @%p\n", size, lptr);
    return lptr;
}

测试应用程序testapp.c

#include <stdio.h>
#include <stdlib.h>
int main()
{
    free(malloc(1024)); // malloc will resolve to __wrap_malloc
    return 0;
}
然后编译应用程序:
gcc -c malloc_wrapper.c
gcc -c testapp.c
gcc -Wl,-wrap,malloc testapp.o malloc_wrapper.o -o testapp

生成的应用程序输出结果为:

$ ./testapp
Malloc: 1024 bytes @0x20d8010

1
似乎调用malloc的函数无法进行动态链接。例如,我编写了一个tools.c,其中包含一个调用malloc的perform_alloc()函数。然后,如果我先制作一个libtools.so,并通过“-ltools”进行动态链接,那么“-Wl,-wrap,malloc”就不起作用了。 - xanpeng
1
正如在https://dev59.com/R1DTa4cB1Zd3GeqPMdEh中所回答的那样,当在C++程序中使用此代码时,需要在函数定义前加上`extern "C"`前缀。 - MKroehnert
这个函数必须被命名为 __wrap_malloc 吗?还是它可以取一个不同的名字?我的意思是,在包装系统调用时,我们是否必须将函数命名为与实际调用相同的名称? - Munib
@return0: 正如 man ld 所示,符号必须具有前缀 __wrap___real_ - Giuseppe Cardone

3
符号是按照在命令行上列出的顺序由链接器解析的,因此如果您在标准库之前列出了您的库,则具有优先权。对于gcc,您需要指定:
gcc <BLAH> -nodefaultlibs <BLAH BLAH> -lYOUR_LIB <OTHER_LIBS>

这样,您的库将首先被搜索和找到。

0
对于Linux和GNU libc,该库内置了拦截和重新实现库中任何函数的支持。
如果您定义了自己的任何libc函数版本,并在libc之前链接它(使其成为可执行文件的一部分,或者在链接命令中在-lc之前链接的库,甚至使用LD_PRELOAD加载,如果libc是动态链接的),那么它将被调用而不是libc版本(甚至是libc本身的其他函数中的调用)。然后,您可以使用__libc_前缀调用该函数以获取库中的实际版本(尽管您需要自己声明该符号)。例如:
       #include <sys/types.h>
       #include <sys/stat.h>
       #include <fcntl.h>

       extern int __libc_open(const char *pathname, int flags, mode_t mode);

       int open(const char *pathname, int flags, mode_t mode) {
           return __libc_open(pathname, flags, mode);
       }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接