首页 > 代码库 > 动态链接库的RPATH和RUNPATH解惑

动态链接库的RPATH和RUNPATH解惑

最近在使用SPP网络框架进行业务开发的时候,遭遇动态库搜索路径问题,为了避免其他同学继续在这方面浪费精力,简单整理了相关知识点。

【背景知识】

这里不打算深入链接器内部,简明扼要给出链接器进行动态库搜索的一些规则。

技术分享

以上是man ld结果部分截图,这里着重澄清两个基本概念:"link time"(链接时)和"runtime"(运行时),比如常用的-L选项属于链接时,而-rpath,LD_LIBRARY_PATH都属于运行时。

很多运维同学倾向于使用LD_LIBRARY_PATH,但是个人认为这并不是一种好方法,因为LD_LIBRARY_PATH作用于全局,可能对其他程序造成未预期的影响。有兴趣的同学可以读一读《Why LD_LIBRARY_PATH is bad》,这篇文章有比较全面的分析。

【实践检验】

目录文件:

技术分享

// main.c
#include <dlfcn.h>

typedef void (*FuncA)(void);

int main(int argc, char *argv[])
{
    void *plugin = dlopen("./libfunc_a.so", RTLD_LAZY);
    FuncA func = (FuncA)dlsym(plugin, "FuncA");
    func();
    return 0;
}
// func_a.c
#include <stdio.h>

void FuncB(void);

void FuncA(void)
{
    printf("[%s]%s:%d\n", __FUNCTION__, __FILE__, __LINE__);
    FuncB();
}
// func_b_sys.c
// func_b_user.c
#include <stdio.h>

void FuncB(void)
{
    printf("[%s]%s:%d\n", __FUNCTION__, __FILE__, __LINE__);
}
# Makefile
all:
    gcc -g -Wall -fPIC -shared -o libfunc_b_sys.so func_b_sys.c
    gcc -g -Wall -fPIC -shared -o libfunc_b_user.so func_b_user.c
    ln -sf /data/warezhou/test/ld/libfunc_b_sys.so /usr/lib64/libfunc_b.so
    ln -sf /data/warezhou/test/ld/libfunc_b_user.so ./lib/libfunc_b.so
    gcc -g -Wall -fPIC -shared -o libfunc_a.so func_a.c -lfunc_b
    gcc -g -Wall -o main main.c -ldl
    gcc -g -Wall -o main_rpath main.c -ldl -Wl,--rpath=./lib,--disable-new-dtags
    gcc -g -Wall -o main_rpath_runpath main.c -ldl -Wl,--rpath=./lib,--enable-new-dtags

clean:
    rm main main_rpath main_rpath_runpath
    rm -rf *.so
    rm -rf /usr/lib64/libfunc_b.so
    rm -rf ./lib/*

 实验结果:

技术分享

技术分享

技术分享

技术分享

【原因分析】

技术分享

上图关于动态库搜索路径的总结来自qt工程师的blog,有兴趣的同学可以阅读原文。简单来说,当可执行文件同时设置RPATH和RUNPATH时,两者同时失效。

查看SPP源码后,发现Makefile里面没有显式指定disable-new-dtags,而这个选项的默认值是平台相关的,我们公司老的suse默认关闭,而新的tlinux默认开启。所以导致发行包里的可执行文件同时设置了RPATH和RUNPATH,最终导致业务模块.so依赖的第三方.so查找不到。

当然,上述问题,通过设置libfunc_a.so的RPATH也是可以解决问题的,有兴趣的同学可以试一试,因为整个搜索过程是递归向上执行的。

动态链接库的RPATH和RUNPATH解惑