Linux 共享库LD_PRELOAD环境变量

共享库转载器有许多可供配置的环境变量,比如我们前面介绍的LD_LIBRARY_PATH环境变量。本文只重点介绍LD_PRELOAD环境变量,因为这个环境变量体现了共享库一个非常重要的特性:共享库覆盖。

下面是man手册中对于LD_PRELOAD环境变量的介绍:

              A  whitespace-separated  list  of additional, user-specified, ELF shared libraries to be loaded before all others.
              This can be used to selectively override functions in other shared libraries.   For  set-user-ID/set-group-ID  ELF
              binaries, only libraries in the standard search directories that are also set-user-ID will be loaded.

我们以一个覆盖标准库sleep函数的例子来说明如何应用LD_PRELOAD环境变量。

首先给出我们的测试程序,程序很简单,休眠1秒钟退出。

#include <unistd.h>

int main()
{
    return sleep (1);
}

使用如下命令编译

gcc -o test  test.c

使用ldd查看可执行文件test依赖的共享库:

$ ldd test 
        linux-gate.so.1 =>  (0x003c5000)
        libc.so.6 => /lib/libc.so.6 (0x4e8b2000)
        /lib/ld-linux.so.2 (0x4e88f000)


在未设置LD_PRELAOD环境变量时,程序运行将调用标准库的sleep函数。下面我们将定义自己的sleep函数,把这个函数编译到一个库,并使用这个库覆盖标准库的sleep函数。

首先给出定义sleep函数的源文件mysleep.c:

#define _GNU_SOURCE 
#include <dlfcn.h>
#include <stdio.h>

unsigned int sleep(unsigned int milliseconds)
{
    fprintf (stderr, "my sleep () called\n");
    
    static unsigned int (*funcptr) (unsigned int) = NULL;
    if (!funcptr)
        funcptr = (unsigned int (*) (unsigned int)) dlsym (RTLD_NEXT, "sleep");
    if (!funcptr) {
        fprintf (stderr, "dlsym Error:%s\n", dlerror ());
        return -1;
    }
    unsigned int seconds = milliseconds/1000;
    if (seconds%1000>=500)
        seconds++;
    if (!seconds)
        seconds = 1;
    return (*funcptr) (seconds);
}

我们的sleep函数接受一个以毫秒为单位的参数,函数首先会调用fprint输出一行调试信息,以帮助我们了解sleep函数是否覆盖成功。然后我们使用dlsym函数获取标准库中sleep的指针,并使用标准库的sleep来实现我们自定义的sleep。需要强调的是,如果我们想要覆盖标准库的某个函数,我们自定义的函数,必须和被覆盖的函数声明相一致。

使用以下makefile文件编译库libmysleep.so.1:

CFLAGS=-Wall 
LIBCFLAGS= $(CFLAGS) -fPIC
CC=gcc
LIBOBJS=mysleep.o
AR=ar rc
LIBRARY=libmysleep.so.1.0.0
SONAME=libmysleep.so.1


$(LIBRARY):$(LIBOBJS)
        $(CC) -shared -Wl,-soname,$(SONAME) -o $@ $(LIBOBJS) -ldl
        ln -sf $@ libmysleep.so
        ln -sf $@ $(SONAME)

%.o:%.cpp
        $(CC) $(LIBCFLAGS) -c -o $@ $< 

clean:
        rm -rf $(LIBRARY) $(LIBOBJS) libmysleep.so* main

使用下面的命令添加库目录到共享库装载程序的搜索目录:

export LD_LIBRARY_PATH=`pwd`

把我们自己的库libmysleep.so.1添加到LD_PRELOAD环境变量的覆盖库列表中:

export LD_PRELOAD=libmysleep.so.1

然后使用ldd查看可执行文件test依赖的共享库:
$ ldd test 
        linux-gate.so.1 =>  (0x001e3000)
        libmysleep.so.1 => /home/wayz11/tem/lib_test/preload/libmysleep.so.1 (0x0089f000)
        libc.so.6 => /lib/libc.so.6 (0x4e8b2000)
        libdl.so.2 => /lib/libdl.so.2 (0x4ea5f000)
        /lib/ld-linux.so.2 (0x4e88f000)
前后对比可发现比没有设置LD_PRELOAD环境变量时多了两个共享库依赖,其中一个是我们自己的覆盖库libmysleep.so.1,另一个是libmysleep.so.1库的依赖库libdl.so.2,因为我们调用了dlsym函数。

好了,我们已经成功完成了标准库sleep函数的覆盖,运行程序输出如下:

$ ./test
my sleep () called

最后一步也很重要,请删除LD_PRELOAD环境变量,除非你想在当前Session中让自己的库一直覆盖下去,也许你只是在测试:

$ unset LD_PRELOAD


LD_PRELOAD是一个环境变量,用于在程序运行时指定要预加载的共享。它的工作原理是,当程序需要调用某个符号时,系统会先在程序自身的符号表中查找,如果找不到,则会在LD_PRELOAD指定的共享中查找。如果在LD_PRELOAD指定的共享中找到了该符号,则使用该符号中的代码。\[2\] 然而,有时候我们可能需要取消LD_PRELOAD的影响。一种常见的方法是在新进程启动前删除LD_PRELOAD环境变量,可以通过调用unsetenv("LD_PRELOAD")来实现。这样可以避免陷入无限循环的情况。\[1\] 另外,还有两种方法可以让LD_PRELOAD失效。一种是通过静态链接,使用gcc的-static参数可以将libc.so.6静态链接到执行程序中,但这也意味着程序不再支持动态链接。另一种方法是设置执行文件的setgid/setuid标志,对于具有SUID权限的执行文件,系统会忽略LD_PRELOAD环境变量。因此,如果你有以root方式运行的程序,最好设置上SUID权限。\[3\] #### 引用[.reference_title] - *1* [深入分析 LD_PRELOAD](https://blog.csdn.net/itworld123/article/details/125755603)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [LD_PRELOAD的使用](https://blog.csdn.net/Long_xu/article/details/128897509)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [linux LD_PRELOAD 预加载 so 简介](https://blog.csdn.net/whatday/article/details/108890018)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值