27

例如,我可以混合使用 GCC-4.6 和 GCC-4.9 编译的一组库。

我知道不同的编译器“品种”(例如 VS)不能与 MinGW 一起使用,但不同代的同一编译器可以吗?有可能出现问题吗?如果是这样呢?

4

2 回答 2

27

同一编译器的不同代有时可以相互兼容,但并非总是如此。例如,GCC 4.7.0更改了其 C/C++ ABI,这意味着使用 4.7.0+ 和 4.7.0- 编译的库不太可能相互兼容(因此在您的示例中,使用 4.6 编译的库不会与用 4.9 编译的库兼容)。在给定的编译器版本中也可能存在 ABI 错误,如 GCC 4.7.0/4.7.1 中所发生的

GCC 版本 4.7.0 和 4.7.1 对 C++ 标准库进行了更改,这影响了 C++11 模式下的 ABI:向 std::list 添加了一个数据成员,改变了它的大小并改变了一些成员函数的定义,并且std::pair 的移动构造函数非常重要,它改变了带有 std::pair 参数或返回类型的函数的调用约定。GCC 版本 4.7.2 的 ABI 不兼容性已得到修复,但因此使用 GCC 4.7.0 或 4.7.1 编译的 C++11 代码可能与使用不同 GCC 版本和 C++ 编译的 C++11 代码不兼容使用任何版本编译的 98/C++03 代码。

GCC ABI Policy and Guidelines 页面表明他们试图保持前向兼容性,但不保持向后兼容性:

版本控制使库二进制文件的后续版本能够添加新符号和添加功能,同时保持与该系列中先前版本的兼容性。因此,如果库二进制文件被精心管理的后续库二进制文件替换,与库二进制文件的初始版本链接的程序二进制文件仍将正确运行。这称为前向兼容性。

相反(向后兼容)是不正确的。不能在发布系列中获取与最新版本的库二进制文件链接的程序二进制文件(添加了额外的符号),在库二进制文件的初始版本中替换,并保持链接兼容。

该页面还对 GCC 用于标记给定组件的不同版本的版本控制系统进行了一些相当长的解释,以及对 GCC 本身背后的版本控制的解释:

允许的更改

  • 以下将导致库次要版本号增加,例如从“libstdc++.so.3.0.4”到“libstdc++.so.3.0.5”。

  • 添加导出的全局或静态数据成员

  • 添加导出函数、静态或非虚拟成员函数

  • 通过附加实例化添加一个或多个导出符号

  • 其他允许的更改是可能的。

禁止更改

以下非详尽列表将导致库主要版本号增加,例如从“libstdc++.so.3.0.4”到“libstdc++.so.4.0.0”。

  • gcc/g++ 编译器 ABI 的变化

  • 更改导出符号的大小

  • 更改导出符号的对齐方式

  • 更改导出符号的布局

  • 更改导出符号的修饰

  • 删除导出的符号

  • 通过添加或删除基类来更改类型的继承属性

  • 更改 C++ 标准中指定的类型的大小、对齐方式或布局。这些可能不一定在库二进制文件中实例化或以其他方式导出,并且包括所有必需的语言环境方面,以及诸如 std::basic_streambuf 等内容。

  • 将显式复制构造函数或析构函数添加到否则将具有隐式版本的类。这将改变编译器在按值返回语句或参数中处理此类的方式:编译器将被迫使用内存,而不是在寄存器中传递此类的实例。有关详细信息,请参阅 C++ ABI 文档的函数调用约定和 API 部分。

注意加粗的位。在一个完美的世界中,具有相同主要版本号的 GCC 版本将是二进制兼容的。这不是一个完美的世界,因此在混合这样的编译器版本之前要非常仔细地进行测试,但总的来说你可能会没事的。

于 2014-05-27T17:30:20.130 回答
3

如果它们与 ABI(应用程序二进制接口)兼容,则只能混合从不同编译器或同一编译器的不同版本生成的二进制文件。

像:

  • 调用程序
  • 名称修改
  • 线程本地存储处理

都是 ABI 的一部分。

如果其中一件事情发生了变化,您会发现您要么得到链接器错误、崩溃或其他形式的意外行为。作为一般规则,编译器供应商通常会尝试至少保持与旧版本的向后兼容性,但不能保证这一点。正如其他人所说,您必须阅读文档或重新编译所有内容。

于 2014-05-27T17:26:01.543 回答