使用动态链接器包装 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
),但是
那是错误的图书馆。它提供了支持 GCC 编译的二进制文件的函数,其中可能包括一些 C 库函数的包装器或替代项,但它不提供 C 库本身。
即使您静态链接 C 库(例如 -lc_nonshared
),您以这种方式获得的库也不会包含可动态加载的符号,您可以通过该符号访问包装函数。这是静态链接的直接结果。
你的库无论如何都不是独立的,因为它试图包装 GLIBC 的 fstat()
实现,而这对它来说是不可用的。
Why is there a relocation error on __libc_start_main
and not on system
?
__libc_start_main
上存在重定位错误,因为该函数是由 glibc 的 libc.so.6 提供的,但不是您的,也不是您的二进制文件本身或动态链接到您的任何其他库二进制。 (见上文(1))
如果 system
或 printf
函数没有重定位错误,那么它们要么不是二进制文件中的外部动态符号,要么被其他库动态满足链接到您的二进制文件。 (更新:链接器调试信息显示前者是这种情况:那些不是外部符号,我指的是没有提供定义的符号;它们是由您的图书馆提供,大概是在 libgcc 中链接的结果。) 细节并不重要,关键是您的策略完全行不通。 考虑使用 LD_PRELOAD
代替。
更新: 另一种方法可能是为您的共享库提供一个构造函数,该函数 dlopen()
是带有标志 RTLD_GLOBAL
的真实 libc.so.6
启用。这样,您的库本身就不必提供完整的 C 库,但它确实需要知道如何找到真正的库。那肯定能解决wrapper中wrapped函数不可用的问题
我试图通过将我的库注入可执行文件来包装 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 namelibc.so.6
.
因此该进程加载您的库 而不是 GLIBC 的 libc.so.6
。 这肯定不是您想要的,除非您提供至少 整个 C 标准库 的 独立 实现。 这需要您的库提供 libc.so.6
中所有内容的 独立 实现,否则动态加载真正的 libc
。我看到您试图通过静态链接 libgcc 来获得完整性(我猜您的意思是使用 -lstatic-libgcc
),但是
那是错误的图书馆。它提供了支持 GCC 编译的二进制文件的函数,其中可能包括一些 C 库函数的包装器或替代项,但它不提供 C 库本身。
即使您静态链接 C 库(例如
-lc_nonshared
),您以这种方式获得的库也不会包含可动态加载的符号,您可以通过该符号访问包装函数。这是静态链接的直接结果。你的库无论如何都不是独立的,因为它试图包装 GLIBC 的
fstat()
实现,而这对它来说是不可用的。
Why is there a relocation error on
__libc_start_main
and not onsystem
?
__libc_start_main
上存在重定位错误,因为该函数是由 glibc 的 libc.so.6 提供的,但不是您的,也不是您的二进制文件本身或动态链接到您的任何其他库二进制。 (见上文(1))
如果 system
或 printf
函数没有重定位错误,那么它们要么不是二进制文件中的外部动态符号,要么被其他库动态满足链接到您的二进制文件。 (更新:链接器调试信息显示前者是这种情况:那些不是外部符号,我指的是没有提供定义的符号;它们是由您的图书馆提供,大概是在 libgcc 中链接的结果。) 细节并不重要,关键是您的策略完全行不通。 考虑使用 LD_PRELOAD
代替。
更新: 另一种方法可能是为您的共享库提供一个构造函数,该函数 dlopen()
是带有标志 RTLD_GLOBAL
的真实 libc.so.6
启用。这样,您的库本身就不必提供完整的 C 库,但它确实需要知道如何找到真正的库。那肯定能解决wrapper中wrapped函数不可用的问题