Безопасно ли связывать объекты gcc 6, gcc 7 и gcc 8?
Безопасно ли связывать объекты С++ 17, С++ 14 и С++ 11 о связывании объектов, скомпилированных с использованием различных языковых стандартов, и Джонатан Уэйкли, превосходный ответ на этот вопрос, объясняет обещания стабильности ABI, которые gcc/libstdc++ сделать, чтобы заверить, что это работает.
Есть еще одна вещь, которая может меняться между версиями gcc - язык ABI через -fabi-version
. Допустим, для простоты у меня есть три объектных файла:
-
foo.o
, скомпилированный с gcc 6.5 С++ 14 -
bar.o
, скомпилированный с gcc 7.4 С++ 14 -
quux.o
, скомпилированный с gcc 8.3 С++ 17
Все с соответствующими языками ABI по умолчанию (то есть 10, 11 и 13). Связывание этих объектов безопасно с точки зрения библиотеки согласно связанному ответу. Но есть ли вещи, которые могут пойти не так с точки зрения языка ABI? Есть ли что-то, за чем я должен следить? Кажется, что большинство изменений языка ABI не вызовут проблем, но изменение соглашения о вызовах для пустых типов классов в 12 может?
Ответы
Ответ 1
Кажется, что большинство изменений языка ABI не вызовут проблем, но изменение соглашения о вызовах для пустых типов классов в 12 может?
Изменение соглашения о вызовах для пустых классов может вызвать проблему на x86-64. Вот пример:
def.hpp:
struct Empty { };
struct Foo {
char dummy[16];
int a;
Foo() : a(42) { }
};
void fn(Empty, Foo);
one.cpp:
#include "def.hpp"
int main() {
fn(Empty(), Foo());
}
two.cpp:
#include <stdio.h>
#include "def.hpp"
void fn(Empty e, Foo foo) {
printf("%d\n", 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
передавался с использованием регистров, проблем не было бы.)
Ответ 2
Большинство из них изменяют искажения незначительными способами, что может привести к появлению некоторых неопределенных ссылок при связывании или просто некоторому раздуванию кода из-за идентичного исходного кода, создающего два эквивалентных символа с разными именами, поэтому компоновщик не будет объединен.
изменение соглашения о вызовах для пустых типов классов в 12 может?
Определенно да. Если у вас есть не окончательные параметры, которые являются пустыми типами, это влияет на ABI для функции, и различия могут привести к неопределенному поведению (на практике, доступ к нежелательной почте в стеке или параметры, получающие неправильные значения).