链接gcc 6,gcc 7和gcc 8对象安全吗?

Bar*_*rry 17 c++ gcc c++17

链接C ++ 17,C ++ 14和C ++ 11对象是否要求链接使用不同语言标准编译的对象是否安全,而Jonathan Wakely在该问题上的出色回答解释了gcc / libstdc ++做出的ABI稳定性承诺确保这行得通。

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

  • 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中的空类类型的调用约定更改可能吗?

gez*_*eza 10

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

空类的调用约定的更改可能会在x86-64上引起问题。这是一个例子:

def.hpp

struct Empty { };

struct Foo {
    char dummy[16];
    int a;

    Foo() : a(42) { }
};

void fn(Empty, Foo);
Run Code Online (Sandbox Code Playgroud)

一个.cpp

#include "def.hpp"

int main() {
    fn(Empty(), Foo());
}
Run Code Online (Sandbox Code Playgroud)

two.cpp

#include <stdio.h>
#include "def.hpp"

void fn(Empty e, Foo foo) {
    printf("%d\n", foo.a);
}
Run Code Online (Sandbox Code Playgroud)

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

g++ -c -fabi-version=11 one.cpp
g++ -c -fabi-version=12 two.cpp
g++ one.o two.o
Run Code Online (Sandbox Code Playgroud)

结果a.out将不会打印预期的42

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

(注意:我已经包括了它,Foo::dummy所以Foo使用堆栈而不是寄存器Foo进行传递。如果使用寄存器进行传递,那将没有问题。)


Jon*_*ely 5

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

12 中空类类型的调用约定可能会发生变化吗?

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