cst*_*fel 3 c++ linux shared-libraries dynamic-loading
dlopen如果我要使用类似C++ 的方式加载一些符号,而该翻译单元中的其他类具有static成员变量,那么这些静态成员变量的行为到底是什么。它们是否被初始化,因为库并没有真正加载您查找的符号(我认为后者是不正确的,因为如果您查找的符号需要它们也需要加载)?
简而言之,不能保证在编译时无法初始化的静态变量将在引用同一翻译单元中的外部可见函数或变量之前被初始化。即使对于静态链接也是如此。至于尝试在动态加载的库中获取静态变量以在加载时进行初始化,我的经验是,通常您会很幸运,特别是对于小程序,但从根本上讲,这是未定义的行为,不应依赖。由此产生的错误是不可预测的,难以重现,并且高度系统特定。
\n\n首先,一些标准和解释为什么这是未定义的行为,然后是一些解决方法。
\n\n不幸的是,静态这个词在标准中被过多使用,所以请耐心等待。该标准参考了静态存储持续时间和静态初始化。标准定义的存储持续时间类型有静态、线程、自动和动态。它们就像听起来一样。静态存储持续时间意味着此类变量的生命周期是程序的整个持续时间。
\n\n静态初始化是一个独特的概念。尽管变量在每次程序执行时只能存储一次,但在程序启动时可能不知道其初始化的值。在程序开始时,所有具有静态存储持续时间的变量都将被零初始化,而那些可以的变量将被常量初始化。要点在 \xc2\xa73.6.2 中,但粗略地说,如果静态变量的初始化仅依赖于常量表达式,则静态变量将被常量初始化。零初始化和常量初始化一起称为静态初始化。对应的是动态初始化。这些是有趣的,但不幸的是,没有可移植的方法来强制动态初始化在main()首次执行之前(在动态链接的情况下)或dlopen()返回之前(在动态加载的情况下)进行。C++ 根本不需要这样。
C++11标准的关键部分在\xc2\xa73.6.2中:
\n\n\n\n\n具有静态存储持续时间的非局部变量的动态初始化是否在 main 的第一个语句之前完成是由实现定义的。如果初始化被推迟到 main 的第一个语句之后的某个时间点,则它应发生在与变量相同的翻译单元中定义的任何函数或变量的第一个 odr-use (3.2) 之前。被初始化。
\n
尽管如此,如果您进行过实验,您就会发现有时这确实有效。有时,您可以通过将任意代码填充到静态变量的构造函数中来在库加载时运行任意代码。是否发生这种情况完全取决于编译器(而不是链接器)。dlopen的联机帮助页对此进行了解释。
\n\n\n\n\n如果动态库导出名为 _init() 的例程,则该代码将在加载后、dlopen() 返回之前执行
\n
检查用标准 C++ 编写的小型共享对象的 asm 输出,我可以看到 clang 3.4 和 g++ 4.8 都添加了 _init 部分,但它们不需要这样做。
\n\n至于解决方法,已经变得司空见惯的 gcc 扩展确实允许控制这种行为。通过向函数添加构造函数属性,我们可以坚持它们在库初始化时运行。dlopen 的链接联机帮助页建议使用此方法。
\n\n请参阅有关函数属性的GCC 文档和这个 SO 问题,其中有一个示例用法。gcc、clang、IBM XL都支持这个扩展,我的猜测是icc也支持它。MSVC 不支持这一点,但我知道有类似的东西。
\n\n真正的便携式解决方案是难以捉摸的。正如标准所说,如果您能以某种方式在与静态变量相同的翻译单元中导致 odr 使用,则必须初始化静态变量。调用一个函数,甚至是仅用于此目的的虚拟函数,都是可行的。
\n| 归档时间: |
|
| 查看次数: |
2686 次 |
| 最近记录: |