【问题标题】:Force dynamic linker to load library at runtime in Linux在 Linux 中强制动态链接器在运行时加载库
【发布时间】:2018-09-04 13:03:58
【问题描述】:

所以有点历史,我有 3 个库:

  • “lib1.so”没有依赖项
  • 与“lib1.so”链接的“lib2.so”
  • “test”可执行程序,无依赖关系

我需要的是在运行时通过“dlopen”方法从“test”可执行文件动态加载“lib2.so”。问题是“lib1.so”无法自动加载,因为链接器不知道在哪里找到它。

我一开始就尝试像这样加载“lib1.so”:

void* ptr_lib1 = dlopen("/usr/local/test/lib1.so", RTLD_NOW | RTLD_GLOBAL);
void* ptr_lib2 = dlopen("/usr/local/test/lib2.so", RTLD_NOW | RTLD_GLOBAL);

但这由于某种原因不起作用,它在调用第二个 dlopen 后给了我错误(来自 dlerror 的文本):

"lib1.so: cannot open shared object file: No such file or directory"

这意味着“lib1.so”正在尝试加载两次:第一次为“lib1.so”调用 dlopen,第二次为“lib2.so”调用 dlopen。

谁能解释为什么以及如何解决这个问题?请不要建议在启动可执行文件之前修改 LD_LIBRARY_PATH。也不建议修改可执行文件的链接器标志。我需要能够在运行时从任何文件夹加载该库。

更新。 经过一些研究,我这样做了:

LD_DEBUG=all ./test

发现库 lib1.so 尝试加载两次(第一次是我调用 dlopen 时,第二次是 LD 尝试解析 lib2.so 的依赖项时):

add /usr/local/test/lib1.so [0] to global scope
opening file=/usr/local/test/lib1.so [0]; direct_opencount=1
...
    30031:  file=lib1.so [0];  needed by /usr/local/test/lib2.so [0]
 30031: find library=lib1.so [0]; searching
 30031:  search path=./tls/x86_64:./tls:./x86_64:.      (RPATH from file ./test)
 30031:   trying file=./tls/x86_64/lib1.so
 30031:   trying file=./tls/lib1.so
 30031:   trying file=./x86_64/lib1.so
 30031:   trying file=./lib1.so
 30031:  search cache=/etc/ld.so.cache
 30031:  search path=/lib64/tls/x86_64:/lib64/tls:/lib64/x86_64:/lib64:/usr/lib64/tls/x86_64:/usr/lib64/tls:/usr/lib64/x86_64:/usr/lib64        (system search path)
 30031:   trying file=/lib64/tls/x86_64/lib1.so
 30031:   trying file=/lib64/tls/lib1.so
 30031:   trying file=/lib64/x86_64/lib1.so
 30031:   trying file=/lib64/lib1.so
 30031:   trying file=/usr/lib64/tls/x86_64/lib1.so
 30031:   trying file=/usr/lib64/tls/lib1.so
 30031:   trying file=/usr/lib64/x86_64/lib1.so
 30031:   trying file=/usr/lib64/lib1.so
 30031:
 30031:
 30031: file=/usr/local/test/lib2.so [0];  destroying link map 

还是不明白怎么回事……

UPDATE2.已满LD_DEBUG log

注意:还有一个库 lib_other.so,忽略它

【问题讨论】:

  • “这不起作用” - 当你尝试时会发生什么? lib1 和 lib2 是否总是在同一目录中?
  • @JohnZwinck,我更新了问题
  • 你能用LD_DEBUG=all跑吗?
  • @yugr 查看更新,lib2.so 加载了两次。怎么样?
  • @AlekDepler 请为首次加载lib1.so 添加日志(在opening file=/usr/local/test/lib1.so [0] 之后)。我们需要了解为什么链接器无法从/usr/local/test 加载它,这是您问题的根本原因。

标签: linux ld dlopen


【解决方案1】:

dlopen(3) 失败时,您应该使用dlerror(3) 来获取一些有用的消息。

所以至少要编码:

void* ptr_lib1 
   = dlopen("/usr/local/test/lib1.so", RTLD_NOW | RTLD_GLOBAL);
if (!ptr_lib1) {
   fprintf(stderr, "dlopen lib1 failure: %s\n", dlerror());
   exit(EXIT_FAILURE);
}

对于其他dlopens 也是如此

注意dlerror 正在给出一个详细的 错误消息。如果您不明白,请在您的问题中提出。

您可能在链接lib1.solib2.so 时忘记设置一些rpath。或者你需要显式设置LD_LIBRARY_PATH,或者运行ldconfig

阅读 Drepper 的 How To Write Shared Libraries 论文了解详情。仔细阅读ld-linux(8)

lib2dlopen 失败,因为找不到它的依赖关系lib1。你需要解决这个问题(通过 rpath、LD_LIBRARY_PATHldconfig 等...)

您可以在lib1 选项中添加一些-Wl,-rpath,/usr/local/test/lib1.so

您还可以(更简单地)决定将所有共享库放入/usr/local/lib/,您将在/etc/ld.so.conf 中提及。然后你需要在每次添加库后运行ldconfig

【讨论】:

  • 我不想使用 rpath 或 LD_LIBRARY_PATH,我需要从任何文件夹加载库的方法,没有限制和环境变量
  • 你可能无法做到这一点(但我不确定你真正想要做什么;我猜你对共享库的理解不够好;阅读 Drepper 的论文应该会有所帮助)。
  • “你应该使用 dlerror(3) 来获取一些有用的信息”——这不是“没有这样的文件或目录”信息吗?
【解决方案2】:

这个问题的根本原因似乎是lib1.soNEEDED by lib2.so,这是因为您在构建时链接了它。所以根本不要在构建时链接它——在构建lib2 时根本不要提及lib1。然后你可以随心所欲地用dlopen() 加载它,lib2 不会尝试独立加载它。

【讨论】:

  • 这个问题的根源是Linux架构,在windows下即使链接库也不存在这样的问题
  • @AlekDepler:你是对的。如果您愿意,您应该考虑改用 Windows。
【解决方案3】:

我有类似的问题,但它只是工作。我只是使用“RTLD_LAZY”而不是“RTLD_NOW”。

你可以试试。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-04-03
    • 2016-12-07
    • 2011-01-04
    • 1970-01-01
    • 2023-01-18
    • 2016-11-28
    • 2011-04-11
    相关资源
    最近更新 更多