Что такое двоичный интерфейс приложений (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 охватывают такие детали, как

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

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

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

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

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

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

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

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

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

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++ для выполнения name mangling с целью совместимости. То есть, если я создаю библиотеку и вы разрабатываете программу, которая использует библиотеку, вы должны иметь возможность использовать другой компилятор, чем я, и не беспокоиться о том, что результирующие двоичные файлы несовместимы из-за разных схем искажения имен. Это действительно только при определении новый двоичный формат файла или написание компилятора или компоновщика.


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

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

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

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

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

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

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

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

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

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

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

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

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

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


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

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

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

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

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

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

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

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

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

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

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

существует множество различных соглашений 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 имеет несколько ABIs-например, fastcall и stdcall два общее использование ABIs. Syscall ABI снова отличается.


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

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

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


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

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

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 имеет свой собственный ABI:

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. Patch-вам не нужно менять вообще свой код. Библиотека просто исправляет некоторые ошибки.
  2. Minor - вам не нужно изменять свой код, так как дополнения
  3. Major-интерфейс (API) изменен, и вам может потребоваться изменить свой код.

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

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

примеры

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. Все это время у вас есть одна Joda-библиотека. Эта несовместимость летит ниже радара благодаря различным решениям:

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

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

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


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


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


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

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

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

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

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

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

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

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

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

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

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


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

функции:

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

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

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

получатель:

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

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

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

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

для приложения Windows RT Store попробуйте найти 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. Если программист библиотеки добавляет дополнительное поле в 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;
}

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