重新加载共享库时,函数是否再次标记为 __attribute__(constructor) 运行?
Are functions marked with __attribute__(constructor) run again when a shared library is reloaded?
假设没有其他链接到共享库 libshlib
的可执行文件已经加载。并假设 libshlib
包含一个标记为 __attribute__(constructor)
的函数和一个标记为 __attribute__(destructor)
的函数。当链接到 libshlib
的可执行文件启动时,将加载 libshlib
并且标有 __attribute(constructor)
的对应函数是 运行 exactly一次。但是如果可以重新加载共享库会发生什么,例如通过用户定义的信号,例如 SIGUSR1
?根据我的测试,__attribute__(constructor)
似乎不再是 运行。这是正确的还是有标准的说法?
我假设您有一个程序 linked 通过(例如):
cc -o mypgm mypgm.o -lshlib
执行时,一旦 ELF 解释器加载 libshlib.so
并执行了构造函数,库就不会再次加载。 旁注:要找到您的口译员,请执行以下操作:readelf -a mypgm | grep interpreter:
如果程序接收到信号(例如SIGUSR1
),信号要么被信号处理程序捕获(假设signal
或sigaction
已被调用以设置一个) ,或者采取默认操作(即 SIGUSR1
的 [IIRC] 程序终止)。这 不会 导致重新加载库。
没有 其他操作可以导致重新加载库。程序退出时只会调用析构函数(例如调用 main
returns 或 exit
)。
甚至手动调用析构函数也没有效果,因为构造函数和析构函数是独立的。 (例如,构造函数可以执行 able = malloc(...)
,而析构函数可以执行 free(able)
。但是,析构函数可以执行 free(baker)
。)调用析构函数不会 "reset" 构造函数。
要获得 "reload" 效果,库需要 动态 loaded/unloaded 通过 dlopen/dlsym/dlclose
。也就是说,link 命令将是:
cc -o mypgm mypgm.o
然后,mypgm
会 [在某个时候] 调用 dlopen("libshlib.so")
(并且会调用构造函数)。当 [and if] mypgm
调用 dlclose
时,libshlib.so
将被卸载(并调用析构函数)。
如果 mypgm
然后调用 dlopen("libshlib.so")
秒 次,构造函数 将 [再次] .
更新:
Note that calling dlclose
does not necessarily unload the library or call destructors.
我刚刚检查了 [在 glibc
] 中的代码。图书馆有refcount。如果在进入 dlclose
时引用计数为 1,则库 将被卸载 ,上面的 libshlib.so
和 dlopen
应该是这种情况 [因为没有人否则会增加它。
换句话说,要强制执行 "desired" 行为,其他任何东西都不应通过 -lshlib
引用 libshlib
。不是程序或任何其他 .so
。这就奠定了基础。
请注意,如果 libshlib.so
想要 glibc
,但程序也是如此,卸载 libshlib
会降低 glibc
引用计数,但 glibc
会保持不变,因为它的引用计数是 [still] >0.
There are conditions where the library can't be unloaded (in fact, these conditions are much more common then conditions when the library can be unloaded).
同样,这取决于引用计数和[可能]某些状态。当库从 "static" linkage 加载时(与 dlopen
相比),refcount 会额外增加,因此不会被抽取。
该代码还处理构造函数在其 自己的 库中调用 dlopen
的情况。
对于给定的 libA
,如果它需要 libB
,B 的引用计数将由 A 的 load/unload.
得到 upped/downed
If the library is not unloaded, then it's not well defined whether destructors will run, and whether the subsequent dlopen will run constructors again
libshlib
以这种方式使用 dlopen
的全部意义在于 保证 在 dlopen
加载并在 [=27] 卸载=] [连同 constructor/destructor 操作]。如果没有对它的静态引用或循环依赖,这 将 为真,这是起始条件。
更新#2:
The part about "as nobody else bumps it up" is way too simplistic.
不要混淆散文和实质内容。
如上所述:如果没有对它的静态引用或循环依赖,将为真。
这意味着只有为shlib
做dlopen/dlclose
的executable/object引用了shlib
中的一个符号。
而且,这只是 通过 dlsym
。否则,它是静态引用(即在对象的符号 table 中作为 UNDEF)]。
并且,没有shlib
拖入的共享库是指shlib
[循环依赖]中定义的符号。
Look at all the places where DF_1_NODELETE is set during symbol resolution.
是的,我看了。
DF_1_NODELETE
只在以下地方设置。 None 适用于这种情况 [或大多数 dlopen
情况]。
- 如果
dlopen
的 flags 参数有 RTLD_NODELETE
,我们可以避免。
- 如果启用分析,与我们的
dlopen
] 相关的 profile 映射 [not 得到 DF_1_NODELETE
设置
- 如果符号具有类型 10 (
STB_GNU_UNIQUE
)[=188= 的绑定类型(例如 LOCAL
、GLOBAL
、WEAK
等) ]
- 符号被非动态对象作为 UNDEF [在其符号 table] 中引用,无法删除 [因为引用对象已设置 DF_1_NODELETE]。由于上述前提条件,这不适用。
- 添加依赖项 [来自 _different 对象] 时出现
malloc
失败。此代码甚至不会针对此处的案例执行。
并且,撇开 OP 的用法不谈,dlopen/dlclose
有合理的理由可以像我设置的那样工作 up/described。
在这里查看我的回答:
在那里,OP 需要一个 不间断 程序,该程序可以 运行 用于 months/years(例如,高可靠性、关键任务应用程序)。如果 [通过包管理器等] 安装了其中一个共享库的更新版本,则程序必须动态地即时 无需 重新执行,能够加载更新的版本。
假设没有其他链接到共享库 libshlib
的可执行文件已经加载。并假设 libshlib
包含一个标记为 __attribute__(constructor)
的函数和一个标记为 __attribute__(destructor)
的函数。当链接到 libshlib
的可执行文件启动时,将加载 libshlib
并且标有 __attribute(constructor)
的对应函数是 运行 exactly一次。但是如果可以重新加载共享库会发生什么,例如通过用户定义的信号,例如 SIGUSR1
?根据我的测试,__attribute__(constructor)
似乎不再是 运行。这是正确的还是有标准的说法?
我假设您有一个程序 linked 通过(例如):
cc -o mypgm mypgm.o -lshlib
执行时,一旦 ELF 解释器加载 libshlib.so
并执行了构造函数,库就不会再次加载。 旁注:要找到您的口译员,请执行以下操作:readelf -a mypgm | grep interpreter:
如果程序接收到信号(例如SIGUSR1
),信号要么被信号处理程序捕获(假设signal
或sigaction
已被调用以设置一个) ,或者采取默认操作(即 SIGUSR1
的 [IIRC] 程序终止)。这 不会 导致重新加载库。
没有 其他操作可以导致重新加载库。程序退出时只会调用析构函数(例如调用 main
returns 或 exit
)。
甚至手动调用析构函数也没有效果,因为构造函数和析构函数是独立的。 (例如,构造函数可以执行 able = malloc(...)
,而析构函数可以执行 free(able)
。但是,析构函数可以执行 free(baker)
。)调用析构函数不会 "reset" 构造函数。
要获得 "reload" 效果,库需要 动态 loaded/unloaded 通过 dlopen/dlsym/dlclose
。也就是说,link 命令将是:
cc -o mypgm mypgm.o
然后,mypgm
会 [在某个时候] 调用 dlopen("libshlib.so")
(并且会调用构造函数)。当 [and if] mypgm
调用 dlclose
时,libshlib.so
将被卸载(并调用析构函数)。
如果 mypgm
然后调用 dlopen("libshlib.so")
秒 次,构造函数 将 [再次] .
更新:
Note that calling
dlclose
does not necessarily unload the library or call destructors.
我刚刚检查了 [在 glibc
] 中的代码。图书馆有refcount。如果在进入 dlclose
时引用计数为 1,则库 将被卸载 ,上面的 libshlib.so
和 dlopen
应该是这种情况 [因为没有人否则会增加它。
换句话说,要强制执行 "desired" 行为,其他任何东西都不应通过 -lshlib
引用 libshlib
。不是程序或任何其他 .so
。这就奠定了基础。
请注意,如果 libshlib.so
想要 glibc
,但程序也是如此,卸载 libshlib
会降低 glibc
引用计数,但 glibc
会保持不变,因为它的引用计数是 [still] >0.
There are conditions where the library can't be unloaded (in fact, these conditions are much more common then conditions when the library can be unloaded).
同样,这取决于引用计数和[可能]某些状态。当库从 "static" linkage 加载时(与 dlopen
相比),refcount 会额外增加,因此不会被抽取。
该代码还处理构造函数在其 自己的 库中调用 dlopen
的情况。
对于给定的 libA
,如果它需要 libB
,B 的引用计数将由 A 的 load/unload.
If the library is not unloaded, then it's not well defined whether destructors will run, and whether the subsequent dlopen will run constructors again
libshlib
以这种方式使用 dlopen
的全部意义在于 保证 在 dlopen
加载并在 [=27] 卸载=] [连同 constructor/destructor 操作]。如果没有对它的静态引用或循环依赖,这 将 为真,这是起始条件。
更新#2:
The part about "as nobody else bumps it up" is way too simplistic.
不要混淆散文和实质内容。
如上所述:如果没有对它的静态引用或循环依赖,将为真。
这意味着只有为shlib
做dlopen/dlclose
的executable/object引用了shlib
中的一个符号。
而且,这只是 通过 dlsym
。否则,它是静态引用(即在对象的符号 table 中作为 UNDEF)]。
并且,没有shlib
拖入的共享库是指shlib
[循环依赖]中定义的符号。
Look at all the places where DF_1_NODELETE is set during symbol resolution.
是的,我看了。
DF_1_NODELETE
只在以下地方设置。 None 适用于这种情况 [或大多数 dlopen
情况]。
- 如果
dlopen
的 flags 参数有RTLD_NODELETE
,我们可以避免。 - 如果启用分析,与我们的
dlopen
] 相关的 profile 映射 [not 得到DF_1_NODELETE
设置 - 如果符号具有类型 10 (
STB_GNU_UNIQUE
)[=188= 的绑定类型(例如LOCAL
、GLOBAL
、WEAK
等) ] - 符号被非动态对象作为 UNDEF [在其符号 table] 中引用,无法删除 [因为引用对象已设置 DF_1_NODELETE]。由于上述前提条件,这不适用。
- 添加依赖项 [来自 _different 对象] 时出现
malloc
失败。此代码甚至不会针对此处的案例执行。
并且,撇开 OP 的用法不谈,dlopen/dlclose
有合理的理由可以像我设置的那样工作 up/described。
在这里查看我的回答:
在那里,OP 需要一个 不间断 程序,该程序可以 运行 用于 months/years(例如,高可靠性、关键任务应用程序)。如果 [通过包管理器等] 安装了其中一个共享库的更新版本,则程序必须动态地即时 无需 重新执行,能够加载更新的版本。