共有ライブラリ内のグローバル/静的変数の初期化に関する問題が発生しました。特定の変数が2回初期化されているようです。共有ライブラリの__attribute __((コンストラクタ))によるグローバル/静的変数の初期化の問題
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
「2回初期化されました」という証拠はありません –
ctor/dtor呼び出しのシーケンスは、 'onLoad()'が呼び出される前に 's4'が構築されなかったように見せますが、その後に構築されます。最初の3つのctorコールは、ヒープ割り当てのものです。 4番目は一時的な 'MyStruct(2)'であり、次のdtor呼び出しは一時的に破棄されます。最後の 'printf()'の後まで、 's4'のデフォルトのctor呼び出しはありません。それは確かに奇妙です。しかし、それはおそらく 's4.s 'が1になる理由です。 – cdhowie
@BoundaryImpositionええ、そういうわけで、私はそれが*そうだと言います。たとえば、 's4'は2回構築されているようですが、構築前に使用されていて、それが構築されています。 – Mine