如何在 Linux 上重新实现(或包装)系统调用函数?
- 2024-11-01 08:41:00
- admin 原创
- 211
问题描述:
假设我想要完全接管 open() 系统调用,也许是为了包装实际的系统调用并执行一些日志记录。一种方法是使用 LD_PRELOAD加载接管 open() 入口点的(用户制作的)共享对象库。
open()
然后,用户编写的 open() 例程通过dlsym()
ing 并调用它来获取指向 glibc 函数的指针。
但是,上面提出的解决方案是动态解决方案。假设我想静态链接自己的包装器。我该怎么做?我猜机制是一样的,但我还猜想用户定义和 libcopen()
之间会发生符号冲突。open()
`open()`
请分享任何其他技术来实现相同目标。
解决方案 1:
您可以使用 提供的换行功能ld
。来自man ld
:
--wrap 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
", 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
解决方案 2:
链接器按照您在命令行中列出的顺序解析符号,因此如果您在标准库之前列出您的库,您将具有优先级。对于 gcc,您需要指定
gcc <BLAH> -nodefaultlibs <BLAH BLAH> -lYOUR_LIB <OTHER_LIBS>
这样,您的图书馆就会首先被搜索并找到。
解决方案 3:
对于 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);
}
扫码咨询,免费领取项目管理大礼包!