gcc链接器扩展__attribute __((constructor))导致main()崩溃

时间:2019-02-16 15:10:48

标签: constructor linker attributes g++ elf

首先,我有一个singleton.cpp文件来构建一个单例对象,并使用 attribute ((constructor))

声明实例。
#include<iostream>
using namespace std;
class singleton{
public:
    singleton(){cout<<"singleton ctor\n";}
};
__attribute__((constructor)) static void beforeFunction()
{
    printf("beforeFunction\n");
    singleton obj;
}

还有一个简单的main.cpp

#include<iostream>
using namespace std;
int main(){
    return 0;
}

我一起构建main.cpp singleton.cpp:

g++ singleton.cpp main.cpp -o main

./main
beforeFunction
Segmentation fault

那么为什么我的程序崩溃了,发生了什么?如何解决?我在ubuntu上使用gcc。非常感谢。

3 个答案:

答案 0 :(得分:2)

我用g++ (Debian 7.3.0-5)g++ (GCC) 9.0.0 20180902 (experimental)复制了此文件。

这很有趣,它失败了:

$ g++ singleton.cpp main.cpp && ./a.out
beforeFunction
Segmentation fault

但这可以按预期工作:

$ g++ main.cpp singleton.cpp && ./a.out
beforeFunction
singleton ctor

正如Acorn正确指出的那样,在您调用单例构造函数时,iostream / std::cout机制尚未正确初始化。之所以发生这种情况,是因为main.o中发出了特殊的代码(并且仅 main.o),它调用了std::ios_base::Init::Init()。并且仅因为main.cpp具有无关的#include <iostream>

  

如何解决?

最好的解决办法是完全不使用__attribute__((constructor)) 。就您而言,没有理由去做您正在做的事情。改为这样做:

// singleton2.cpp
#include<iostream>
using namespace std;

class singleton{
  public:
    singleton(){cout<<"singleton ctor\n";}
};

static singleton obj;

使用上面的代码,链接的任何顺序均有效:

$ g++ main.cpp singleton2.cpp && ./a.out
singleton ctor

$ g++ singleton2.cpp main.cpp && ./a.out
singleton ctor

如果您坚持使用__attribute__((constructor)),请确保main.o在链接行之前可以使用iostream的任何其他对象。

答案 1 :(得分:1)

  

那为什么我的程序崩溃了,发生了什么事?

运行iostream函数时,__attribute__((constructor))机器很可能尚未初始化。

  

如何解决?

使用printf之类的C I / O似乎可以解决您的问题;或更妙的是,避免完全使用__attribute__((constructor))(它不是标准的C或C ++,即,它使程序不可移植)。

请注意,__attribute__((constructor))无需创建单例或全局对象。

答案 2 :(得分:1)

std::cout是在静态C ++对象的帮助下初始化的,请参见<iostream>

  // For construction of filebuffers for cout, cin, cerr, clog et. al.
  static ios_base::Init __ioinit;

由于您的代码既依赖于此静态构造函数,又具有ELF构造函数,因此它会遇到此GCC limitation

  

但是,目前,尚不确定调用静态存储持续时间的C ++对象的构造函数和用属性constructor装饰的函数的顺序。

如果您改用C ++对象,则顺序是明确定义的。 GCC手册还建议使用init_priority attribute,但是由于您不能将其应用于__ioinit的定义(通过预处理器黑客除外),因此我认为在这种情况下这没有帮助。