我试图通过将我的库注入可执行文件来包装GLIBC fstat
函数(它可以是任何其他函数:它只是一个概念证明)。我这样做是将我的库放在可执行文件RPATH
所指的名称为libc.so.6
的位置。
我的图书馆的源代码如下:
#define _GNU_SOURCE
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <dlfcn.h>
int fstat(int fd, struct stat *buf){
typeof(fstat) *old_fstat;
// Try with a printf...
printf("HOOT! fstat wrapped!");
old_fstat = dlsym(RTLD_NEXT, "fstat");
return (*old_fstat)(fd, buf);
}
我用--version-script
编译它(添加符号版本控制)并将其静态链接到libgcc。
gcc -Wall -fPIC -c -o wrapperlib.o wrapperlib.c
gcc -shared -static-libgcc -fPIC -Wl,-soname -Wl,libc.so.6 -Wl,--version-script=wrapperlib.map,-Bstatic -o libc.so.6 wrapperlib.o
wrapperlib.map
包含:
GLIBC_2.0 {
};
当我执行目标程序时,我的库被加载但是我收到以下错误:
./target: relocation error: ./target: symbol __libc_start_main, version GLIBC_2.0 not defined in file libc.so.6 with link time reference
如果我提供自己的__libc_start_main
实现,我就不会收到此错误(当然,它会崩溃)。
int __libc_start_main(int (*main) (int, char **, char **), int argc, char *argv, void (*init) (void), void (*fini) (void), void (*rtld_fini) (void), void *stack_end) {
system("echo I am in __libc_start_main");
printf("printf: I am in __libc_start_main");
fflush(stdin);
return 0;
}
为什么__libc_start_main
而非system
出现重定位错误?
(顺便说一句,对system
的调用确实有效,但对printf
的调用没有产生任何输出。我在这里遗漏了一些明显的东西吗?)
谢谢
编辑:
使用LD_DEBUG=all
运行目标会输出:
http://pastebin.com/iVVbwf6n
答案 0 :(得分:1)
我[放置]我的库,其中可执行文件
RPATH
指向的名称为libc.so.6
。
因此,该过程会加载您的库而不是 GLIBC的libc.so.6
。 除非你提供至少整个C标准库的独立实现,否则肯定不是你想要的。这需要你的库来提供libc.so.6
中所有内容的独立实现,或者动态加载实际libc
。我看到你试图通过静态链接libgcc来实现完整性(我猜你的意思是使用-lstatic-libgcc
),但是
这是错误的库。它提供了支持GCC编译的二进制文件的功能,它可能包含某些C库函数的包装器或替代品,但它本身不提供C库。
即使您静态链接C库(例如-lc_nonshared
),您获得的库也不会包含可动态加载的符号,您可以通过该符号访问包装函数。这是静态链接的直接后果。
你的图书馆无论如何都不是独立的,因为它试图包装GLIBC的fstat()
实现,而这个实现不可用。
为什么
__libc_start_main
而不是system
上有重定位错误?
__libc_start_main
上存在重定位错误,因为该函数由glibc的libc.so.6提供,但不是由您的,也不是您的二进制本身或动态链接到您的二进制文件的任何其他库。 (见上文(1))
如果system
或printf
函数没有重定位错误,那么它们不是二进制中的外部动态符号,或者是动态链接到的其他库所满足的你的二进制。 ( 更新: 链接器调试信息显示前者是这种情况:那些不是外部符号,我指的是没有提供定义的符号;它们是由您的库提供的,大概是由于链接在libgcc中。)细节并不重要,因为关键是您的策略完全不可行。考虑使用{ {3}}而不是。
更新:另一种方法可能是为您的共享库提供一个构造函数,dlopen()
是真正的libc.so.6
并启用了标记RTLD_GLOBAL
。这样,您的库本身不必提供完整的C库,但它确实需要知道如何找到真正的库。这肯定会解决包装函数在包装器中不可用的问题。