Linux共享库被重复加载

3
我有以下设置:
- 一个相当复杂的基于Qt+QML的应用程序 - 一个共享的Linux库,也具有一些Qt功能
通过LD_PRELOAD技巧注入共享库到应用程序中后,它将启动一个TCP服务器,通过该服务器公开应用程序内部对象。目标是访问应用程序内部而不特定修改应用程序源代码。
我看到的奇怪现象是,共享库被加载两次,我无法理解原因。由于库和应用程序都依赖于Qt,如果Linux加载多个相同的Qt库,则可以理解。
但应用程序不依赖于共享库,我也没有覆盖应用程序本身的任何函数。
我想知道:
- 如何防止共享库被加载两次(我正在考虑使用shell环境变量,但这似乎是一个丑陋的hack) - 可能会导致共享库被加载两次的原因是什么
编辑:
Employed-Russian的评论引导了我解决第二个问题。目标应用程序正在启动子进程,该进程继承环境变量,包括LD_PRELOAD。子进程是导致库被加载两次的原因。
至于问题#1,我也遵循他的建议:库初始化函数简单地取消设置环境变量LD_PRELOAD。结果,子进程不再重新加载库。

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

4

共享库为什么会被加载两次?

加载器非常努力地避免重复加载同一共享库。

很可能您的代码链接到了两个不同的共享库,这才是导致混乱的原因。

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


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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接