I meet an issue with global/static variables' initialization with __attribute__((constructor)) in shared library, that certain variables seem to be initialized twice.
Below are code snippets:
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;
}
Compiled with
$ g++ -fPIC -shared -std=c++14 shared.cpp -o libshared.so
$ g++ -std=c++14 -o main main.cpp -ldl
The output:
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
The value of s1 and s3 are expected.
But s2 and s4 behave weird.
s2.get()should be0x121b220, but infoo()it becomesnullptr;s4's value is printed ass4.s: 2inonLoad(), but after that its constructor is called with default values=1, then infoo()its value iss=1.
Putting the variables in anonymous namespace has the same result.
What's wrong with s2 and s4?
My OS: Ubuntu 16.04.2, GCC: 5.4.0
s4was never constructed beforeonLoad()got called, but then gets constructed after. The first three ctor calls are from your heap allocations. The fourth is the temporaryMyStruct(2)and the following dtor call is the temporary being destroyed. There's no default ctor call fors4-- until after the finalprintf(). That's strange indeed. That's probably whys4.sbecomes 1, though. - cdhowies4seems to be constructed twice, or it is used before constructed, then it's constructed... - MineMyStructconstructor. - Lightness Races in Orbit-Oso we don't get copy elision. See my comment.s4does appear to be copy-assigned from the temporary prior to its default construction, which happens afteronLoad()returns, clobbering the value 2 with 1. - cdhowie