重新加载共享库时,是否再次运行标有__attribute __(构造函数)的函数?

时间:2016-08-27 17:10:03

标签: c gcc clang shared-libraries

假设尚未加载针对共享库libshlib链接的其他可执行文件。并假设libshlib包含一个标有__attribute__(constructor)的函数和一个标有__attribute__(destructor)的函数。当启动与libshlib链接的可执行文件时,将加载libshlib,并且标记为__attribute(constructor)的相应功能将完全运行一次。但是如果可以重新加载共享库,会发生什么?通过用户定义的信号,如SIGUSR1?从我的测试来看,__attribute__(constructor)似乎没有再次运行。这是正确的还是有标准的说法呢?

1 个答案:

答案 0 :(得分:2)

我假设你有一个链接的程序通过(例如):

cc -o mypgm mypgm.o -lshlib

执行后,一旦ELF解释器加载了libshlib.so并执行了构造函数,就永远不会再加载库。 旁注:要找到您的口译员,请执行以下操作:readelf -a mypgm | grep interpreter:

如果程序收到一个信号(例如SIGUSR1),信号将由信号处理程序捕获(假设signalsigaction已被调用以设置一个),或者采取默认操作(SIGUSR1的[IIRC]程序终止)。这 not 导致重新加载库。

还有 no 其他操作可能导致重新加载库。程序退出时只会调用析构函数(例如main返回或调用exit)。

即使手动调用析构函数也没有效果,因为构造函数和析构函数是独立的。 (例如,构造函数可以执行able = malloc(...),析构函数可以执行free(able)。但是,析构函数可以执行free(baker)。)。调用析构函数不会重置"构造函数。

获得"重新加载"效果,库需要通过dlopen/dlsym/dlclose 动态加载/卸载。也就是说,链接命令将是:

cc -o mypgm mypgm.o

然后,mypgm将[在某个时刻]调用dlopen("libshlib.so")(并且将调用构造函数)。当[和 if ] mypgm调用dlclose时,libshlib.so将被卸载(并且析构函数被调用)。

如果mypgm然后调用dlopen("libshlib.so") 第二时间,则再次调用

<强>更新

  

请注意,调用dlclose不会必须卸载库或调用析构函数。

我刚检查了[glibc]中的代码。该库有一个refcount。如果引用dlclose后引用计数为1,则 将被卸载,libshlib.so上面dlopen应该是这种情况[因为没有其他人碰到它起来]。

换句话说,强迫&#34;期望&#34;没有其他行为应通过libshlib引用-lshlib。不是该计划或任何其他.so。这奠定了基础。

请注意,如果libshlib.so需要glibc,但该计划也是如此,卸载libshlib会降低glibc引用计数,但glibc将保留,因为它的引用计数[仍然]> 0。

  

在某些情况下,库无法卸载(实际上,这些条件比可以卸载库时的条件更常见)。

同样,这取决于引用计数和[可能]某些状态。从静态&#34;加载库时链接(与dlopen对比),引用计数获得额外的增量,因此不会被拉扯。

该代码还处理构造函数在其自己的库上调用dlopen的情况。

对于给定的libA,如果需要libB,则B&#39}的引用计数会被A的加载/卸载升级/降级。

  

如果未卸载库,则无法定义析构函数是否运行,以及后续dlopen是否将再次运行构造函数

dlopen以这种方式使用libshlib的重点是保证 dlopen的加载和dlclose的卸载[以及构造函数/析构函数]。如果没有静态引用或循环依赖,这为真,这是起始标准。

更新#2:

  

部分关于&#34;因为没有其他人碰到它&#34;太简单了。

不要将散文与实质内容混为一谈。

如上所述:如果没有对它的静态引用或循环依赖,则为真。

这意味着执行dlopen/dlclose shlib shlib的可执行文件/对象是dlsym中的符号。

而且,这只是通过shlib 。否则,它是一个静态引用(即在对象的符号表中作为UNDEF)]。

而且,shlib拖入的 no 共享库是指DF_1_NODELETE [循环依赖]中定义的符号。

  

查看符号解析期间设置DF_1_NODELETE的所有位置。

是的,我看了

dlopen仅在 中设置以下位置。它们都不适用于这种情况[或大多数dlopen场景]。

  1. 如果RTLD_NODELETE标记参数有dlopen,我们可以避免。
  2. 如果启用了性能分析,配置文件地图[与我们的DF_1_NODELETE]相关,则会设置LOCAL
  3. 如果符号具有类型10(GLOBAL
  4. 的绑定类型(例如WEAKSTB_GNU_UNIQUEmalloc等)
  5. 非动态对象将符号引用为UNDEF [在其符号表中],不能将其删除[因为引用对象设置了DF_1_NODELETE]。由于上述前提条件,这不适用。
  6. 从_different对象添加依赖项时出现dlopen/dlclose失败。此代码甚至不执行此处的情况。
  7. 而且,除了OP的用法之外,{{1}}有正当理由可以像我设置/描述的那样工作。

    请在此处查看我的回答:Is it possible to perform munmap based on information in /proc/self/maps?

    在那里,OP需要一个可以运行数月/年的不间断程序(例如,高可靠性,任务关键型应用程序)。如果[通过包管理器等]安装了其中一个共享库的更新版本,则程序必须动态地,动态地,而不需要重新执行,能够加载新版本。