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

org.apache.commons.io.FilenameUtils версия 2.4 дает следующий ответ

public static String removeExtension(String filename) {
    if (filename == null) {
        return null;
    }
    int index = indexOfExtension(filename);
    if (index == -1) {
        return filename;
    } else {
        return filename.substring(0, index);
    }
}

public static int indexOfExtension(String filename) {
    if (filename == null) {
        return -1;
    }
    int extensionPos = filename.lastIndexOf(EXTENSION_SEPARATOR);
    int lastSeparator = indexOfLastSeparator(filename);
    return lastSeparator > extensionPos ? -1 : extensionPos;
}

public static int indexOfLastSeparator(String filename) {
    if (filename == null) {
        return -1;
    }
    int lastUnixPos = filename.lastIndexOf(UNIX_SEPARATOR);
    int lastWindowsPos = filename.lastIndexOf(WINDOWS_SEPARATOR);
    return Math.max(lastUnixPos, lastWindowsPos);
}

public static final char EXTENSION_SEPARATOR = '.';
private static final char UNIX_SEPARATOR = '/';
private static final char WINDOWS_SEPARATOR = '\\';
409
задан Dave Mackey 26 February 2019 в 14:43
поделиться

8 ответов

Один из простых способов понять «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.

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

470
ответ дан 22 November 2019 в 23:09
поделиться

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

2
ответ дан 22 November 2019 в 23:09
поделиться

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

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

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

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

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

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

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

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

  • «Двоичный» в ABI не исключает использование строк или текста. Если вы хотите связать DLL экспортирую класс C ++, где-то в нем методы и подписи типа должны быть закодированы. Вот где приходит на имя C ++.
  • Причина, по которой вы никогда не предоставили ABI, так это то, что подавляющее большинство программистов никогда не сделает этого. Abis предоставляются теми же людьми, разработающими платформу (I.E. Операционная система), и очень немногие программисты когда-либо будут иметь привилегию для разработки широко используемых ABI.
135
ответ дан 22 November 2019 в 23:09
поделиться

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

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

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

7
ответ дан 22 November 2019 в 23:09
поделиться

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

Прежде чем мы продолжим, убедитесь, что поведение, продемонстрированное ниже, является тем, чего вы ожидаете от Python.

>>> a1 = [1]
>>> a2 = a1
>>> print a2[0]
1
>>> a1[0] = 2
>>> print a2[0]
2

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

>>> a1 = (1,)
>>> a2 = a1
>>> print a2[0]
1
>>> a1 = (2,)
>>> print a2[0]
1

В этом последнем случае мы заменили весь список, а не обновили его содержание. Для таких неизменяемых типов, как кортежи, это единственное допустимое поведение.

Почему это важно? Допустим, у вас есть дикт:

>>> t1 = (1,2)
>>> d1 = { t1 : 'three' }
>>> print d1
{(1,2): 'three'}
>>> t1[0] = 0  ## results in a TypeError, as tuples cannot be modified
>>> t1 = (2,3) ## creates a new tuple, does not modify the old one
>>> print d1   ## as seen here, the dict is still intact
{(1,2): 'three'}

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

-121--2014932-

К сожалению, увеличение:: поток не является библиотекой «только для заголовка», поэтому его необходимо скомпилировать. Есть два способа обойти его.

  1. Предварительно созданный установочный пакет загружается из boostpro (при условии, что вы находитесь в окнах) -- https://sourceforge.net/projects/boost/files/boost-binaries/
  2. вы можете построить его самостоятельно - см. http://www.boost.org/doc/libs/1_35_0/more/getting_started/index.html
-121--4690784-

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

3
ответ дан 22 November 2019 в 23:09
поделиться

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

1
ответ дан 22 November 2019 в 23:09
поделиться

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

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

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

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

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

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

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

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

10
ответ дан 22 November 2019 в 23:09
поделиться

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

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

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

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

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

17
ответ дан 22 November 2019 в 23:09
поделиться
Другие вопросы по тегам:

Похожие вопросы: