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

Wrapping a glibc function using the dynamic linker

我试图通过将我的库注入可执行文件来包装 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 编译它(添加符号版本控制)并静态 link 它到 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

I [place] my library where the executable's RPATH is pointing with the name 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() 实现,而这对它来说是不可用的。

Why is there a relocation error on __libc_start_main and not on system?

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

如果 systemprintf 函数没有重定位错误,那么它们要么不是二进制文件中的外部动态符号,要么被其他库动态满足链接到您的二进制文件。 (更新:链接器调试信息显示前者是这种情况:那些不是外部符号,我指的是没有提供定义的符号;它们是由您的图书馆提供,大概是在 libgcc 中链接的结果。) 细节并不重要,关键是您的策略完全行不通。 考虑使用 LD_PRELOAD 代替。

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