【问题标题】:gcc linker extension __attribute__((constructor)) causes crash in main()gcc 链接器扩展 __attribute__((constructor)) 导致 main() 崩溃
【发布时间】:2019-02-16 15:10:48
【问题描述】:

首先我有一个 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。非常感谢。

【问题讨论】:

    标签: constructor linker attributes g++ elf


    【解决方案1】:

    我用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 &lt;iostream&gt;

    如何解决?

    最好的解决办法是不使用__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 位于您的链接行之前任何其他可以使用iostreams 的对象。

    【讨论】:

    • 我很确定 main.cpp singleton.cpp 订单只有效,因为 main.cpp 中有一个流浪的 #include &lt;iostream&gt;
    【解决方案2】:

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

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

    如何解决?

    要么使用像printf 这样的C I/O,这似乎适用于你的情况;或者更好的是,完全避免使用__attribute__((constructor))(它不是标准的 C 或 C++,即它使您的程序不可移植)。

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

    【讨论】:

      【解决方案3】:

      std::cout 是在静态 C++ 对象的帮助下初始化的,参见 &lt;iostream&gt;

        // 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 的定义(通过预处理器hackery 除外),我认为这在这种情况下没有帮助。

      【讨论】:

        猜你喜欢
        • 2013-11-03
        • 1970-01-01
        • 1970-01-01
        • 2019-05-01
        • 1970-01-01
        • 2012-09-18
        • 1970-01-01
        • 1970-01-01
        • 2014-01-28
        相关资源
        最近更新 更多