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

287

Я никогда не понимал, что такое 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.

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

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

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

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

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

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

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

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

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

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

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

  • 5
    «Пожалуйста, не говорите, ОС» Компиляторы должны знать ABI. Линкеры должны знать ABI. Ядро должно знать ABI, чтобы настроить программу в ОЗУ для правильной работы. Что касается C ++, см. Ниже, он намеренно превращает метки в тарабарщину из-за перегрузки и закрытых методов, и компоновщик и любой другой компилятор должны иметь совместимое искажение имен для работы с ним, другими словами, тот же ABI.
  • 0
    "как это" архитектура набора инструкций (ISA) "конкретный?" Вы не можете запустить исполняемый файл x86 на процессоре ARM, точка. Таким образом, ABI, используемый в системе x86, не может быть тем же, который используется в ARM.
Показать ещё 10 комментариев
Теги:
binary
operating-system
abi
compiler-construction

13 ответов

319
Лучший ответ

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

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

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

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

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

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

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

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

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

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

  • 7
    Для других пользователей: также прочитайте ответ JesperE.
  • 1
    @bta, спасибо за отличный ответ. Является ли соглашение о вызовах своего рода ABI? Спасибо
Показать ещё 3 комментария
79

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

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

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

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

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

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

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

РЕДАКТИРОВАТЬ: Некоторые примечания для пояснения:

  • "Binary" в ABI не исключает использования строк или текста. Если вы хотите связать DLL, экспортирующую класс С++, где-то в ней должны быть закодированы методы и типы подписей. Это то, что происходит с именем С++.
  • Причина, по которой вы никогда не предоставляли ABI, заключается в том, что подавляющее большинство программистов никогда этого не сделает. ABI предоставляются теми же людьми, которые разрабатывают платформу (то есть операционную систему), и очень немногие программисты когда-либо получат привилегии для разработки широко используемого ABI.
  • 0
    Я совсем не уверен, что мой шаблон неисправен. Потому что каждый, где этот шаблон для интерфейса верен. Итак, да, я хочу, чтобы я ожидал, что ABI также вписывается в этот шаблон, но это не так. ВАЖНО, я до сих пор не понимаю. Я не знаю, настолько ли я глуп или что-то еще, но это просто не приходит мне в голову. Я не могу понять ответы и статью вики.
  • 1
    @jesperE, «ABI управляет такими вещами, как, как параметры передаются, где возвращаются значения». Это относится к «cdecl, stdcall, fastcall, pascal», верно?
Показать ещё 2 комментария
14

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

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

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

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

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

  • 0
    Почему ABI определяется архитектурой? Почему разные ОС на одной архитектуре не могут определять разные ABI?
8

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

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

Потребитель: компилятор, сборщики, операционная система, архитектура процессора.

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

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

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

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

В Windows есть несколько ABI - например, fastcall и stdcall - это два распространенных ABI. Сценарий ABI снова отличается.

7

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

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

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

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

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

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

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

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

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

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

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

Существует много различных соглашений ABI/call. Вот некоторые из них:

  • Для процессора x86 или x86-64 (32-разрядная среда):
    • Cdecl
    • STDCALL
    • азЬсаИ
    • VECTORCALL
    • THISCALL
  • Для процессора x86-64 (64-разрядная среда):
    • SystemV
    • MSNATIVE
    • VECTORCALL
  • Для процессора ARM (32-разрядный)
    • AAPCS
  • Для процессора ARM (64-разрядная версия)
    • AAPCS64

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

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

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

6

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

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

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

4

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

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

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, МОГУТ иметь возможность звонить друг другу. Но необходимо соблюдать дополнительное требование SAME ABI.

Например, Windows была совместима с POSIX API:

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

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

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

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

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

  • 0
    Соглашение о вызовах Windows x86-64 не использует соглашение о вызовах SysV, которое используют все другие операционные системы x86-64. Linux / OS X / FreeBSD имеют одинаковое соглашение о вызовах, но не имеют полного ABI. ABI ОС включает номера системных вызовов. например, freebsd.org/doc/en_US.ISO8859-1/books/developers-handbook/… говорит, что SYS_execve - 11 для 32-битного Linux, но 59 для FreeBSD.
  • 0
    спасибо за ваш комментарий, я изменил свой комментарий, чтобы лучше ответить на разницу между ABI и API.
Показать ещё 4 комментария
3

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

2

Резюме

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

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

Определение интерфейса: ABI, API

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

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

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

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

Примеры

Пример использования Java

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

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

Виртуальная машина Dalvik нарушила ABI

Для Dalvik VM нужен другой тип байт-кода, чем байт-код 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 vms: Oracle one, IBM one, open Java или любой другой; и второй ABI - тот, что вокруг Dalvik.

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

Scala не имеет бинарной совместимости между младшими версиями Scala: 2.X. По этой причине один и тот же 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) для всех других версий, в то время как JVM 4 не знает, как запускать код, предназначенный для JVM 5. Все это, пока у вас есть одна joda-библиотека. Эта несовместимость пролетает над радаром благодаря различным решениям:

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

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

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

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

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

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

API-интерфейсы, перенесенные через языки

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

1

Бинарный интерфейс приложения (ABI)

Функциональность:

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

Существующие объекты:

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

потребитель:

  • Компилятор языка, сборщик...

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

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

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

Для приложения Windows RT Store попробуйте выполнить поиск ARM ABI, если вы действительно хотите, чтобы какая-то команда сборки инструментов работала вместе.

1

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

0

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

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

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

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

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

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

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

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

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

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

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

0

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

Ещё вопросы

Сообщество Overcoder
Наверх
Меню