c++ - 共享库中 __attribute__((constructor)) 的全局/静态变量初始化问题

标签 c++ c++11 shared-libraries

我遇到了全局/静态变量初始化的问题 __attribute__((constructor))在共享库中,某些变量似乎被初始化了两次。

下面是代码片段:

共享.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);
}

ma​​in.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预计。

但是s2s4行为怪异。

  • s2.get()应该是0x121b220 ,但是在 foo()它变成nullptr ;
  • s4的值打印为 s4.s: 2onLoad() ,但之后会使用默认值 s=1 调用其构造函数。 ,然后在 foo()它的值为s=1 .

将变量放入匿名命名空间具有相同的结果。

s2 有什么问题吗?和s4

我的操作系统:Ubuntu 16.04.2,GCC:5.4.0

最佳答案

根据 this GCC bug report 上的讨论和 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 可能会导致为未初始化的指针成员调用删除。根据 C++ 规范,GCC 的未指定行为会转换为未定义行为(在本例中),因为它是 undefined behavior to read from an uninitialized variable (未初始化的 unsigned char 除外)。

无论如何,要纠正这个问题,您确实需要使用 __attribute((init_priority)) 在构造函数之前顺序初始化静态声明的对象。

关于c++ - 共享库中 __attribute__((constructor)) 的全局/静态变量初始化问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43941159/

相关文章:

c++ - ifstream getline 问题

c++ - std::transform 的最后一个参数

linux - 运行程序时无法加载共享库

c - 从共享/动态库加载符号表结构的可移植性如何?

c++ - 在 OpenGL 中动态加载着色器毫无意义吗?

c++ - 按值移除 std::vector 中的对象

c++ - std::mutex::lock 阻塞 CPU 使用

c++ - 编译 C++ 代码时出现编译错误

c++ - Qt 应用程序从 QtCreator 启动时会找到库,但不能从命令行启动

c++ - 在 POSIX 中正确使用消息队列