linux – 为什么在初始化之前通过LD_PRELOAD加载库?

linux – 为什么在初始化之前通过LD_PRELOAD加载库?,第1张

概述在下面的最小示例中,通过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 在下面的最小示例中,通过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 correctlyfopen_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 %s\n",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 %s\n",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 subdirg_orig_fopen 0x1  fopen file /proc/filesystemsg_orig_fopen 0x1  fopen file /proc/mountssubdirg_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);}
总结

以上是内存溢出为你收集整理的linux – 为什么在初始化之前通过LD_PRELOAD加载库?全部内容,希望文章能够帮你解决linux – 为什么在初始化之前通过LD_PRELOAD加载库?所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/yw/1047618.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-25
下一篇 2022-05-25

发表评论

登录后才能评论

评论列表(0条)

保存