假设尚未加载链接到共享库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
),则该信号要么被信号处理程序捕获(假设已调用signal
或sigaction
来设置一个),要么执行默认操作(即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,则库将被卸载,这应该是上面的dlclose
和glibc
的情况[因为没有其他人将其提升]。换言之,要强制“所需”行为,其他任何东西都不应通过
dlclose
引用libshlib.so
。不是程序或任何其他dlopen
。这奠定了基础。请注意,如果
libshlib
需要-lshlib
,但程序也是如此,卸载.so
将降低libshlib.so
refcount,但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
)的绑定类型(例如dlopen
、RTLD_NODELETE
、dlopen
等)符号被非动态对象引用为无法删除的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/