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

时间:2016-12-14 14:28:40

标签: c gcc wrapper glibc dynamic-linking

我试图通过将我的库注入可执行文件来包装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 个答案:

答案 0 :(得分: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()实现,而这个实现不可用。

  4.   

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

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

    如果systemprintf函数没有重定位错误,那么它们不是二进制中的外部动态符号,或者是动态链接到的其他库所满足的你的二进制。 ( 更新: 链接器调试信息显示前者是这种情况:那些不是外部符号,我指的是没有提供定义的符号;它们是由您的库提供的,大概是由于链接在libgcc中。细节并不重要,因为关键是您的策略完全不可行。考虑使用{ {3}}而不是。

    更新:另一种方法可能是为您的共享库提供一个构造函数,dlopen()是真正的libc.so.6并启用了标记RTLD_GLOBAL。这样,您的库本身不必提供完整的C库,但它确实需要知道如何找到真正的库。这肯定会解决包装函数在包装器中不可用的问题。