Global/Static 共享库中 __attribute__((constructor)) 的变量初始化问题
Global/Static variables initialization issue with __attribute__((constructor)) in shared library
我在共享库中用 __attribute__((constructor))
初始化 global/static 变量时遇到一个问题,某些变量似乎被初始化了两次。
下面是代码片段:
shared.cpp
struct MyStruct
{
MyStruct(int s = 1)
: s(s) {
printf("%s, this: %p, s=%d\n", __func__, this, s);
}
~MyStruct() {
printf("%s, this: %p, s=%d\n", __func__, this, s);
}
int s;
};
MyStruct* s1 = nullptr;
std::unique_ptr<MyStruct> s2 = nullptr;
std::unique_ptr<MyStruct> s3;
MyStruct s4;
void onLoad() __attribute__((constructor));
void onLoad()
{
s1 = new MyStruct;
s2 = std::make_unique<MyStruct>();
s3 = std::make_unique<MyStruct>();
s4 = MyStruct(2);
printf("&s1: %p, &s2: %p, &s3: %p\n", &s1, &s2, &s3);
printf("s1: %p, s2: %p, s3: %p\n", s1, s2.get(), s3.get());
printf("s4: %p, s4.s: %d\n", &s4, s4.s);
}
extern "C" void foo()
{
printf("&s1: %p, &s2: %p, &s3: %p\n", &s1, &s2, &s3);
printf("s1: %p, s2: %p, s3: %p\n", s1, s2.get(), s3.get());
printf("s4: %p, s4.s: %d\n", &s4, s4.s);
}
main.cpp
#include <cstdio>
#include <dlfcn.h>
using Foo = void(*)(void);
int main()
{
printf("Calling dlopen...\n");
void* h = dlopen("./libshared.so", RTLD_NOW | RTLD_GLOBAL);
Foo f = reinterpret_cast<Foo>(dlsym(h, "foo"));
printf("\nCalling foo()...\n");
f();
return 0;
}
编译为
$ g++ -fPIC -shared -std=c++14 shared.cpp -o libshared.so
$ g++ -std=c++14 -o main main.cpp -ldl
输出:
Calling dlopen...
MyStruct, this: 0x121b200, s=1
MyStruct, this: 0x121b220, s=1
MyStruct, this: 0x121b240, s=1
MyStruct, this: 0x7ffc19736910, s=2
~MyStruct, this: 0x7ffc19736910, s=2
&s1: 0x7fb1fe487190, &s2: 0x7fb1fe487198, &s3: 0x7fb1fe4871a0
s1: 0x121b200, s2: 0x121b220, s3: 0x121b240
s4: 0x7fb1fe4871a8, s4.s: 2
MyStruct, this: 0x7fb1fe4871a8, s=1
Calling foo()...
&s1: 0x7fb1fe487190, &s2: 0x7fb1fe487198, &s3: 0x7fb1fe4871a0
s1: 0x121b200, s2: (nil), s3: 0x121b240
s4: 0x7fb1fe4871a8, s4.s: 1
~MyStruct, this: 0x7fb1fe4871a8, s=1
~MyStruct, this: 0x121b240, s=1
s1
和 s3
的值是预期的。
但是 s2
和 s4
表现得很奇怪。
s2.get()
应该是0x121b220
,但在foo()
中就变成了nullptr
;
s4
的值在 onLoad()
中打印为 s4.s: 2
,但之后它的构造函数以默认值 s=1
调用,然后在 foo()
] 其值为s=1
.
将变量放在匿名命名空间中具有相同的结果。
s2
和 s4
有什么问题?
我的 OS: Ubuntu 16.04.2, GCC: 5.4.0
根据 this GCC bug report and this follow-up doc patch 上的讨论,您所看到的似乎是 GCC 中未指定的行为(不是错误)。
However, the order in which constructors for C++ objects with static storage duration and functions decorated with attribute constructor
are invoked is unspecified. In mixed declarations, attribute init_priority
can be used to impose a specific ordering.
在这种情况下似乎勉强避免了段错误,因为分配给未初始化的 std::unique_ptr
可能会导致为未初始化的指针成员调用 delete
。根据 C++ 规范,GCC 的未指定行为转化为未定义的行为(在这种特殊情况下),因为它是 undefined behavior to read from an uninitialized variable(除了未初始化的 unsigned char
)。
无论如何,要解决此问题,您确实需要使用 __attribute((init_priority))
在构造函数之前对静态声明的对象进行初始化。
我在共享库中用 __attribute__((constructor))
初始化 global/static 变量时遇到一个问题,某些变量似乎被初始化了两次。
下面是代码片段:
shared.cpp
struct MyStruct
{
MyStruct(int s = 1)
: s(s) {
printf("%s, this: %p, s=%d\n", __func__, this, s);
}
~MyStruct() {
printf("%s, this: %p, s=%d\n", __func__, this, s);
}
int s;
};
MyStruct* s1 = nullptr;
std::unique_ptr<MyStruct> s2 = nullptr;
std::unique_ptr<MyStruct> s3;
MyStruct s4;
void onLoad() __attribute__((constructor));
void onLoad()
{
s1 = new MyStruct;
s2 = std::make_unique<MyStruct>();
s3 = std::make_unique<MyStruct>();
s4 = MyStruct(2);
printf("&s1: %p, &s2: %p, &s3: %p\n", &s1, &s2, &s3);
printf("s1: %p, s2: %p, s3: %p\n", s1, s2.get(), s3.get());
printf("s4: %p, s4.s: %d\n", &s4, s4.s);
}
extern "C" void foo()
{
printf("&s1: %p, &s2: %p, &s3: %p\n", &s1, &s2, &s3);
printf("s1: %p, s2: %p, s3: %p\n", s1, s2.get(), s3.get());
printf("s4: %p, s4.s: %d\n", &s4, s4.s);
}
main.cpp
#include <cstdio>
#include <dlfcn.h>
using Foo = void(*)(void);
int main()
{
printf("Calling dlopen...\n");
void* h = dlopen("./libshared.so", RTLD_NOW | RTLD_GLOBAL);
Foo f = reinterpret_cast<Foo>(dlsym(h, "foo"));
printf("\nCalling foo()...\n");
f();
return 0;
}
编译为
$ g++ -fPIC -shared -std=c++14 shared.cpp -o libshared.so
$ g++ -std=c++14 -o main main.cpp -ldl
输出:
Calling dlopen...
MyStruct, this: 0x121b200, s=1
MyStruct, this: 0x121b220, s=1
MyStruct, this: 0x121b240, s=1
MyStruct, this: 0x7ffc19736910, s=2
~MyStruct, this: 0x7ffc19736910, s=2
&s1: 0x7fb1fe487190, &s2: 0x7fb1fe487198, &s3: 0x7fb1fe4871a0
s1: 0x121b200, s2: 0x121b220, s3: 0x121b240
s4: 0x7fb1fe4871a8, s4.s: 2
MyStruct, this: 0x7fb1fe4871a8, s=1
Calling foo()...
&s1: 0x7fb1fe487190, &s2: 0x7fb1fe487198, &s3: 0x7fb1fe4871a0
s1: 0x121b200, s2: (nil), s3: 0x121b240
s4: 0x7fb1fe4871a8, s4.s: 1
~MyStruct, this: 0x7fb1fe4871a8, s=1
~MyStruct, this: 0x121b240, s=1
s1
和 s3
的值是预期的。
但是 s2
和 s4
表现得很奇怪。
s2.get()
应该是0x121b220
,但在foo()
中就变成了nullptr
;s4
的值在onLoad()
中打印为s4.s: 2
,但之后它的构造函数以默认值s=1
调用,然后在foo()
] 其值为s=1
.
将变量放在匿名命名空间中具有相同的结果。
s2
和 s4
有什么问题?
我的 OS: Ubuntu 16.04.2, GCC: 5.4.0
根据 this GCC bug report and this follow-up doc patch 上的讨论,您所看到的似乎是 GCC 中未指定的行为(不是错误)。
However, the order in which constructors for C++ objects with static storage duration and functions decorated with attribute
constructor
are invoked is unspecified. In mixed declarations, attributeinit_priority
can be used to impose a specific ordering.
在这种情况下似乎勉强避免了段错误,因为分配给未初始化的 std::unique_ptr
可能会导致为未初始化的指针成员调用 delete
。根据 C++ 规范,GCC 的未指定行为转化为未定义的行为(在这种特殊情况下),因为它是 undefined behavior to read from an uninitialized variable(除了未初始化的 unsigned char
)。
无论如何,要解决此问题,您确实需要使用 __attribute((init_priority))
在构造函数之前对静态声明的对象进行初始化。