api Что такое двоичный интерфейс приложения (ABI)?



7 Answers

Если вы знаете сборку и как все работает на уровне ОС, вы соответствуете определенному ABI. ABI управляет такими вещами, как параметры передаются, где размещаются значения возврата. Для многих платформ существует только один ABI, и в этих случаях ABI - это просто «как все работает».

Тем не менее, ABI также управляет такими вещами, как, например, классы и объекты на C ++. Это необходимо, если вы хотите иметь возможность передавать ссылки на объекты через границы модулей или если вы хотите смешать код, скомпилированный с разными компиляторами.

Кроме того, если у вас есть 64-разрядная ОС, которая может выполнять 32-разрядные двоичные файлы, у вас будут разные ABI для 32- и 64-разрядного кода.

В общем, любой код, который вы связываете с тем же исполняемым файлом, должен соответствовать одному и тому же ABI. Если вы хотите связываться между кодом с использованием различных ABI, вы должны использовать некоторую форму протоколов RPC или сериализации.

Я думаю, что вы слишком стараетесь сжимать разные типы интерфейсов в фиксированный набор характеристик. Например, интерфейс необязательно должен быть разделен на потребителей и производителей. Интерфейс - это просто соглашение, посредством которого взаимодействуют два объекта.

ABI могут быть (частично) ISA-агностиками. Некоторые аспекты (например, соглашения о вызовах) зависят от ISA, в то время как другие аспекты (например, макет класса C ++) этого не делают.

Четко определенный ABI очень важен для людей, пишущих компиляторы. Без четко определенного ABI невозможно было бы создать интероперабельный код.

EDIT: Некоторые примечания для уточнения:

  • «Binary» в ABI не исключает использования строк или текста. Если вы хотите связать DLL, экспортирующую класс C ++, где-то в ней должны быть закодированы методы и типы подписей. Вот здесь и появляется C ++ name-mangling.
  • Причина, по которой вы никогда не предоставляли ABI, заключается в том, что подавляющее большинство программистов никогда этого не сделает. ABI предоставляются теми же людьми, которые разрабатывают платформу (то есть операционную систему), и очень немногие программисты когда-либо получат привилегии для разработки широко используемого ABI.
api compiler-construction binary operating-system abi

Я никогда не понимал, что такое ABI. Пожалуйста, не указывайте мне статью в Википедии. Если бы я мог это понять, я бы не стал размещать такой длинный пост.

Это мой взгляд на разные интерфейсы:

Пульт телевизора - это интерфейс между пользователем и телевизором. Это уже существующая сущность, но бесполезная (не предоставляет никакой функциональности) сама по себе. Все функции для каждой из этих кнопок на пульте дистанционного управления реализованы в телевизоре.

Интерфейс: это уровень существующей сущности между functionality и consumer этой функциональности. Интерфейс сам по себе не делает ничего. Он просто вызывает функциональность, лежащую позади.

Теперь в зависимости от того, кто у пользователя есть разные типы интерфейсов.

Команды интерфейса командной строки (CLI) - это существующие сущности, потребитель - пользователь, а функции - отстающие.

functionality: моя программная функциональность, которая решает какую-то цель, с которой мы описываем этот интерфейс.

existing entities: команды

consumer: пользователь

Окно графических пользовательских интерфейсов (GUI) , кнопки и т. Д. - это существующие сущности, и снова потребитель - это пользователь, и функции лежат позади.

functionality: моя программная функциональность, которая решает какую-то цель, с которой мы описываем этот интерфейс.

existing entities: окно, кнопки и т. д.

consumer: пользователь

Функции API прикладного программирования (API) или, если быть более верными, интерфейсы (в сопряженном программировании) - это существующие сущности, потребитель - это еще одна программа, а не пользователь, и снова функциональность лежит за этим слоем.

functionality: моя программная функциональность, которая решает какую-то цель, с которой мы описываем этот интерфейс.

existing entities: функции, интерфейсы (массив функций).

consumer: другая программа / приложение.

Бинарный интерфейс приложения (ABI) Здесь начинается моя проблема.

functionality: ???

existing entities: ???

consumer: ???

  • Я написал программное обеспечение на разных языках и предоставлял различные типы интерфейсов (CLI, GUI и API), но я не уверен, если бы я когда-либо предоставлял ABI.

Википедия говорит:

ABI охватывают такие детали, как

  • тип данных, размер и выравнивание;
  • вызывающее соглашение, которое контролирует, как передаются аргументы функций, и возвращает полученные значения;
  • номера системных вызовов и как приложение должно выполнять системные вызовы в операционной системе;

Другие ABI стандартизируют такие детали, как

  • сглаживание имени C ++,
  • распространение исключений и
  • вызывая соглашение между компиляторами на той же платформе, но не требует межплатформенной совместимости.
  • Кому нужны эти детали? Пожалуйста, не говорите ОС. Я знаю программирование сборки. Я знаю, как работают ссылки и загрузка. Я знаю, что именно происходит внутри.

  • Почему появилось имя C ++? Я думал, что мы говорим на двоичном уровне. Почему появляются языки?

Во всяком случае, я загрузил [PDF] System V Application Binary Interface Edition 4.1 (1997-03-18), чтобы узнать, что именно он содержит. Ну, большая часть этого не имела никакого смысла.

  • Почему он содержит две главы (4-й и 5-й) для описания формата файла ELF ? Фактически, это только две важные главы этой спецификации. Остальные главы «специфичны для процессора». Во всяком случае, я думал, что это совершенно другая тема. Пожалуйста, не говорите, что спецификации формата файлов ELF - это ABI. Он не может быть интерфейсом в соответствии с определением.

  • Я знаю, поскольку мы говорим на таком низком уровне, он должен быть очень конкретным. Но я не уверен, как это специфическая «архитектура набора инструкций (ISA)»?

  • Где я могу найти ABI Microsoft Windows?

Итак, это основные запросы, которые меня прослушивают.




Бинарный интерфейс приложения (ABI) похож на API, но функция недоступна для вызывающего абонента на уровне исходного кода. Доступно / доступно только двоичное представление.

ABI могут быть определены на уровне архитектуры процессора или на уровне ОС. ABI - это стандарты, которым должна следовать фаза кода-генератора компилятора. Стандарт фиксируется либо ОС, либо процессором.

Функциональность. Определите механизм / стандарт для выполнения вызовов функций независимо от языка реализации или конкретного компилятора / компоновщика / инструментальной цепочки. Предоставьте механизм, который позволяет JNI, или интерфейс Python-C, и т. Д.

Существующие объекты: функции в форме машинного кода.

Потребитель: другая функция (в том числе одна на другом языке, скомпилированная другим компилятором или связанная другим компоновщиком).




Позвольте мне хотя бы ответить на часть вашего вопроса. На примере того, как Linux ABI влияет на системные символы и почему это полезно.

Системный вызов - это способ для программы пользовательского пространства запросить ядерное пространство для чего-то. Он работает, помещая числовой код для вызова и аргумент в определенный регистр и вызывая прерывание. Чем переключается на kernelspace, и ядро ​​просматривает числовой код и аргумент, обрабатывает запрос, возвращает результат обратно в регистр и запускает переход обратно в пользовательское пространство. Это необходимо, например, когда приложение хочет выделить память или открыть файл (syscalls «brk» и «open»).

Теперь в syscalls есть короткие имена «brk» и т. Д. И соответствующие коды операций, они определены в системном файле заголовка. Пока эти коды операций остаются неизменными, вы можете запускать одни и те же скомпилированные программы пользовательских программ с разными обновленными ядрами без перекомпиляции. Таким образом, у вас есть интерфейс, используемый прекомпилированными двоичными файлами, следовательно, ABI.




Чтобы вызвать код в разделяемых библиотеках или вызвать код между единицами компиляции, объектный файл должен содержать метки для вызовов. C ++ управляет именами меток, чтобы обеспечить скрытие данных и разрешить перегруженные методы. Вот почему вы не можете смешивать файлы с разных компиляторов C ++, если они явно не поддерживают один и тот же ABI.




ABI должен быть согласован между вызывающим и вызываемым абонентами, чтобы быть уверенным, что вызов преуспевает. Использование стека, использование регистров, поэтапный запуск стека. Все это самые важные части ABI.




Я также пытался понять ответы ABI, и ответ JesperE был очень полезен.

С очень простой точки зрения мы можем попытаться понять ABI, рассматривая двоичную совместимость.

KDE wiki определяет библиотеку как бинарную совместимость «если динамически связанная программа с прежней версией библиотеки продолжает работать с более новыми версиями библиотеки без необходимости перекомпиляции». Подробнее о динамической компоновке см. Static linking vs dynamic linking

Теперь давайте попробуем взглянуть на самые базовые аспекты, необходимые для того, чтобы библиотека была совместимой с двоичными файлами (при условии, что в библиотеке нет изменений исходного кода):

  1. Архитектура набора команд с одинаковой / обратной совместимостью (инструкции процессора, структура регистра файлов, организация стека, типы доступа к памяти, а также размеры, макет и выравнивание основных типов данных, к которым процессор может напрямую обращаться)
  2. Та же конвенция о вызовах
  3. Это соглашение о сходстве имен (это может потребоваться, если скажем, что программа Fortran должна вызвать некоторую библиотечную функцию C ++).

Конечно, есть много других деталей, но это в основном то, что охватывает ABI.

Более конкретно, чтобы ответить на ваш вопрос, из вышесказанного, можно сделать вывод:

Функциональность ABI: двоичная совместимость

существующие объекты: существующая программа / библиотеки / ОС

потребитель: библиотеки, ОС

Надеюсь это поможет!




Термин 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, которая может быть скрыта внутри библиотеки. Цена, которую вы платите за это, заключается в том, что задействованы операции кучи.




Related