gcc 链接器扩展 __attribute__((constructor)) 导致 main() 崩溃
gcc linker extension __attribute__((constructor)) causes crash in main()
首先,我有一个 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.cppsingleton.cpp:
g++ singleton.cpp main.cpp -o main
./main
beforeFunction
Segmentation fault
那么为什么我的程序崩溃了,发生了什么?如何解决?我在 ubuntu 上使用 gcc。非常感谢。
So why my program crashes, what happened?
最有可能的是,iostream
机器在 __attribute__((constructor))
函数 运行 时尚未初始化。
How to fix it?
或者像 printf
这样使用 C I/O,这似乎适用于您的情况;或更好,避免完全使用 __attribute__((constructor))
(它不是标准 C 或 C++,即它使您的程序不可移植)。
请注意,__attribute__((constructor))
不需要创建单例或全局对象。
我用 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>
.
How to fix it?
最好的解决办法是完全不使用 __attribute__((constructor))
。就您而言,没有理由做您正在做的事情。改为这样做:
// singleton2.cpp
#include<iostream>
using namespace std;
class singleton{
public:
singleton(){cout<<"singleton ctor\n";}
};
static singleton obj;
使用上面的代码,linking 的任一顺序都有效:
$ g++ main.cpp singleton2.cpp && ./a.out
singleton ctor
$ g++ singleton2.cpp main.cpp && ./a.out
singleton ctor
如果您坚持使用 __attribute__((constructor))
,请确保 main.o
在您的 link 行上 在 任何其他可以使用的对象之前iostream
s.
std::cout
是在静态 C++ 对象的帮助下初始化的,参见 <iostream>
:
// For construction of filebuffers for cout, cin, cerr, clog et. al.
static ios_base::Init __ioinit;
由于您的代码既依赖于此静态构造函数又具有 ELF 构造函数,因此它会遇到此 GCC limitation:
However, at present, the order in which constructors for C++ objects with static storage duration and functions decorated with attribute constructor
are invoked is unspecified.
如果改用 C++ 对象,则顺序定义明确。 GCC 手册还建议使用 init_priority
attribute,但由于您不能将其应用于 __ioinit
的定义(除非通过预处理器 hackery),我认为这在这种情况下没有帮助。
首先,我有一个 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.cppsingleton.cpp:
g++ singleton.cpp main.cpp -o main
./main
beforeFunction
Segmentation fault
那么为什么我的程序崩溃了,发生了什么?如何解决?我在 ubuntu 上使用 gcc。非常感谢。
So why my program crashes, what happened?
最有可能的是,iostream
机器在 __attribute__((constructor))
函数 运行 时尚未初始化。
How to fix it?
或者像 printf
这样使用 C I/O,这似乎适用于您的情况;或更好,避免完全使用 __attribute__((constructor))
(它不是标准 C 或 C++,即它使您的程序不可移植)。
请注意,__attribute__((constructor))
不需要创建单例或全局对象。
我用 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>
.
How to fix it?
最好的解决办法是完全不使用 __attribute__((constructor))
。就您而言,没有理由做您正在做的事情。改为这样做:
// singleton2.cpp
#include<iostream>
using namespace std;
class singleton{
public:
singleton(){cout<<"singleton ctor\n";}
};
static singleton obj;
使用上面的代码,linking 的任一顺序都有效:
$ g++ main.cpp singleton2.cpp && ./a.out
singleton ctor
$ g++ singleton2.cpp main.cpp && ./a.out
singleton ctor
如果您坚持使用 __attribute__((constructor))
,请确保 main.o
在您的 link 行上 在 任何其他可以使用的对象之前iostream
s.
std::cout
是在静态 C++ 对象的帮助下初始化的,参见 <iostream>
:
// For construction of filebuffers for cout, cin, cerr, clog et. al.
static ios_base::Init __ioinit;
由于您的代码既依赖于此静态构造函数又具有 ELF 构造函数,因此它会遇到此 GCC limitation:
However, at present, the order in which constructors for C++ objects with static storage duration and functions decorated with attribute
constructor
are invoked is unspecified.
如果改用 C++ 对象,则顺序定义明确。 GCC 手册还建议使用 init_priority
attribute,但由于您不能将其应用于 __ioinit
的定义(除非通过预处理器 hackery),我认为这在这种情况下没有帮助。