¿Qué es una interfaz binaria de aplicación (ABI)?

Nunca entendí claramente qué es un ABI. Por favor, no me señale un artículo de Wikipedia. Si pudiera entenderlo, no estaría aquí publicando una publicación tan larga.

Esta es mi forma de pensar acerca de las diferentes interfaces:

Un control remoto de TV es una interfaz entre el usuario y el televisor. Es una entidad existente, pero inútil (no proporciona ninguna funcionalidad) por sí mismo. Toda la funcionalidad para cada uno de esos botones en el control remoto se implementa en el televisor.

Interfaz: es una capa de “entidad existente” entre la functionality y el consumer de esa funcionalidad. Una interfaz en sí misma no hace nada. Simplemente invoca la funcionalidad subyacente.

Ahora, dependiendo de quién sea el usuario, hay diferentes tipos de interfaces.

Los comandos de la interfaz de línea de comandos (CLI) son las entidades existentes, el consumidor es el usuario y la funcionalidad se encuentra detrás.

functionality: mi funcionalidad de software que resuelve algún propósito para el cual estamos describiendo esta interfaz.

existing entities: comandos

consumer: usuario

La ventana de la interfaz gráfica de usuario (GUI) , los botones, etc. son las entidades existentes, y de nuevo el consumidor es el usuario y la funcionalidad se encuentra detrás.

functionality: mi funcionalidad de software que resuelve algún propósito para el cual estamos describiendo esta interfaz.

existing entities: ventana, botones, etc.

consumer: usuario

Las funciones de la interfaz de progtwigción de aplicaciones (API) o, para ser más correctas, las interfaces (en la progtwigción basada en interfaces) son las entidades existentes, el consumidor aquí es otro progtwig, no un usuario, y nuevamente la funcionalidad se encuentra detrás de esta capa.

functionality: mi funcionalidad de software que resuelve algún propósito para el cual estamos describiendo esta interfaz.

existing entities: funciones, interfaces (conjunto de funciones).

consumer: otro progtwig / aplicación.

Application Binary Interface (ABI) Aquí es donde comienza mi problema.

functionality: ???

existing entities: ???

consumer: ???

  • He escrito software en diferentes idiomas y proporcioné diferentes tipos de interfaces (CLI, GUI y API), pero no estoy seguro, si alguna vez, proporcioné alguna ABI.

Wikipedia dice:

ABI cubre detalles tales como

  • tipo de datos, tamaño y alineación;
  • la convención de llamadas, que controla cómo se pasan los argumentos de las funciones y se recuperan los valores de retorno;
  • los números de llamada del sistema y cómo una aplicación debe hacer llamadas al sistema operativo del sistema;

Otros ABI estandarizan detalles tales como

  • el cambio de nombre de C ++,
  • propagación de excepción, y
  • convocatoria de convención entre comstackdores en la misma plataforma, pero no requiere compatibilidad multiplataforma.
  • ¿Quién necesita estos detalles? Por favor, no digas el SO. Sé progtwigción de ensamblaje. Sé cómo funciona el enlace y la carga. Sé lo que sucede exactamente dentro.

  • ¿Por qué entraron en juego los nombres C ++? Pensé que estamos hablando a nivel binario. ¿Por qué entran los idiomas?

De todos modos, he descargado la aplicación [B] System V Application Binary Interface Edition 4.1 (1997-03-18) para ver exactamente qué contiene. Bueno, la mayor parte no tenía ningún sentido.

  • ¿Por qué contiene dos capítulos (4to y 5to) para describir el formato de archivo ELF ? De hecho, estos son los únicos dos capítulos importantes de esa especificación. El rest de los capítulos son “específicos del procesador”. De todos modos, pensé que era un tema completamente diferente. Por favor, no diga que las especificaciones de formato de archivo ELF son el ABI. No califica para ser una interfaz de acuerdo con la definición.

  • Lo sé, ya que estamos hablando a un nivel tan bajo que debe ser muy específico. Pero no estoy seguro de cómo es específico “architecture de conjunto de instrucciones (ISA)”.

  • ¿Dónde puedo encontrar el ABI de Microsoft Windows?

Entonces, estas son las principales consultas que me molestan.

Una manera fácil de entender “ABI” es compararlo con “API”.

Ya estás familiarizado con el concepto de una API. Si desea usar las características de, por ejemplo, alguna biblioteca o su sistema operativo, usará una API. La API consta de tipos de datos / estructuras, constantes, funciones, etc. que puede usar en su código para acceder a la funcionalidad de ese componente externo.

Un ABI es muy similar. Piense en ello como la versión comstackda de una API (o como una API en el nivel de lenguaje de máquina). Cuando escribe código fuente, accede a la biblioteca a través de una API. Una vez que se comstack el código, su aplicación accede a los datos binarios en la biblioteca a través de la ABI. El ABI define las estructuras y los métodos que su aplicación comstackda usará para acceder a la biblioteca externa (al igual que la API), solo en un nivel inferior.

Los ABI son importantes cuando se trata de aplicaciones que usan bibliotecas externas. Si un progtwig está diseñado para usar una biblioteca en particular y esa biblioteca se actualiza más tarde, no desea tener que volver a comstackr esa aplicación (y desde el punto de vista del usuario final, es posible que no tenga la fuente). Si la biblioteca actualizada usa el mismo ABI, entonces su progtwig no necesitará cambiar. La interfaz de la biblioteca (que es lo que realmente le importa a su progtwig) es la misma aunque el funcionamiento interno haya cambiado. Dos versiones de una biblioteca que tienen la misma ABI a veces se denominan “compatibles con binarios” ya que tienen la misma interfaz de bajo nivel (debe poder reemplazar la versión anterior por la nueva y no tener mayores problemas).

A veces, los cambios de ABI son inevitables. Cuando esto sucede, los progtwigs que usan esa biblioteca no funcionarán a menos que se vuelvan a comstackr para usar la nueva versión de la biblioteca. Si el ABI cambia pero la API no lo hace, las versiones de la biblioteca vieja y nueva a veces se llaman “fuente compatible”. Esto implica que, aunque un progtwig comstackdo para una versión de biblioteca no funcionará con el otro, el código fuente escrito para uno funcionará para el otro si se vuelve a comstackr.

Por esta razón, los escritores de bibliotecas tienden a tratar de mantener su ABI estable (para minimizar la interrupción). Mantener un ABI estable significa no cambiar las interfaces de funciones (tipo de retorno y número, tipos y orden de argumentos), definiciones de tipos de datos o estructuras de datos, constantes definidas, etc. Se pueden agregar nuevas funciones y tipos de datos, pero los existentes deben permanecer lo mismo. Si expande, por ejemplo, un campo de estructura de datos de 16 bits en un campo de 32 bits, entonces el código ya comstackdo que usa esa estructura de datos no accederá a ese campo (o a cualquier otro que lo siga) correctamente. El acceso a los miembros de la estructura de datos se convierte en direcciones de memoria y compensaciones durante la comstackción y, si la estructura de datos cambia, estos desplazamientos no apuntarán a lo que el código espera que apunten y los resultados serán impredecibles en el mejor de los casos.

Un ABI no es necesariamente algo que usted proporcionará explícitamente a menos que esté esperando que las personas interactúen con su código mediante ensamblaje. Tampoco es específico del idioma, ya que (por ejemplo) una aplicación C y una aplicación Pascal usarán el mismo ABI después de que se hayan comstackdo.

Editar: Con respecto a su pregunta sobre los capítulos sobre el formato de archivo ELF en los documentos SysV ABI: El motivo por el que se incluye esta información es porque el formato ELF define la interfaz entre el sistema operativo y la aplicación. Cuando le dice al sistema operativo que ejecute un progtwig, espera que el progtwig se formatee de cierta manera y (por ejemplo) espera que la primera sección del binario sea un encabezado ELF que contiene cierta información en desplazamientos de memoria específicos. Así es como la aplicación comunica información importante acerca de sí mismo al sistema operativo. Si construye un progtwig en un formato binario que no sea ELF (como a.out o PE), entonces un SO que espera que las aplicaciones con formato ELF no puedan interpretar el archivo binario o ejecutar la aplicación. Esta es una gran razón por la cual las aplicaciones de Windows no se pueden ejecutar directamente en una máquina Linux (o viceversa) sin volver a comstackrse o ejecutarse dentro de algún tipo de capa de emulación que pueda trasladarse de un formato binario a otro.

IIRC, Windows actualmente usa el formato Portable Executable (o, PE). Hay enlaces en la sección de “enlaces externos” de esa página de Wikipedia con más información sobre el formato PE.

Además, con respecto a su nota sobre el cambio de nombre de C ++: El ABI puede definir una forma “estandarizada” para que un comstackdor de C ++ genere nombres para fines de compatibilidad. Es decir, si creo una biblioteca y desarrollas un progtwig que usa la biblioteca, deberías poder usar un comstackdor diferente del que yo tenía y no tener que preocuparte de que los binarios resultantes sean incompatibles debido a los diferentes esquemas de creación de nombres. Esto realmente solo es útil si está definiendo un nuevo formato de archivo binario o escribiendo un comstackdor o enlazador.

Si conoce el ensamblaje y cómo funcionan las cosas en el nivel del sistema operativo, se está conformando con un determinado ABI. El ABI gobierna cosas como cómo se pasan los parámetros, dónde se colocan los valores de retorno. Para muchas plataformas, solo hay una ABI para elegir, y en esos casos la ABI es solo “cómo funcionan las cosas”.

Sin embargo, el ABI también regula cosas como cómo las clases / objetos se presentan en C ++. Esto es necesario si desea poder pasar referencias de objeto a través de los límites del módulo o si desea mezclar código comstackdo con diferentes comstackdores.

Además, si tiene un sistema operativo de 64 bits que puede ejecutar binarios de 32 bits, tendrá diferentes ABI para códigos de 32 y 64 bits.

En general, cualquier código que vincule al mismo ejecutable debe cumplir con el mismo ABI. Si desea comunicarse entre códigos utilizando diferentes ABI, debe usar algún tipo de RPC o protocolos de serialización.

Creo que estás tratando demasiado de exprimir diferentes tipos de interfaces en un conjunto fijo de características. Por ejemplo, una interfaz no necesariamente tiene que dividirse en consumidores y productores. Una interfaz es solo una convención mediante la cual dos entidades interactúan.

Los ABI pueden ser (parcialmente) ISA-agnósticos. Algunos aspectos (como las convenciones de llamada) dependen del ISA, mientras que otros aspectos (como el diseño de clases C ++) no.

Un ABI bien definido es muy importante para las personas que escriben comstackdores. Sin un ABI bien definido, sería imposible generar código interoperable.

EDITAR: Algunas notas para aclarar:

  • “Binario” en ABI no excluye el uso de cadenas o texto. Si desea vincular una DLL exportando una clase de C ++, en algún lugar de ella los métodos y las firmas de tipo deben estar codificados. Ahí es donde entra en juego la creación de nombres C ++.
  • La razón por la que nunca proporcionó un ABI es que la gran mayoría de los progtwigdores nunca lo hará. Los ABI son proporcionados por las mismas personas que diseñan la plataforma (es decir, el sistema operativo), y muy pocos progtwigdores tendrán el privilegio de diseñar un ABI ampliamente utilizado.

Una interfaz binaria de aplicación (ABI) es similar a una API, pero la función no es accesible para la persona que llama en el nivel del código fuente. Solo una representación binaria es accesible / disponible.

Los ABI se pueden definir en el nivel de architecture del procesador o en el nivel del sistema operativo. Los ABI son estándares a seguir por la fase de generación de código del comstackdor. El estándar lo fija el sistema operativo o el procesador.

Funcionalidad: define el mecanismo / estándar para realizar llamadas a funciones independientemente del lenguaje de implementación o un comstackdor / enlazador / cadena de herramientas específico. Proporcione el mecanismo que permite JNI, o una interfaz Python-C, etc.

Entidades existentes: funciones en forma de código máquina.

Consumidor: otra función (incluida una en otro idioma, comstackda por otro comstackdor o vinculada por otro enlazador).

En realidad , no necesitas un ABI en absoluto si–

  • Tu progtwig no tiene funciones, y–
  • Su progtwig es un solo ejecutable que se ejecuta solo (es decir, un sistema integrado) donde es literalmente lo único que se ejecuta y no necesita hablar con nada más.

Un resumen simplificado:

API: “Aquí están todas las funciones a las que puede llamar”.

ABI: “Así es como llamar a una función”.

El ABI es un conjunto de reglas que los comstackdores y vinculadores cumplen para comstackr su progtwig para que funcione correctamente. ABI cubren múltiples temas:

  • Podría decirse que la parte más grande y más importante de un ABI es el estándar de llamada de procedimiento a veces conocido como la “convención de llamada”. Las convenciones de llamadas estandarizan cómo las “funciones” se traducen a código ensamblador.
  • Los ABI también dictan cómo se deben representar los nombres de las funciones expuestas en las bibliotecas para que otro código pueda llamar a esas bibliotecas y saber qué argumentos se deben pasar. Esto se llama “cambio de nombre”.
  • Los ABI también dictan qué tipo de tipos de datos se pueden usar, cómo se deben alinear y otros detalles de bajo nivel.

Echando un vistazo más profundo a la convocatoria de convenciones, que considero que es el núcleo de un ABI:

La máquina en sí no tiene ningún concepto de “funciones”. Cuando escribe una función en un lenguaje de alto nivel como c, el comstackdor genera una línea de código ensamblador como _MyFunction1: Esta es una etiqueta , que eventualmente se resolverá en una dirección por el ensamblador. Esta etiqueta marca el “inicio” de su “función” en el código de ensamblaje. En el código de alto nivel, cuando “llamas” esa función, lo que realmente estás haciendo es que la CPU salte a la dirección de esa etiqueta y continúe ejecutándose allí.

En preparación para el salto, el comstackdor debe hacer un montón de cosas importantes. La convención de llamadas es como una lista de verificación que el comstackdor sigue para hacer todo esto:

  • Primero, el comstackdor inserta un poco de código ensamblador para guardar la dirección actual, de modo que cuando su “función” esté lista, la CPU pueda volver al lugar correcto y continuar la ejecución.
  • A continuación, el comstackdor genera código de ensamblado para pasar los argumentos.
    • Algunas convenciones de llamadas dictan que los argumentos se deben poner en la stack ( en un orden particular, por supuesto).
    • Otras convenciones dictan que los argumentos deben ponerse en registros particulares ( dependiendo de sus tipos de datos por supuesto).
    • Todavía otras convenciones dictan que se debe usar una combinación específica de stack y registros.
  • Por supuesto, si antes había algo importante en esos registros, esos valores ahora se sobrescriben y pierden para siempre, por lo que algunas convenciones de llamadas pueden dictar que el comstackdor debe guardar algunos de esos registros antes de poner los argumentos en ellos.
  • Ahora el comstackdor inserta una instrucción de salto que le dice a la CPU que vaya a la etiqueta que hizo previamente ( _MyFunction1: . En este punto, puede considerar que la CPU está “en” su “función”.
  • Al final de la función, el comstackdor coloca un código de ensamblaje que hará que la CPU escriba el valor de retorno en el lugar correcto. La convención de llamadas dictará si el valor de retorno debe colocarse en un registro particular (dependiendo de su tipo), o en la stack.
  • Ahora es el momento de la limpieza. La convención de llamadas dictará dónde el comstackdor coloca el código del conjunto de limpieza.
    • Algunas convenciones dicen que la persona que llama debe limpiar la stack. Esto significa que una vez que la “función” está lista y la CPU vuelve al estado anterior, el siguiente código a ser ejecutado debe ser un código de limpieza muy específico.
    • Otras convenciones dicen que algunas partes particulares del código de limpieza deben estar al final de la “función” antes del salto atrás.

Hay muchas convenciones ABI / llamadas diferentes. Algunos principales son:

  • Para la CPU x86 o x86-64 (entorno de 32 bits):
    • CDECL
    • STDCALL
    • FASTCALL
    • VECTORCALL
    • ESTA LLAMADA
  • Para la CPU x86-64 (entorno de 64 bits):
    • SYSTEMV
    • MSNATIVO
    • VECTORCALL
  • Para la CPU ARM (32 bits)
    • AAPCS
  • Para la CPU ARM (64 bits)
    • AAPCS64

Aquí hay una gran página que muestra las diferencias en el ensamblaje generado al comstackr para diferentes ABI.

Otra cosa para mencionar es que un ABI no solo es relevante dentro del módulo ejecutable de su progtwig. También lo usa el vinculador para asegurarse de que su progtwig llame a las funciones de la biblioteca correctamente. Usted tiene múltiples bibliotecas compartidas ejecutándose en su computadora, y siempre que su comstackdor sepa qué ABI usan cada una de ellas, puede invocar las funciones adecuadamente sin necesidad de explotar la stack.

El comstackdor que entiende cómo llamar a las funciones de la biblioteca es extremadamente importante. En una plataforma alojada (es decir, una donde un sistema operativo carga progtwigs), su progtwig ni siquiera puede parpadear sin realizar una llamada al kernel.

Funcionalidad: conjunto de contratos que afectan al comstackdor, los escritores de ensamblaje, el enlazador y el sistema operativo. Los contratos especifican cómo se organizan las funciones, dónde se pasan los parámetros, cómo se pasan los parámetros y cómo funcionan las funciones. Estos son generalmente específicos para una tupla (architecture del procesador, sistema operativo).

Entidades existentes: distribución de parámetros, semántica de funciones, asignación de registros. Por ejemplo, las architectures ARM tienen numerosos ABI (APCS, EABI, GNU-EABI, no importa un montón de casos históricos): si utiliza un ABI mixto, su código simplemente no funcionará cuando llame a través de los límites.

Consumidor: comstackdor, ensambladores, sistema operativo, architecture específica de CPU.

¿Quién necesita estos detalles? El comstackdor, escritores de ensamblaje, enlazadores que generan código (o requisitos de alineación), sistema operativo (manejo de interrupciones, interfaz de syscall). Si hiciste la progtwigción de assembly, ¡te estabas conformando con un ABI!

El mapeo de nombres en C ++ es un caso especial: es un problema de enlazador y de enlance al enlance dynamic. Si el mapeo de nombres no está estandarizado, entonces los enlaces dynamics no funcionarán. De ahora en adelante, el C ++ ABI se llama simplemente eso, el C ++ ABI. No es un problema de nivel de vinculador, sino un problema de generación de código. Una vez que tiene un binario en C ++, no es posible hacerlo compatible con otro C ++ ABI (manipulación de nombres, manejo de excepciones) sin recomstackr desde la fuente.

ELF es un formato de archivo para el uso de un cargador y un enlazador dynamic. ELF es un formato de contenedor para código binario y datos, y como tal especifica el ABI de un fragmento de código. No consideraría que ELF sea un ABI en sentido estricto, ya que los ejecutables de PE no son un ABI.

Todos los ABI son conjuntos de instrucciones específicos. Un ARM ABI no tendrá sentido en un procesador MSP430 o x86_64.

Windows tiene varios ABI, por ejemplo, fastcall y stdcall son dos ABI de uso común. El syscall ABI es diferente de nuevo.

Déjame al menos responder una parte de tu pregunta. Con un ejemplo de cómo el ABI de Linux afecta las llamadas al sistema, y ​​por qué es útil.

Una llamada al sistema es una forma de que un progtwig de espacio de usuario solicite algo al espacio del kernel. Funciona poniendo el código numérico para la llamada y el argumento en un registro determinado y desencadenando una interrupción. Luego se produce un cambio en el espacio del kernel y el kernel busca el código numérico y el argumento, maneja la solicitud, vuelve a colocar el resultado en un registro y activa un cambio al espacio de usuario. Esto es necesario, por ejemplo, cuando la aplicación desea asignar memoria o abrir un archivo (syscalls “brk” y “open”).

Ahora los syscalls tienen nombres cortos “brk”, etc. y los códigos de operación correspondientes, estos se definen en un archivo de encabezado específico del sistema. Mientras estos códigos de operación permanezcan iguales, puede ejecutar los mismos progtwigs de usuario comstackdos con diferentes núcleos actualizados sin tener que volver a comstackr. Entonces tienes una interfaz utilizada por binarios precomstackdos, por lo tanto, ABI.

La mejor manera de diferenciar entre ABI y API es saber por qué y para qué se utiliza:

Para x86-64 generalmente hay un ABI (y para x86 de 32 bits hay otro conjunto):

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 lo siguen con algunas ligeras variaciones. Y Windows x64 tiene su propio ABI:

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

Conociendo el ABI y asumiendo que otro comstackdor también lo sigue, entonces los binarios teóricamente saben cómo llamarse entre sí (bibliotecas API en particular) y pasar parámetros sobre la stack o por registros, etc. O qué registros se cambiarán al llamar a las funciones, etc. Esencialmente, estos conocimientos ayudarán al software a integrarse entre sí. Conociendo el orden de la disposición de registros / stack, puedo unir fácilmente diferentes progtwigs escritos en ensamblajes sin muchos problemas.

Pero API son diferentes:

Se trata de un nombre de funciones de alto nivel, con un argumento definido, de modo que si se construyen diferentes piezas de software usando estas API, PUEDEN llamarse entre sí. Pero se debe cumplir un requisito adicional de SAME ABI.

Por ejemplo, Windows solía ser compatible con POSIX API:

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

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

Y Linux también cumple con POSIX. Pero los binarios no se pueden mover y ejecutar inmediatamente. Pero debido a que usaron los mismos NOMBRES en la API compatible con POSIX, puede tomar el mismo software en C, recomstackrlo en el sistema operativo diferente e inmediatamente ejecutarlo.

Las API están destinadas a facilitar la integración del software: etapa de precomstackción. Entonces, después de la comstackción, el software puede verse totalmente diferente, si los ABI son diferentes.

ABI está destinado a definir la integración exacta de software en el nivel binario / ensamblado.

Para llamar al código en bibliotecas compartidas, o código de llamada entre unidades de comstackción, el archivo objeto debe contener tags para las llamadas. C ++ descifra los nombres de las tags de métodos para imponer el ocultamiento de datos y permitir métodos sobrecargados. Es por eso que no puede mezclar archivos de diferentes comstackdores de C ++ a menos que explícitamente admitan el mismo ABI.

Resumen

Hay varias interpretaciones y fuertes opiniones de la capa exacta que define una ABI (interfaz binaria de aplicación).

En mi opinión, una ABI es una convención subjetiva de lo que se considera una plataforma / dada para una API específica. El ABI es el “rest” de las convenciones que “no cambiará” para una API específica o que será abordado por el entorno de tiempo de ejecución: ejecutores, herramientas, enlazadores, comstackdores, jvm y SO.

Definición de una interfaz : ABI, API

Si desea utilizar una biblioteca como joda-time, debe declarar una dependencia en joda-time-...jar . La biblioteca sigue las mejores prácticas y utiliza versiones semánticas . Esto define la compatibilidad API en tres niveles:

  1. Parche – No necesita cambiar todo su código. La biblioteca solo corrige algunos errores.
  2. Menor – No necesita cambiar su código ya que las adiciones
  3. Mayor: la interfaz (API) ha cambiado y es posible que deba cambiar su código.

Para que pueda utilizar una nueva versión principal de la misma biblioteca, aún deben respetarse muchas otras convenciones:

  • El lenguaje binario utilizado para las bibliotecas (en los casos de Java, la versión de destino de JVM que define el bytecode de Java)
  • Convenciones de llamadas
  • Convenciones de JVM
  • Convenciones de enlace
  • Convenciones de tiempo de ejecución Todo esto está definido y gestionado por las herramientas que utilizamos.

Ejemplos

Estudio de caso de Java

Por ejemplo, Java estandarizó todas estas convenciones, no en una herramienta, sino en una especificación JVM formal. La especificación permitió a otros proveedores proporcionar un conjunto diferente de herramientas que pueden generar bibliotecas compatibles.

Java proporciona otros dos casos de estudio interesantes para ABI: versiones de Scala y máquina virtual Dalvik .

La máquina virtual Dalvik rompió el ABI

La VM Dalvik necesita un tipo diferente de bytecode que el bytecode de Java. Las bibliotecas de Dalvik se obtienen convirtiendo el bytecode de Java (con la misma API) para Dalvik. De esta forma, puede obtener dos versiones de la misma API: definida por el original joda-time-1.7.2.jar . Podríamos llamarme joda-time-1.7.2.jar y joda-time-1.7.2-dalvik.jar . Utilizan un ABI diferente para los vms de Java estándar orientados a la stack: uno de Oracle, uno de IBM, Java abierto o cualquier otro; y el segundo ABI es el que rodea a Dalvik.

Las versiones sucesivas de Scala son incompatibles

Scala no tiene compatibilidad binaria entre versiones menores de Scala: 2.X. Por esta razón, la misma API “io.reactivex” %% “rxscala”% “0.26.5” tiene tres versiones (en el futuro, más): para Scala 2.10, 2.11 y 2.12. ¿Qué ha cambiado? No lo sé por el momento , pero los binarios no son compatibles. Probablemente, las últimas versiones agreguen cosas que hacen que las bibliotecas sean inutilizables en las antiguas máquinas virtuales, probablemente cosas relacionadas con las convenciones de enlace / nombre / parámetro.

Las versiones sucesivas de Java son incompatibles

Java también tiene problemas con las principales versiones de JVM: 4,5,6,7,8,9. Ofrecen solo compatibilidad con versiones anteriores. Jvm9 sabe cómo ejecutar código comstackdo / dirigido (opción de -target de javac) para todas las demás versiones, mientras que JVM 4 no sabe cómo ejecutar código dirigido a JVM 5. Todo esto mientras tiene una joda-library. Esta incompatibilidad vuela debajo del radar gracias a diferentes soluciones:

  1. Versiones semánticas: cuando las bibliotecas apuntan a una JVM más alta, generalmente cambian la versión principal.
  2. Utiliza JVM 4 como ABI, y estás a salvo.
  3. Java 9 agrega una especificación sobre cómo puede incluir bytecode para una JVM específica en la misma biblioteca.

¿Por qué comencé con la definición de la API?

API y ABI son solo convenciones sobre cómo defines la compatibilidad. The lower layers are generic in respect of a plethora of high level semantics. That’s why it’s easy to make some conventions. The first kind of conventions are about memory alignment, byte encoding, calling conventions, big and little endian encodings, etc. On top of them you get the executable conventions like others described, linking conventions, intermediate byte code like the one used by Java or LLVM IR used by GCC. Third you get conventions on how to find libraries, how to load them (see Java classloaders). As you go higher and higher in concepts you have new conventions that you consider as a given. That’s why they didn’t made it to the semantic versioning . They are implicit or collapsed in the major version. We could amend semantic versioning with --- . This is what is actually happening already: platform is already a rpm , dll , jar (JVM bytecode), war (jvm+web server), apk , 2.11 (specific Scala version) and so on. When you say APK you already talk about a specific ABI part of your API.

API can be ported to different ABI

The top level of an abstraction (the sources written against the highest API can be recompiled/ported to any other lower level abstraction.

Let’s say I have some sources for rxscala. If the Scala tools are changed I can recompile them to that. If the JVM changes I could have automatic conversions from the old machine to the new one without bothering with the high level concepts. While porting might be difficult will help any other client. If a new operating system is created using a totally different assembler code a translator can be created.

APIs ported across languages

There are APIs that are ported in multiple languages like reactive streams . In general they define mappings to specific languages/platforms. I would argue that the API is the master specification formally defined in human language or even a specific programming language. All the other “mappings” are ABI in a sense, else more API than the usual ABI. The same is happening with the REST interfaces.

The ABI needs to be consistent between caller and callee to be certain that the call succeeds. Stack use, register use, end-of-routine stack pop. All these are the most important parts of the ABI.

In short and in philosophy, only things of a kind can get along well, and the ABI could be seen as the kind of which software stuff work together.

I was also trying to understand ABI and JesperE’s answer was very helpful.

From a very simple perspective, we may try to understand ABI by considering binary compatibility.

KDE wiki defines a library as binary compatible “if a program linked dynamically to a former version of the library continues running with newer versions of the library without the need to recompile.” For more on dynamic linking, refer Static linking vs dynamic linking

Now, let’s try to look at just the most basic aspects needed for a library to be binary compatibility (assuming there are no source code changes to the library):

  1. Same/backward compatible instruction set architecture (processor instructions, register file structure, stack organization, memory access types, along with sizes, layout, and alignment of basic data types the processor can directly access)
  2. Same calling conventions
  3. Same name mangling convention (this might be needed if say a Fortran program needs to call some C++ library function).

Sure, there are many other details but this is mostly what the ABI also covers.

More specifically to answer your question, from the above, we can deduce:

ABI functionality: binary compatibility

existing entities: existing program/libraries/OS

consumer: libraries, OS

¡Espero que esto ayude!

Application binary interface (ABI)

Funcionalidad

  • Translation from the programmer’s model to the underlying system’s domain data type, size, alignment, the calling convention, which controls how functions’ arguments are passed and return values retrieved; the system call numbers and how an application should make system calls to the operating system; the high-level language compilers’ name mangling scheme, exception propagation, and calling convention between compilers on the same platform, but do not require cross-platform compatibility…

Existing entities:

  • Logical blocks that directly participate in program’s execution: ALU, general purpose registers, registers for memory/ I/O mapping of I/O, etc…

consumer:

  • Language processors linker, assembler…

These are needed by whoever has to ensure that build tool-chains work as a whole. If you write one module in assembly language, another in Python, and instead of your own boot-loader want to use an operating system, then your “application” modules are working across “binary” boundaries and require agreement of such “interface”.

C++ name mangling because object files from different high-level languages might be required to be linked in your application. Consider using GCC standard library making system calls to Windows built with Visual C++.

ELF is one possible expectation of the linker from an object file for interpretation, though JVM might have some other idea.

For a Windows RT Store app, try searching for ARM ABI if you really wish to make some build tool-chain work together.

The term ABI is used to refer to two distinct but related concepts.

When talking about compilers it refers to the rules used to translate from source-level constructs to binary constructs. How big are the data types? how does the stack work? how do I pass parameters to functions? which registers should be saved by the caller vs the callee?

When talking about libraries it refers to the binary interface presented by a compiled library. This interface is the result of a number of factors including the source code of the library, the rules used by the compiler and in some cases definitions picked up from other libraries.

Changes to a library can break the ABI without breaking the API. Consider for example a library with an interface like.

 void initfoo(FOO * foo) int usefoo(FOO * foo, int bar) void cleanupfoo(FOO * foo) 

and the application programmer writes code like

 int dostuffwithfoo(int bar) { FOO foo; initfoo(&foo); int result = usefoo(&foo,bar) cleanupfoo(&foo); return result; } 

The application programmer doesn’t care about the size or layout of FOO, but the application binary ends up with a hardcoded size of foo. If the library programmer adds an extra field to foo and someone uses the new library binary with the old application binary then the library may make out of bounds memory accesses.

OTOH if the library author had designed their API like.

 FOO * newfoo(void) int usefoo(FOO * foo, int bar) void deletefoo((FOO * foo, int bar)) 

and the application programmer writes code like

 int dostuffwithfoo(int bar) { FOO * foo; foo = newfoo(); int result = usefoo(&foo,bar) deletefoo(&foo); return result; } 

Then the application binary does not need to know anything about the structure of FOO, that can all be hidden inside the library. The price you pay for that though is that heap operations are involved.