linux – 为什么在初始化之前通过LD_PRELOAD加载库?
发布时间:2020-05-27 19:30:46 所属栏目:Linux 来源:互联网
导读:在下面的最小示例中,通过LD_PRELOAD加载的库以及拦截fopen和openat的函数显然在初始化之前运行. ( Linux是CentOS 7.3).为什么?? 库文件comm.c: #define _GNU_SOURCE#include dlfcn.h#include stdarg.h#include stdio.h#include fcntl.htypedef
|
在下面的最小示例中,通过LD_PRELOAD加载的库以及拦截fopen和openat的函数显然在初始化之前运行. ( Linux是CentOS 7.3).为什么?? 库文件comm.c: #define _GNU_SOURCE
#include <dlfcn.h>
#include <stdarg.h>
#include <stdio.h>
#include <fcntl.h>
typedef FILE *(*fopen_type)(const char *,const char *);
// initialize to invalid value (non-NULL)
// init() should initialize this correctly
fopen_type g_orig_fopen = (fopen_type) 1;
typedef int (*openat_type)(int,const char *,int,...);
openat_type g_orig_openat;
void init() {
g_orig_fopen = (fopen_type)dlsym(RTLD_NEXT,"fopen");
g_orig_openat = (openat_type)dlsym(RTLD_NEXT,"openat");
}
FILE *fopen(const char *filename,const char *mode) {
// have to do this here because init is not called yet???
FILE * const ret = ((fopen_type)dlsym(RTLD_NEXT,"fopen"))(filename,mode);
printf("g_orig_fopen %p fopen file %sn",g_orig_fopen,filename);
return ret;
}
int openat(int dirfd,const char* pathname,int flags,...) {
int fd;
va_list ap;
printf("g_orig_fopen %p openat file %sn",pathname);
if (flags & (O_CREAT)) {
va_start(ap,flags);
fd = g_orig_openat(dirfd,pathname,flags,va_arg(ap,mode_t));
}
else
fd = g_orig_openat(dirfd,flags);
return fd;
}
编译: gcc -shared -fPIC -Wl,-init,init -ldl comm.c -o comm.so 我有一个空的子目录subdir.然后看起来在init之前调用库函数fopen: #LD_PRELOAD=./comm.so find subdir g_orig_fopen 0x1 fopen file /proc/filesystems g_orig_fopen 0x1 fopen file /proc/mounts subdir g_orig_fopen 0x7f7b2e574620 openat file subdir 解决方法显然,在comm.so初始化之前调用fopen.有趣的是在fopen()中放置一个断点以便理解(检查 this link以获得各种包的调试符号).我得到了这个回溯:(gdb) bt #0 fopen (filename=0x7ffff79cd2e7 "/proc/filesystems",mode=0x7ffff79cd159 "r") at comm.c:28 #1 0x00007ffff79bdb0e in selinuxfs_exists_internal () at init.c:64 #2 0x00007ffff79b5d98 in init_selinuxmnt () at init.c:99 #3 init_lib () at init.c:154 #4 0x00007ffff7de88aa in call_init (l=<optimized out>,argc=argc@entry=1,argv=argv@entry=0x7fffffffdf58,env=env@entry=0x7fffffffdf68) at dl-init.c:72 #5 0x00007ffff7de89bb in call_init (env=0x7fffffffdf68,argv=0x7fffffffdf58,argc=1,l=<optimized out>) at dl-init.c:30 #6 _dl_init (main_map=0x7ffff7ffe170,env=0x7fffffffdf68) at dl-init.c:120 #7 0x00007ffff7dd9c5a in _dl_start_user () from /lib64/ld-linux-x86-64.so.2 #8 0x0000000000000001 in ?? () #9 0x00007fffffffe337 in ?? () #10 0x0000000000000000 in ?? () 很明显,comm.so依赖于其他库(libdl.so需要libselinux.so).并且comm.so不是唯一声明init函数的库. libdl.so和libselinux.so也声明了一些. 因此,comm.so是第一个要加载的库(因为它是用LD_PRELOAD声明的)但是,comm.so依赖于libdl.so(因为在编译期间是-ldl)而libdl.so依赖于libselinux.so.因此,为了加载comm.so,之前调用libdl.so和libselinux.so的init函数.最后,来自libselinux.so的init函数调用fopen() 就个人而言,我通常在第一次调用符号时解析动态符号.像这样: FILE *fopen(const char *filename,const char *mode) {
static FILE *(*real_fopen)(const char *filename,const char *mode) = NULL;
if (!real_fopen)
real_fopen = dlsym(RTLD_NEXT,"fopen");
return real_fopen(filename,mode);
} (编辑:安卓应用网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
推荐文章
站长推荐
热点阅读
