Термин ABI используется для обозначения двух разных, но связанных понятий.
Когда речь идет о компиляторах, это относится к правилам, используемым для перевода из конструкций исходного уровня в двоичные конструкции. Насколько велики типы данных? как работает стек? как передать параметры в функции? какие регистры должны быть сохранены вызывающим абонентом против вызываемого абонента?
Когда речь идет о библиотеках, это относится к двоичному интерфейсу, представленному скомпилированной библиотекой. Этот интерфейс является результатом ряда факторов, включая исходный код библиотеки, правила, используемые компилятором, а в некоторых случаях определения, взятые из других библиотек.
Изменения в библиотеке могут нарушить ABI, не нарушая API. Рассмотрим, например, библиотеку с интерфейсом вроде.
void initfoo(FOO * foo)
int usefoo(FOO * foo, int bar)
void cleanupfoo(FOO * foo)
и прикладной программист пишет код, подобный
int dostuffwithfoo(int bar) {
FOO foo;
initfoo(&foo);
int result = usefoo(&foo,bar)
cleanupfoo(&foo);
return result;
}
Программисту приложения не важен размер или расположение FOO, но двоичный файл приложения заканчивается жестко заданным размером foo. Если программист библиотеки добавляет дополнительное поле в foo, и кто-то использует новый двоичный файл библиотеки со старым двоичным файлом приложения, то библиотека может получить доступ к памяти за пределами границ.
OTOH, если автор библиотеки разработал свой API как.
FOO * newfoo(void)
int usefoo(FOO * foo, int bar)
void deletefoo((FOO * foo, int bar))
и разработчик приложения пишет код, подобный
int dostuffwithfoo(int bar) {
FOO * foo;
foo = newfoo();
int result = usefoo(&foo,bar)
deletefoo(&foo);
return result;
}
Тогда бинарному приложению не нужно ничего знать о структуре FOO, и все это можно спрятать внутри библиотеки. Цена, которую вы платите за это, хотя это то, что кучи операций.