动态加载的类的静态成员变量 [英] Static member variable for class that is dynamically loaded

查看:315
本文介绍了动态加载的类的静态成员变量的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如果我要使用C ++中的dlopen之类的东西来加载某些符号,而该转换单元中的其他类具有static成员变量,那么这些静态成员变量的行为究竟是什么.它们是初始化还是否,是因为库实际上不是仅加载您所查找的符号而被初始化(我想后者是不正确的,因为如果您查找的符号也需要加载这些符号)?

If I were to load up some symbols using something like dlopen in C++ while other classes in that translation unit had static member variables what exactly is the behavior of those static member variables. Do they get initialized or no because the library isn't really loaded just the symbols that you looked up (which I'm thinking the latter is not true because if the symbol you looked up needs those they need to be loaded too)?

推荐答案

简而言之,不能保证在引用外部转换函数或同一转换单元中的变量之前,将初始化在编译时无法初始化的静态变量.到.即使对于静态链接也是如此.至于尝试在动态加载的库中获取静态变量以在加载时进行初始化,我的经验是,您常常会很幸运,尤其是对于小型程序,但从根本上讲,这是未定义的行为,因此不应依赖.由此产生的错误是不可预测的,难以复制且高度特定于系统的.

In short, there's no guarantee that static variables that cannot be initialized at compile time will be initialized before an externally visible function or variable in the same translation unit is referred to. This is true even for static linking. As for trying to get static variables in dynamically loaded libraries to initialize upon loading, my experience is that often you'll get lucky, especially for small programs, but fundamentally this is undefined behavior and should not be relied on. The resulting bugs are unpredictable, difficult to reproduce, and highly system specific.

首先,提供一些标准术语并解释为什么这是未定义的行为,然后提供一些解决方法.

First, some standardese and an explanation of why this is undefined behavior and then some workarounds.

不幸的是,静态"一词在标准中已重载,请多多包涵.该标准同时引用了静态存储持续时间静态初始化.标准定义的存储期限类型为静态,线程,自动和动态.他们像他们听起来那样.静态存储持续时间表示该变量的生存期是程序的整个持续时间.

The word static is unfortunately overloaded in the Standard so bear with me. The Standard makes reference to both static storage duration and static initialization. The types of storage duration defined by the Standard are static, thread, automatic, and dynamic. They are as they sound. Static storage duration means that the lifetime of such a variable is the entire duration of the program.

静态初始化是一个不同的概念.尽管每个程序执行一次只能存储一个变量,但是在程序启动时可能不知道将使用该变量初始化的值.在程序开始时,所有具有静态存储持续时间的变量将被初始化为零,然后将那些可以被持久化的变量初始化为常数.细点在第3.6.2节中,但粗略地讲,如果静态变量的初始化仅依赖于常量表达式,则将对其进行常量初始化.零初始化和常量初始化共同称为静态初始化.对应的是动态初始化.这些是有趣的,但是不幸的是,在动态链接的情况下,没有一种便携式方法强制执行动态初始化,首先是在main()之前执行,在动态加载的情况下是在dlopen()返回之前. C ++根本不需要这样.

Static initialization is a distinct concept. Although a variable may be stored only once per program execution, the value it will be initialized with may be not be known when the program starts. At the start of the program, all variables with static storage duration will be zero initialized and those that can be will then be constant initialized. The fine points are in §3.6.2 but roughly, a static variable will be constant initialized if its initialization relies only on constant expressions. Together, zero initialization and constant initialization are termed static initialization. The counterpart is dynamic initialization. These are the interesting ones but unfortunately there's no portable way to force dynamic initialization to take place before main() first executes, in the case of dynamic linking, or before dlopen() returns, in the case of dynamic loading. C++ simply does not demand such.

C ++ 11标准的关键部分在第3.6.2节中:

The key part of the C++11 Standard is in §3.6.2:

是否动态初始化一个是由实现定义的 具有静态存储持续时间的非局部变量是在 主声明的第一个.如果初始化被推迟到一些 在第一个main陈述之后的时间点,应在第一个main陈述之前发生 在该函数中定义的任何函数或变量的第一个odr-use(3.2) 与要初始化的变量相同的转换单元.

It is implementation-defined whether the dynamic initialization of a non-local variable with static storage duration is done before the first statement of main. If the initialization is deferred to some point in time after the first statement of main, it shall occur before the first odr-use (3.2) of any function or variable defined in the same translation unit as the variable to be initialized.

尽管如此,如果您进行了实验,您会发现有时这确实可行.有时,通过将其填充到静态变量的构造函数中,可以使任意代码在库加载时运行.是否发生这种情况仅取决于编译器(而不是链接器). dlopen 的手册页进行了解释.

Nonetheless, if you've experimented, you've noticed that sometimes this does work. Sometimes you can get arbitrary code to run upon library loading by stuffing it in the constructors of static variables. Whether this happens is simply up to the compiler (not the linker). The manpage for dlopen explains.

如果动态库导出了名为_init()的例程,则该代码将在加载之后dlopen()返回之前执行

If a dynamic library exports a routine named _init(), then that code is executed after the loading, before dlopen() returns

检查以标准C ++编写的小型共享库的asm输出,我可以看到clang 3.4和g ++ 4.8都添加了_init节,但是并不需要这样做.

Inspecting the asm output of a small shared object written in standard C++, I can see that clang 3.4 and g++ 4.8 both add an _init section, however they are not required to do so.

对于变通办法,已经很常见的gcc扩展确实可以控制此行为.通过向函数添加构造函数属性,我们可以坚持要求它们在库初始化时运行. dlopen的链接的联机帮助页建议使用此方法.

As for workarounds, a gcc extension that has become commonplace does allow control of this behavior. By adding a constructor attribute to functions, we can insist that they be run upon library initialization. The linked manpage for dlopen suggests using this method.

有关功能属性和 GCC文档 ="https://stackoverflow.com/questions/2053029/how-exactly-does-attribute-constructor-work">此SO问题,其中包含示例用法. gcc,clang,IBM XL支持此扩展,我猜测icc也支持它. MSVC不支持此功能,但我知道有类似的地方.

See the GCC documentation on function attributes and this SO question which has an example usage. This extension is supported by gcc, clang, IBM XL, and my guess is that icc supports it too. MSVC does not support this but I understand there's something similar.

真正的便携式解决方案难以捉摸.正如标准所说,如果您能以某种方式在与静态变量相同的转换单元中使用odr,则必须初始化静态变量.调用一个函数,甚至只是一个虚函数都可以.

A truly portable solution is elusive. As the Standard says, if you can somehow cause an odr usage in the same translation unit as the static variable, then the static variable must be initialized. Calling a function, even a dummy function just for this purpose, would work.

这篇关于动态加载的类的静态成员变量的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆