【问题标题】:Wrapping a glibc function using the dynamic linker使用动态链接器包装 glibc 函数
【发布时间】:2016-12-14 14:28:39
【问题描述】:

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

【问题讨论】:

    标签: c gcc wrapper glibc dynamic-linking


    【解决方案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

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

    【讨论】:

    • 感谢您的回答。我不使用 LD_PRELOAD,因为重点是仅使用目标的 RPATH 来包装 GLIBC 函数调用。不过,我看不出还有哪个库可以满足这些符号:我已经编辑以包含 LD_DEBUG 输出。
    • @mr_sparxx,在注意到您静态链接 libgcc 的评论后,我已经大幅修改和更新了答案,我之前忽略了这一点。
    • 是的,我的意思是-lstatic-gcc(我已经编辑了我的帖子),这是我的误解,我会尝试你通过更新打开的路径。
    • 其实是-static-libgcc,但目的就是你说的那个。谢谢。
    猜你喜欢
    • 1970-01-01
    • 2010-10-01
    • 2012-10-22
    • 1970-01-01
    • 2012-05-09
    • 1970-01-01
    • 2013-12-01
    • 2013-01-12
    相关资源
    最近更新 更多