链接 gcc 6、gcc 7 和 gcc 8 对象是否安全?

Is it safe to link gcc 6, gcc 7, and gcc 8 objects?

本文关键字:gcc 对象 是否 安全 链接      更新时间:2023-10-16

链接C++17、C++14和C++11对象是否安全 询问链接使用不同语言标准编译的对象,Jonathan Wakely 对这个问题的出色回答解释了 gcc/libstdc++ 为确保这有效而做出的 ABI 稳定性承诺。

不过,在 gcc 版本之间还有一件事可以更改 - 通过-fabi-version的语言 ABI .假设,为简单起见,我有三个目标文件:

  • foo.o,使用 gcc 6.5 c++14 编译
  • bar.o,使用 gcc 7.4 c++14 编译
  • quux.o,使用 gcc 8.3 c++17 编译

所有这些都使用各自的默认语言 ABI(即 10、11 和 13)。根据链接的答案,从库的角度来看,将这些对象链接在一起是安全的。但是,从语言ABI的角度来看,是否有可能出错的事情?有什么我应该注意的吗?大多数语言 ABI 更改似乎不会引起问题,但是 12 中空类类型的调用约定更改可能会?

大多数语言 ABI 更改似乎不会引起问题,但是 12 中空类类型的调用约定更改可能会?

更改空类的调用约定可能会导致 x86-64 出现问题。下面是一个示例:

def.hpp

struct Empty { };
struct Foo {
char dummy[16];
int a;
Foo() : a(42) { }
};
void fn(Empty, Foo);

一.cpp

#include "def.hpp"
int main() {
fn(Empty(), Foo());
}

二.cpp

#include <stdio.h>
#include "def.hpp"
void fn(Empty e, Foo foo) {
printf("%dn", foo.a);
}

现在,如果您使用 G++ 8 编译它们,具有不同的 ABI 11 和 12,例如:

g++ -c -fabi-version=11 one.cpp
g++ -c -fabi-version=12 two.cpp
g++ one.o two.o

生成的a.out不会打印预期的42

原因是旧的 ABI (11) 为堆栈上的Empty()保留了空间,但新的 ABI (12) 没有。因此,foo的地址在主叫方和被叫方之间会有所不同。

(注意:我已经包含了Foo::dummy以便使用堆栈而不是寄存器传递Foo因此。如果使用寄存器传递Foo,则不会有问题。

它们中的大多数都以次要的方式更改重整,这可能会导致链接时出现一些未定义的引用,或者只是由于相同的源代码生成两个具有不同名称的等效符号而导致一些代码膨胀,因此链接器不会合并。

空类类型的调用约定更改 12 可能?

是的,当然。如果您的非最终参数是空类型,那么这会影响函数的 ABI,并且差异可能导致未定义的行为(在实践中,访问堆栈上的垃圾,或参数获得错误的值)。