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


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

это мое мышление о различных интерфейсах:

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

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

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

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

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

existing entities: команды

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

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

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

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

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

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

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

existing entities: функции, Интерфейс(набор функций).

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

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

functionality: ???

existing entities: ???

consumer: ???

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

в Википедии написано:

детали крышки ABIs как

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

другое Абис стандартизировать детали, такие как

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

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

в любом случае, я скачал [PDF] двоичный интерфейс приложения System V выпуск 4.1 (1997-03-18) чтобы увидеть, что именно он содержит. Ну, большинство из них ничего не сделали чувство.

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

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

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

Итак, это основные вопросы, которые меня беспокоят.

14 331

14 ответов:

один простой способ понять " ABI "- сравнить его с"API".

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

ABI очень похож. Подумайте об этом как о скомпилированной версии API (или как API на уровень машинного языка). При написании исходного кода Вы получаете доступ к библиотеке через API. После компиляции кода ваше приложение получает доступ к двоичным данным в библиотеке через ABI. ABI определяет структуры и методы, которые ваше скомпилированное приложение будет использовать для доступа к внешней библиотеке (как и API), только на более низком уровне.

ABIs важны, когда речь заходит о приложениях, которые используют внешние библиотеки. Если программа построена для использования конкретного библиотека и эта библиотека позже обновляется, вы не хотите повторно компилировать это приложение (и с точки зрения конечного пользователя у вас может не быть источника). Если обновленная библиотека использует тот же ABI, то ваша программа не будет нуждаться в изменении. Интерфейс к библиотеке (это все, о чем действительно заботится ваша программа) является тем же самым, хотя внутренняя работа, возможно, изменилась. Две версии библиотеки, которые имеют один и тот же ABI, иногда называются "двоично совместимыми", поскольку они имеют тот же низкоуровневый интерфейс (вы должны иметь возможность заменить старую версию на новую и не иметь никаких серьезных проблем).

иногда изменения ABI неизбежны. Когда это происходит, любые программы, использующие эту библиотеку, не будут работать, если они не будут повторно скомпилированы для использования новой версии библиотеки. Если ABI изменяется, но API не делает, то старые и новые версии библиотеки иногда называются "совместимыми с исходным кодом". Это означает, что в то время как программа скомпилирована для одного версия библиотеки не будет работать с другой, исходный код, написанный для одного, будет работать для другого, если он будет повторно скомпилирован.

по этой причине авторы библиотек, как правило, пытаются сохранить их ABI стабильным (чтобы свести к минимуму нарушения). Сохранение стабильности ABI означает, что не изменяются интерфейсы функций (тип и число возвращаемых значений, типы и порядок аргументов), определения типов данных или структур данных, определенные константы и т. д. Можно добавлять новые функции и типы данных, но существующие должны оставаться неизменными. Если вы развернете, скажем, 16-разрядное поле структуры данных в 32-разрядное поле, то уже скомпилированный код, который использует эту структуру данных, не будет правильно обращаться к этому полю (или любому следующему за ним). Доступ к элементам структуры данных преобразуется в адреса памяти и смещения во время компиляции, и если структура данных изменяется, то эти смещения не будут указывать на то, что код ожидает от них, и результаты в лучшем случае непредсказуемы.

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

Edit: Что касается вашего вопроса о главах, касающихся формата файла ELF в документах SysV ABI: причина, по которой эта информация включена, заключается в том, что формат ELF определяет интерфейс между операционная система и приложение. Когда вы говорите ОС запускать программу, она ожидает, что программа будет отформатирована определенным образом и (например) ожидает, что первый раздел двоичного файла будет заголовком ELF, содержащим определенную информацию при определенных смещениях памяти. Это то, как приложение передает важную информацию о себе в операционную систему. Если вы создадите программу в двоичном формате, отличном от Elf (например, A.out или PE), то ОС, которая ожидает, что приложения в формате ELF будут невозможно интерпретировать двоичный файл или запустить приложение. Это одна из главных причин, по которой приложения Windows не могут быть запущены непосредственно на машине Linux (или наоборот) без повторной компиляции или запуска внутри какого-либо уровня эмуляции, который может переводиться из одного двоичного формата в другой.

IIRC, Windows в настоящее время использует Портативный Исполняемый Файл (или, PE) формат. Есть ссылки в разделе "внешние ссылки" этой страницы Википедии с более подробной информацией о PE формата.

кроме того, что касается вашей заметки о c++ name mangling: ABI может определить "стандартизированный" способ для компилятора C++ делать имя mangling с целью совместимости. То есть, если я создаю библиотеку, и вы разрабатываете программу, которая использует библиотеку, вы должны иметь возможность использовать другой компилятор, чем я, и не беспокоиться о том, что полученные двоичные файлы несовместимы из-за разных схем искажения имен. Это действительно только при определении новый двоичный формат файла или написание компилятора или компоновщика.

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

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

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

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

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

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

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

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

  • "Binary" в ABI не исключает использование строк или текста. Если вы хотите связать DLL, экспортирующую класс C++, где-то в нем должны быть закодированы методы и сигнатуры типов. Вот где C++ name-mangling входит.
  • причина, по которой вы никогда не предоставляли ABI, заключается в том, что подавляющее большинство программистов никогда этого не сделают. Абис предоставляются одними и теми же людьми проектирование платформы (т. е. операционной системы), и очень немногие программисты когда-либо будут иметь привилегию проектировать широко используемый ABI.

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

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

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

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

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

ты на самом деле не нужен ABI вообще, если--

  • ваша программа не имеет функции, и--
  • ваша программа представляет собой один исполняемый файл, который работает в одиночку (т. е. встроенная система), где это буквально единственное, что работает, и ему не нужно говорить ни с чем другим.

упрощенное резюме:

API: " вот все функции, которые вы можете вызов."

ABI:"Это как для вызова функции."

ABI-это набор правил, которых придерживаются компиляторы и компоновщики для компиляции вашей программы, чтобы она работала правильно. ABIs охватывают несколько тем:

  • возможно, самая большая и самая важная часть ABI-это вызов процедуры стандартные иногда известный как"соглашение о вызове". Зовущий соглашения стандартизируют способ перевода "функций" в ассемблерный код.
  • ABIs также диктуют, как имена из открытых функций в библиотеках должны быть представлены так, чтобы другой код мог вызывать эти библиотеки и знать, какие аргументы должны быть переданы. Это называется "искажение имени".
  • ABIs также диктуют, какие типы данных могут быть использованы, как они должны быть выровнены, и другие детали низкого уровня.

более глубокий взгляд на вызывая соглашение, которое я считаю ядром ABI:

сама машина не имеет понятия "функции". Когда вы пишете функцию на языке высокого уровня, таком как c, компилятор генерирует строку кода сборки, такую как _MyFunction1:. Это же метка, который в конечном итоге будет разрешен в адрес ассемблером. Эта метка отмечает "начало" вашей "функции"в коде сборки. В высокоуровневом коде, когда вы "вызываете" эту функцию, что вы на самом деле это приводит к тому, что процессор прыжок по адресу этой метки и продолжить выполнение там.

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

  • во-первых, компилятор вставляет немного кода сборки для сохранения текущего адреса, так что когда ваша "функция" будет выполнена, процессор может вернуться в нужное место и продолжить выполнение.
  • далее компилятор генерирует ассемблерный код для передачи аргументов.
    • некоторые соглашения о вызовах диктовать, что аргументы должны быть помещены в стек (в особом порядке конечно).
    • другие соглашения диктуют, что аргументы должны быть помещены в определенные регистры (в зависимости от их типов данных конечно).
    • еще другие соглашения диктуют, что определенная комбинация стека и следует использовать регистры.
  • конечно, если раньше в этих регистрах было что-то важное, эти значения теперь перезаписываются и теряются навсегда, поэтому некоторые соглашения о вызовах могут диктовать, что компилятор должен сохранить некоторые из этих регистров, прежде чем помещать в них аргументы.
  • теперь компилятор вставляет команду перехода, сообщающую процессору перейти к этой метке, которую он сделал ранее (_MyFunction1:). На этом этапе вы можете считать, что процессор находится " в" ваша функция."
  • в конце функции компилятор помещает некоторый код сборки, который заставит процессор записать возвращаемое значение в правильное место. Соглашение о вызове будет определять, следует ли возвращаемое значение помещать в определенный регистр (в зависимости от его типа) или в стек.
  • теперь пришло время для очистки. Соглашение о вызове определяет, куда компилятор помещает код сборки очистки.
    • некоторые конвенции говорят, что вызывающий должен очистить стек. Это означает, что после того, как "функция" будет выполнена, и процессор вернется туда, где он был раньше, самый следующий код для выполнения должен быть очень специфичным кодом очистки.
    • другие соглашения говорят, что некоторые конкретные части кода очистки должны быть в конце "функции" до прыжок назад.

существует много различных соглашений ABIs / calling. Некоторые основные из них являются:

  • для процессора x86 или x86-64 (32-разрядная среда):
    • CDECL
    • нарушением соглашения о стандартном
    • FASTCALL
    • VECTORCALL
    • THISCALL
  • для процессора x86-64 (64-разрядная среда):
    • SYSTEMV
    • MSNATIVE
    • VECTORCALL
  • для процессора ARM (32-разрядный)
    • AAPCS
  • для процессора ARM (64-битовый)
    • AAPCS64

здесь это отличная страница, которая на самом деле показывает различия в сборке, созданной при компиляции для разных ABIs.

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

ваш компилятор понимает, как вызывать библиотечные функции очень важно. На размещенной платформе (то есть там, где ОС загружает программы) Ваша программа даже не может мигать без вызова ядра.

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

существующие сущности: расположение параметров, семантика функций, распределение регистров. Например, архитектура ARM имеет множество ABIs (APCS, EABI, GNU-EABI, не говоря уже о куче исторических случаев) - использование смешанного ABI приведет к тому, что ваш код просто не будет работать при вызове через границы.

потребитель: компилятор, авторы сборок, операционная система, специфическая архитектура процессора.

кому нужны эти подробности? Компилятор, авторы сборок, компоновщики, которые выполняют генерацию кода (или требования к выравниванию), операционная система (обработка прерываний, интерфейс syscall). Если вы делали ассемблерное Программирование, вы были соответствующие ABI!

C++ name mangling-это особый случай - его проблема с компоновщиком и динамическим компоновщиком - если имя mangling не стандартизировано, то динамическое связывание не будет работать. Отныне C++ ABI называется именно так, C++ ABI. Это не проблема уровня компоновщика,а проблема генерации кода. Если у вас есть двоичный файл C++, невозможно сделать его совместимым с другим C++ ABI (искажение имени, обработка исключений) без перекомпиляции из источник.

ELF-это формат файла для использования загрузчика и динамического компоновщика. ELF-это формат контейнера для двоичного кода и данных, и как таковой определяет ABI части кода. Я бы не рассматривал ELF как ABI в строгом смысле, поскольку исполняемые файлы PE не являются ABI.

все Абис специфический набор инструкций. ARM ABI не будет иметь смысла на процессоре MSP430 или x86_64.

в Windows есть несколько Абис - например, азьсаи и нарушением соглашения о стандартном два Абис общего пользования. Системных вызовов Аби снова отличается.

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

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

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

лучший способ различать ABI и API-это знать, почему и для чего он используется:

для x86-64 обычно есть один ABI (а для x86 32-бит есть другой набор):

http://www.x86-64.org/documentation/abi.pdf

https://developer.apple.com/library/mac/documentation/DeveloperTools/Conceptual/LowLevelABI/140-x86-64_Function_Calling_Conventions/x86_64.html

http://people.freebsd.org / ~obrien / amd64-elf-abi. pdf

Linux + FreeBSD + MacOSX следуют за ним с некоторыми небольшими вариациями. И Windows x64 имеют свои собственные Аби:

http://eli.thegreenplace.net/2011/09/06/stack-frame-layout-on-x86-64/

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

но API разные:

это имена функций высокого уровня, с определенным аргументом, таким образом, что если разные части программного обеспечения строятся с использованием этих API, могут быть в состоянии вызывать друг друга. Но дополнительное требование того же ABI должно быть соблюдено.

например, Windows раньше была POSIX API уступчивый:

https://en.wikipedia.org/wiki/Windows_Services_for_UNIX

https://en.wikipedia.org/wiki/POSIX

и Linux также совместим с POSIX. Но двоичные файлы нельзя просто переместить и запустить немедленно. Но поскольку они использовали одни и те же имена в совместимом API POSIX, вы можете взять одно и то же программное обеспечение на C, перекомпилировать его в другой ОС и сразу же запустить его.

API предназначены для облегчения интеграции программного обеспечения-стадии предварительной компиляции. Поэтому после компиляции программное обеспечение может выглядеть совершенно по - другому-если ABI разные.

ABI предназначены для определения точной интеграции программного обеспечения на двоичном / сборочном уровне.

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

резюме

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

на мой взгляд, ABI-это субъективно того, что считается заданной/платформой для конкретного API. ABI - это "остальные" соглашения, которые "не изменятся" для конкретного API или которые будут рассмотрены средой выполнения: исполнителями, инструментами, компоновщиками, компиляторами, jvm и ОС.

задание интерфейс: ABI, API

если вы хотите использовать библиотеку, такую как joda-time, вы должны объявить зависимость от joda-time-<major>.<minor>.<patch>.jar. Библиотека следует рекомендациям и использует Семантическое Управление Версиями. Это определяет совместимость API на трех уровнях:

  1. патч-вам не нужно менять вообще свой код. Библиотека просто исправляет некоторые ошибки.
  2. Minor - вам не нужно менять свой код с момента дополнения
  3. Major-интерфейс (API) изменен, и вам может потребоваться изменить свой код.

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

  • двоичный язык, используемый для библиотек (в случаях Java целевая версия JVM, которая определяет байт-код Java)
  • соглашения о вызове
  • JVM conventions
  • связывание условности
  • соглашения о времени выполнения Все это определяется и управляется инструментами, которые мы используем.

примеры

Java case study

например, Java стандартизировала все эти соглашения не в инструменте, а в формальной спецификации JVM. Спецификация позволила другим поставщикам предоставить другой набор инструментов, которые могут выводить совместимые библиотеки.

Java предоставляет два других интересных тематических исследования для ABI: Scala версии и Далвик виртуальная машина.

виртуальная машина Dalvik сломала ABI

виртуальная машина Dalvik нуждается в другом типе байт-кода, чем байт-код Java. Библиотеки Dalvik получаются путем преобразования байт-кода Java (с тем же API) для Dalvik. Таким образом, вы можете получить две версии одного и того же API: определенный оригиналом joda-time-1.7.2.jar. Мы могли бы позвонить мне joda-time-1.7.2.jar и joda-time-1.7.2-dalvik.jar. Они используют другой ABI для стандартных Java-виртуальных машин, ориентированных на стек: Один Oracle, один IBM, open Java или любой другой; а второй ABI-это тот, который вокруг Dalvik.

последовательные релизы Scala несовместимы

Scala не имеет двоичной совместимости между младшими версиями Scala: 2.Икс. По этой причине тот же API " io.reactivex "% % "rxscala" % "0.26.5" имеет три версии (в будущем больше): для Scala 2.10, 2.11 и 2.12. Что изменилось? пока не знаю, но двоичные файлы не совместимы. Наверное последние версии добавляют вещи, которые делают библиотеки непригодными для использования на старых виртуальных машинах, вероятно, связанные с соглашениями о связывании/именовании/параметрах.

последовательные выпуски Java несовместимы

Java также имеет проблемы с основными выпусками JVM: 4,5,6,7,8,9. Они предлагают только обратную совместимость. Jvm9 знает, как запустить код, скомпилированный/целевых (javac и по -target option) для всех других версий, в то время как JVM 4 не знает, как запустить код, предназначенный для JVM 5. Все это пока у вас есть одна Джода-библиотека. Эта несовместимость летит ниже радара благодаря различным решениям:

  1. семантическое управление версиями: когда библиотеки нацелены на более высокую JVM, они обычно меняют основную версию.
  2. используйте JVM 4 в качестве ABI, и вы в безопасности.
  3. Java 9 добавляет спецификацию о том, как вы можете включить байт-код для конкретной целевой JVM в той же библиотеке.

почему я начал с API определение?

API и ABI-это просто соглашения о том, как вы определяете совместимость. Нижние слои являются родовыми в отношении множества семантики высокого уровня. Вот почему легко сделать некоторые соглашения. Первый вид соглашений касается выравнивания памяти, байтового кодирования, соглашений о вызовах, больших и малых конечных кодировках и т. д. Поверх них вы получаете исполняемые соглашения, как и другие описанные, связывающие соглашения,промежуточный байт-код как один используется Java или LLVM IR используется GCC. В-третьих, вы получаете соглашения о том, как найти библиотеки, как их загрузить (см. загрузчики классов Java). По мере того, как вы поднимаетесь все выше и выше в концепциях, у вас появляются новые соглашения, которые вы рассматриваете как данность. Вот почему они не добрались до семантическое управление версиями. Они неявны или свернуты в майор версия. Мы могли бы изменить семантическое управление версиями с помощью <major>-<minor>-<patch>-<platform/ABI>. Это то, что на самом деле уже происходит: платформа уже является rpm,dll,jar (байт-код JVM), war(jvm + web server),apk,2.11 (конкретная версия Scala) и так далее. Когда вы говорите APK, вы уже говорите о конкретной части ABI вашего API.

API можно портировать на разные ABI

верхний уровень абстракции (источники, написанные против самого высокого API, могут быть перекомпилированы / портированы на любую другую абстракцию более низкого уровня.

допустим, у меня есть некоторые источники для rxscala. Если инструменты Скала изменить я могу перекомпилировать их к этому. Если изменения JVM я мог бы иметь автоматические преобразования из старой машины в новую, не беспокоясь о концепциях высокого уровня. В то время как портирование может быть трудно поможет любому другому клиенту. Если новая операционная система создается с использованием совершенно другого ассемблерного кода, можно создать транслятор.

API, портированные между языками

есть API, которые портируются на нескольких языках, таких как реактивные потоки. В общем случае они определяют сопоставления с конкретными языками / платформами. Я бы сказал, что API-это основная спецификация, формально определенная на человеческом языке или даже на конкретном языке программирования. Все остальные "сопоставления" являются ABI в некотором смысле, иначе больше API, чем обычный ABI. То же самое происходит и с остальными интерфейсами.

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

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

Я также пытался понять Аби и ответ Джеспера был очень полезным.

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

KDE wiki определяет библиотеку как двоичную совместимую ", если программа, динамически связанная с предыдущей версией библиотеки, продолжает работать с более новыми версиями библиотеки без необходимости перекомпиляции."Для получения дополнительной информации о динамической компоновке см. статическое соединение против динамического связывание

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

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

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

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

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

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

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

надеюсь, что это помогает!

двоичный интерфейс приложений (ABI)

функции:

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

Действующие лица:

  • логические блоки, непосредственно участвующие в выполнении программы: ALU, регистры общего назначения, регистры для отображения памяти / ввода / вывода ввода/вывода и др...

получатель:

  • компоновщик языковых процессоров, ассемблер...

Они необходимы тем, кто должен гарантировать, что цепочки инструментов сборки работают в целом. Если вы пишете один модуль на ассемблере, другой на Python, а вместо собственного загрузчика хотите использовать операционную систему, то ваши "прикладные" модули работают через "бинарные" границы и требуют согласования такого "интерфейса".

искажение имен C++, потому что объектные файлы из разных языков высокого уровня могут потребоваться для связи в вашем приложение. Рассмотрите возможность использования стандартной библиотеки GCC для системных вызовов Windows, построенной с помощью Visual C++.

ELF является одним из возможных ожиданий компоновщика из объектного файла для интерпретации, хотя JVM может иметь какую-то другую идею.

для приложения Магазина Windows RT попробуйте найти ARM 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, и кто-то использует новый двоичный файл библиотеки со старым двоичным файлом приложения, то библиотека может сделать доступ к памяти вне границ.

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;
}

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