我从 Stack Overflow 迁移了这个问题;虽然它涉及到编程方面的内容,但我越是尝试了解发生的情况,我就越认为这可能与RPATH
和相关$ORIGIN
,我认为这与 Unix/Linux 相关。 (我的问题在 Stack Overflow 上也没有得到太多关注)我将尽可能多地删除问题中与编程相关的方面(例如 CMakeLists.txt),但如果回复者说它们是相关的。
我创建了一个可执行文件,它从可执行文件所在的子目录加载模块库(或“共享库”或“插件库”,因为它有不同的称呼)。目录结构为:
build/
├── main # the executable
└── plugins
└── libmy-plugin.so
为了与这个问题的编程方面保持一定的距离,我不会显示完整的 C 代码(除非有人说它不会使这个问题偏离本网站的主题)。但它实际上相当于加载模块库的“hello-world”,只不过有一点不同:它从相对路径加载插件库。具体来说,它加载plugins/libmy-plugin.so
.最低限度的代码片段是:
const char* plugin = "plugins/libmy-plugin.so";
void* handle = dlopen(plugin, RTLD_NOW);
手册页dlopen
说:
函数 dlopen() 加载以 null 结尾的字符串文件名命名的动态共享对象(共享库)文件,并返回加载对象的不透明“句柄”...如果文件名为 NULL,则返回的句柄用于主文件程序。如果文件名包含斜杠(“/”),则将其解释为(相对或绝对)路径名。
手册页ld-linux.so
说:
$ORIGIN(或等效的 ${ORIGIN})
这扩展到包含程序或共享对象的目录。因此,位于 somedir/app 中的应用程序可以进行编译,
gcc -Wl,-rpath,'$ORIGIN/../lib'
以便无论 somedir 位于目录层次结构中的哪个位置,它都能在 somedir/lib 中找到关联的共享对象。这有助于创建“交钥匙”应用程序,这些应用程序不需要安装到特殊目录中,而是可以解压缩到任何目录中,并且仍然可以找到它们自己的共享对象。
据我所知如何检查,我编译的二进制文件确实将其RPATH
设置为$ORIGIN
:
$ objdump -x ./build/main | grep PATH
RUNPATH $ORIGIN
$
$ readelf -d ./build/main
Dynamic section at offset 0x2d98 contains 28 entries:
Tag Type Name/Value
0x0000000000000001 (NEEDED) Shared library: [libc.so.6]
0x000000000000001d (RUNPATH) Library runpath: [$ORIGIN]
...
根据我的阅读,由于$ORIGIN
“扩展到包含程序的目录”和“[i]f文件名包含斜杠(“/”),那么它被解释为(相对或绝对)路径名”,这应该意味着程序如果将其RPATH
设置为使用包含斜杠(并且是相对路径)的文件名$ORIGIN
进行调用dlopen()
,则该模块库的搜索路径应该相对于包含程序的目录。
但这不是我观察到的。我实际观察到的是,当我的 CWD 是包含可执行文件的目录时,可执行文件仅找到模块库。如果我在任何其他目录中,并指定可执行文件的完整相对/绝对路径,它不会找到模块库(并且似乎不受设置影响LD_LIBRARY_PATH
):
$ ./build/main
failed loading plugins/libmy-plugin.so: plugins/libmy-plugin.so: cannot open shared object file: No such file or directory
$
$ LD_LIBRARY_PATH=./build ./build/main
failed loading plugins/libmy-plugin.so: plugins/libmy-plugin.so: cannot open shared object file: No such file or directory
$
$ LD_LIBRARY_PATH=/home/user/dev/cmake-learn/rpath/build/ ./build/main
failed loading plugins/libmy-plugin.so: plugins/libmy-plugin.so: cannot open shared object file: No such file or directory
$
$ cd build/
$ ./main
all good
$
我在脑海中反复思考这是一个编程问题还是一个 Unix/Linux 问题,正如我提到的,我有一种非特定的感觉,这与 Unix/Linux 模块搜索路径有关,但如果社区认为这真的是一个编程问题,我会再次在 Stack Overflow 上碰碰运气。
(我在 Stack Overflow 上的原始问题的评论者说搜索路径始终与 CWD 相关,但这不是一个完全解释性的答案,因此我无法将该声明与我对注释手册页部分的阅读相一致)
答案1
对我来说,man dlopen
可以理解为:要么文件名有/
所以不被搜索(它相对于“.”或来自“/”),要么没有,并且在所有路径机制中搜索它。
您可能应该期望必须使用dlopen("libmy-plugin.so",)
并提供-Wl,-rpath,'$ORIGIN/plugins'