使用动态链接器包装glibc函数

4

我正在尝试通过将我的库注入到可执行文件中,来包装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
1个回答

1
我将我的库放在可执行文件的RPATH指向的位置,并命名为libc.so.6。因此,进程加载您的库,而不是GLIBC的libc.so.6。这肯定不是您想要的,除非您提供了至少整个C标准库的独立实现。这需要您的库提供libc.so.6中所有内容的独立实现,否则动态加载真正的libc。我看到您尝试通过静态链接libgcc(我猜您的意思是使用-lstatic-libgcc)来实现完整性,但是...
  1. 那个库是错误的。它提供支持GCC编译的二进制文件的函数,其中可能包括一些C库函数的包装器或替代品,但它本身并没有提供C库。

  2. 即使您静态链接了C库(例如-lc_nonshared),以这种方式获得的库也不会包含一个动态可加载的符号,通过它可以访问包装的函数。这是静态链接的直接结果。

  3. 无论如何,您的库都不是独立的,因为它试图包装GLIBC对fstat()的实现,而该实现对其不可用。

为什么__libc_start_main上有重定位错误而system上没有?

__libc_start_main上有重定位错误,因为该函数由glibc的libc.so.6提供,但不是由您的库、您的二进制文件本身或任何其他动态链接到您的二进制文件的库提供的。(见上面的(1))

如果systemprintf函数没有重定位错误,那么要么它们不是您二进制文件中的外部动态符号,要么它们由与您二进制文件动态链接的其他库满足。(更新: 链接器调试信息显示前者是正确的: 它们不是外部符号,我指的是没有提供定义的符号; 它们由您的库提供,可能是通过链接libgcc而获得的。) 细节并不重要,因为问题在于您的策略根本行不通。 考虑使用LD_PRELOAD代替。 更新: 另一种选择可能是给您的共享库一个构造函数,该函数使用启用了标志RTLD_GLOBALdlopen()打开真正的libc.so.6库。这样,您的库就不必自己提供完整的C库,但它确实需要知道如何找到真正的库。这肯定可以解决包装函数在包装器中无法使用的问题。

您IP地址为143.198.54.68,由于运营成本限制,当前对于免费用户的使用频率限制为每个IP每72小时10次对话,如需解除限制,请点击左下角设置图标按钮(手机用户先点击左上角菜单按钮)。 - 0xdeda10
@mr_sparxx,在注意到您的评论后,我对答案进行了实质性的修订和更新,因为我之前忽略了您静态链接libgcc的情况。 - John Bollinger
是的,我的意思是-lstatic-gcc(我已经编辑了我的帖子),那是我的误解,我会尝试您更新开辟的道路。 - 0xdeda10
实际上是 -static-libgcc,但目的正如您所提到的。谢谢。 - 0xdeda10

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