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

最佳答案

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

cc -o mypgm mypgm.o -lshlib

执行时,一旦ELF解释器加载了libshlib.so并执行了构造函数,就再也不会加载库。旁注:要找到您的口译员,请执行:readelf -a mypgm | grep interpreter:
如果程序接收到一个信号(例如SIGUSR1),则该信号要么被信号处理程序捕获(假设已调用signalsigaction来设置一个),要么执行默认操作(即SIGUSR1的[IIRC]程序终止)。这不会导致重新加载库。
没有其他操作可以导致库重新加载。只有在程序退出时调用析构函数(例如,或main)才被调用。
即使手动调用析构函数也没有效果,因为构造函数和析构函数是独立的。(例如,构造函数可以做exit,析构函数可以做able = malloc(...)。但是,析构函数可以代替。调用析构函数不会“重置”构造函数。
要获得“重新加载”效果,需要通过free(able)动态加载/卸载库。也就是说,link命令将是:
cc -o mypgm mypgm.o

然后,free(baker)将[在某个时刻]调用dlopen/dlsym/dlclose(并且将调用构造函数)。当[且如果]mypgm调用dlopen("libshlib.so")时,mypgm将被卸载(并调用析构函数)。
如果dlclose然后第二次调用libshlib.so,则将[再次]调用这些构造函数。
更新:
请注意,调用mypgm不一定卸载库或调用析构函数。
我刚刚检查了代码。库中有一个refcount。如果refcount在进入dlopen("libshlib.so")时为1,则库将被卸载,这应该是上面的dlcloseglibc的情况[因为没有其他人将其提升]。
换言之,要强制“所需”行为,其他任何东西都不应通过dlclose引用libshlib.so。不是程序或任何其他dlopen。这奠定了基础。
请注意,如果libshlib需要-lshlib,但程序也是如此,卸载.so将降低libshlib.sorefcount,但glibc将保留,因为其refcount为[静止]>0。
有些条件无法卸载库(事实上,这些条件比卸载库时的条件更常见)。
同样,这取决于refcount和[可能]某个状态。当从“静态”链接加载库时(相对于libshlib),refcount得到一个额外的增量,因此不会被拖拽。
代码还处理构造函数在其自身库上调用glibc的情况。
对于给定的glibc,如果它需要dlopen,B的refcount将由a的加载/卸载来增加/减少。
如果库没有被卸载,那么析构函数是否会运行,以及后续的dlopen是否会再次运行构造函数还没有很好的定义
这样使用dlopen是为了保证在libA时加载和在libB时卸载(以及构造函数/析构函数操作)。如果不存在对它的静态引用或循环依赖项(这是开始条件),则会出现这种情况。
更新2:
关于“没有人会把它搞砸”的部分过于简单化了。
不要把散文和实体混为一谈。
如上所述:如果没有对它的静态引用或循环依赖项,那么这将是正确的。
这意味着只有为dlopen执行libshlib的可执行文件/对象引用dlopen中的符号。
而且,这只是通过dlclose。否则,它是一个静态引用(即在对象的符号表中为UNDEF)。
而且,dlopen/dlclose引入的共享库没有引用在shlib[循环依赖项]中定义的符号。
查看在符号解析期间设置DF_1_nodelite的所有位置。
是的,我确实看了。
shlib仅在以下位置设置。它们都不适用于这种情况[或大多数情况]。
如果dlsym的flags参数有shlib,我们可以避免。
如果启用配置文件,配置文件映射[与我们的shlib]无关]将得到DF_1_NODELETE
如果符号具有类型10(dlopen)的绑定类型(例如dlopenRTLD_NODELETEdlopen等)
符号被非动态对象引用为无法删除的UNDEF[在其符号表中][因为引用对象已设置DF_1_nodelite]。由于上述先决条件,这不适用。
添加依赖项[来自不同对象]时出现DF_1_NODELETE失败。这段代码甚至不适用于这里的情况。
而且,撇开OP的用法不谈,LOCAL按照我的设置/描述工作是有正当理由的。
请看我的答案:Is it possible to perform munmap based on information in /proc/self/maps?
在那里,OP需要有一个可以运行数月/数年的不间断程序(例如高可靠性、任务关键型应用程序)。如果[通过包管理器等]安装了某个共享库的更新版本,则程序必须动态地、动态地、无需重新执行即可加载更新版本。

关于c - 重新加载共享库时,带有__attribute __(constructor)标记的函数是否再次运行?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/39183705/

10-12 15:04
查看更多