如何规避dlopen()缓存?

时间:2017-08-30 07:51:32

标签: c linker posix dlopen

根据其man pagedlopen()不会加载同一个库两次:

  

如果再次使用dlopen()加载相同的共享对象,则相同   返回对象句柄。动态链接器维护引用   计算对象句柄,因此动态加载的共享对象是   在多次调用dlclose()之前不会释放   因为dlopen()成功了。任何初始化返回(参见   以下)只被召唤一次。但是,后续的dlopen()调用   使用RTLD_NOW加载相同的共享对象可能会强制符号   先前使用RTLD_LAZY加载的共享对象的解析。

(强调我的)。

但实际上是什么决定了共享对象的身份?我试着查看代码,但没有走得太远。是吗:

  • 某种形式的规范化路径名(例如realpath?)
  • the inode?
  • libray的内容?

我很确定我可以排除最后一点,因为实际的文件系统副本会产生两个不同的句柄。

解释这个问题背后的动机:我正在使用一些具有静态全局变量的代码。我需要该代码的多个实例以线程安全的方式运行。我目前的方法是将所述代码编译并链接到动态库中并多次加载该库。通过一些链接器魔术,它似乎创建了全局变量的几个副本,并将每个库中的访问权限解析为它自己的副本。唯一的问题是我的原型复制生成的库n次n次并发使用。这不仅有点丑陋,而且我还怀疑它可能会在不同的平台上破裂。

那么根据POSIX标准,dlopen()的确切行为是什么?

编辑:因为它出现在评论和答案中,所以不能重构代码绝对不是一种选择。这将涉及数月甚至数年的工作,并可能首先牺牲使用代码的所有好处。 存在正在进行的研究项目可能会以更清洁的方式解决这个问题,但它是实际的研究,可能会失败。我现在需要一个解决方案。

edit2:因为人们似乎仍然不相信用例实际上是有效的。我正在研究一种纯函数式语言,它应该嵌入到更大的C / C ++应用程序中。因为我需要一个带有垃圾收集器的原型,一个经验证的类型检查器,以及ASAP的合理性能,我使用OCaml作为中间代码。现在,我正在将一个源模块编译成一个OCaml模块,将生成的目标代码(包括启动等)链接到一个共享库与OCaml运行时和dlopen() strong>共享库。每个.so都有自己的运行时副本,包括几个全局变量(例如指向年轻代的指针),也就是说,应该是完全正常的。该库正好暴露了两个函数:初始化程序和单个导出,它执行原始模块要执行的任何操作。不会导出/共享OCaml运行时的符号。当我加载库时,其内部符号按预期重新定位,我现在唯一的问题是我实际上需要在运行时为每个作业实例复制.so文件。

关于线程局部存储:这实际上是一个有趣的想法,因为对运行时的修改确实相当简单。但问题是OCaml编译器生成的机器代码,因为它不能发出tls符号的加载指令(但是?)。

1 个答案:

答案 0 :(得分:5)

POSIX说:

  

只有一个目标文件的副本被带入地址空间,即使在引用该文件时多次调用dlopen(),并且即使使用不同的路径名来引用该文件。

所以答案是" inode"。复制库文件"应该工作",但硬链接不会。除了。因为它们将暴露相同的全局符号,并且当发生这种情况时,所有(可移植性)投注都是关闭的。您处于弱定义行为的中间,这些行为是通过错误修复而不是良好的设计发展而来的。

当你陷入困境时,不要深入挖掘。添加额外的可怕黑客以使基本上破坏的库工作的方法只会导致额外的破坏。只花几个小时来修复库不使用全局变量而不是花费数天时间来破解动态链接(最好是不可移植的)。