Linux 共享库被加载两次

Posted

技术标签:

【中文标题】Linux 共享库被加载两次【英文标题】:Linux shared library is being loaded twice 【发布时间】:2016-06-28 20:32:23 【问题描述】:

我有以下设置:

一个相当复杂的基于 Qt+QML 的应用程序 一个共享的 linux 库,也有一些 Qt 功能

使用 LD_PRELOAD 技巧将共享库注入应用程序。一旦加载,它就会启动一个 TCP 服务器,通过它公开应用程序内部对象。目标是在不专门修改应用程序源代码的情况下访问应用程序内部。

我看到的奇怪的事情是共享库被加载了两次,我不明白为什么。由于库和应用程序都依赖于 Qt,我会理解 Linux 是否会加载相同 Qt 库的多个副本。

但该应用程序不依赖于共享库,并且我没有覆盖应用程序本身的任何函数。

我想知道:

    如何防止共享库被加载两次(我正在考虑使用 shell 环境变量,但它似乎是一个丑陋的 hack)

    什么可能导致共享库被加载两次

编辑

来自 Employed-Russian 的 cmets(见下文)引导我朝着正确的方向解决问题 #2。目标应用程序正在启动一个继承环境变量的子进程,包括 LD_PRELOAD。子进程是库被加载两次的原因。

至于问题#1,我也听从了他的建议:库初始化函数简单地取消设置环境变量LD_PRELOAD。因此,子进程不再重新加载库。

【问题讨论】:

你怎么知道库被“加载”了两次?仅仅因为库中的“启动”代码可能运行两次并不意味着目标地址空间中必须有两个库副本。可能有,但不一定是这样。 确实我的启动代码运行了两次,这就是为什么我认为库被加载了两次。在这种特殊情况下,它似乎很奇怪,但我不知道启动函数会执行两次的任何其他原因。该库是用 GCC 编译的,我使用的是构造函数属性。 如果库被加载两次,那么它不会被加载到同一个地址。您可以检测到:将构造函数的地址转储到控制台。第一次和第二次加载应该是不同的。如果它们相等,那么可能您的库已加载,然后卸载,然后再次加载 - 这是完全有效的,您应该优雅地处理。 库被加载了两次。我通过转储库构造函数和析构函数的地址来确认这一点。但是我仍然不明白为什么会发生这种情况,因为注入了 LD_PRELOAD 的库。我认为可以,因为目标应用程序正在分叉一个新进程,该进程将继承父级的环境。但是对应用程序进行跟踪不会显示任何分叉系统调用。 【参考方案1】:

什么可能导致共享库被加载两次

加载器竭尽全力加载同一个共享库两次。

您的代码极有可能链接到两个独立共享库中,而正是造成所有混乱的原因。

设置LD_DEBUG=libs,files 应该清楚地显示从哪些路径加载了哪些库。

【讨论】:

@Employed_Russian,应用程序链接到 Qt 库,并且注入到应用程序中的共享库链接到 Qt 库。你是这个意思吗 ?如果是这样,我希望只加载 Qt 库而不是注入的库本身。 @ngoncalves 不,我不是这个意思。请再次阅读答案。 @Employed_Russian,共享库和应用程序之间没有公共代码并且两者的构建过程是完全独立的。但是调试显示了原因:应用程序正在启动一个进程,然后加载共享库。我想是因为进程继承了父环境变量 @ngoncalves 是的,LD_PRELOAD 被子进程继承。您可以在创建孩子之前从应用程序中取消设置。 我不能。使用 LD_PRELOAD 的目的是允许我检查应用程序(用于测试目的),而无需实际更改应用程序源代码。在我添加了多次加载库的保护措施后,一切都按预期工作。

以上是关于Linux 共享库被加载两次的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Linux 的 QTCreator 中使用 dlopen 打开的共享库中设置断点

项目文件夹中的共享库

Linux:为啥加载器会找到我的共享库?

Linux共享库两种加载方式简述

如何在 GCC Linux 中指定非默认共享库路径?运行时出现“加载共享库时出错”

Linux之静态库共享库