significado application api compiler-construction binary operating-system abi

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



abi significado (14)

Nunca entendí claramente lo que es un ABI. Por favor, no me apunte a un artículo de Wikipedia. Si pudiera entenderlo, no estaría aquí publicando un post tan largo.

Esta es mi mentalidad acerca de las diferentes interfaces:

Un control remoto de TV es una interfaz entre el usuario y la TV. Es una entidad existente, pero inútil (no proporciona ninguna funcionalidad) por sí misma. 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 que está detrás.

Ahora, dependiendo de quién es 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 software que resuelve algún propósito para el que describimos 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 nuevamente el consumidor es el usuario y la funcionalidad está detrás.

functionality: mi software que resuelve algún propósito para el que describimos esta interfaz.

existing entities: ventana, botones etc.

consumer: usuario

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

functionality: mi software que resuelve algún propósito para el que describimos esta interfaz.

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

consumer: otro programa / aplicación.

Interfaz binaria de aplicación (ABI) Aquí es donde comienza mi problema.

functionality: ???

existing entities: ???

consumer: ???

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

Wikipedia dice:

ABIs cubren detalles tales como

  • tipo de datos, tamaño y alineación;
  • la convención de llamada, 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 del sistema al sistema operativo;

Otros ABIs estandarizan detalles tales como

  • el nombre de C ++ mangling,
  • propagación de excepciones, y
  • convención de llamada entre compiladores en la misma plataforma, pero no requieren compatibilidad multiplataforma.
  • ¿Quién necesita estos detalles? Por favor, no digas el sistema operativo. Conozco la programación del montaje. Sé cómo funcionan los enlaces y la carga. Sé lo que sucede exactamente dentro.

  • ¿Por qué entró el nombre de C ++? Pensé que estamos hablando a nivel binario. ¿Por qué entran los idiomas?

De todos modos, descargué la versión 4.1 (1997-03-18) de la Interfaz Binaria de la Aplicación del Sistema V (1997-03-18) para ver qué contiene exactamente. Bueno, la mayor parte no tenía ningún sentido.

  • ¿Por qué contiene dos capítulos (4º y 5º) para describir el formato de archivo ELF ? De hecho, estos son los únicos dos capítulos significativos de esa especificación. El resto de los capítulos son "específicos del procesador". De todos modos, pensé que es un tema completamente diferente. Por favor, no digas que las especificaciones de formato de archivo ELF son el ABI. No califica para ser una interfaz según la definición.

  • Lo sé, ya que estamos hablando a un nivel tan bajo, debe ser muy específico. ¿Pero no estoy seguro de cómo es la "arquitectura de conjunto de instrucciones (ISA)" específica?

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

Por lo tanto, estas son las principales consultas que me están molestando.


Resumen

Existen varias interpretaciones y opiniones sólidas sobre la capa exacta que definen 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 "resto" de las convenciones que "no cambiarán" para una API específica o que serán abordadas por el entorno de ejecución: ejecutores, herramientas, enlazadores, compiladores, jvm y sistemas operativos.

Definiendo una interfaz : ABI, API

Si desea utilizar una biblioteca como joda-time, debe declarar una dependencia en joda-time-<major>.<minor>.<patch>.jar . La biblioteca sigue las mejores prácticas y utiliza la versión semántica . Esto define la compatibilidad API en tres niveles:

  1. Parche: no es necesario que cambies tu código. La biblioteca solo corrige algunos errores.
  2. Menor: no es necesario cambiar el código desde las adiciones
  3. Principal: la interfaz (API) ha cambiado y es posible que necesite cambiar su código.

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

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

Ejemplos

Caso de estudio 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 Dalvik virtual Dalvik .

Máquina virtual Dalvik rompió el ABI

La máquina virtual de Dalvik necesita un tipo diferente de código de bytes que el código de bytes de Java. Las bibliotecas de Dalvik se obtienen al convertir el código de bytes de Java (con la misma API) para Dalvik. De esta manera, puede obtener dos versiones de la misma API: definida por el joda-time-1.7.2.jar . Podríamos llamarme joda-time-1.7.2.jar y joda-time-1.7.2-dalvik.jar . Usan un ABI diferente, uno para vms Java estándar orientado a la pila: el de Oracle, el de IBM, Java abierto o cualquier otro; y el segundo ABI es el que rodea a Dalvik.

Los lanzamientos sucesivos de Scala son incompatibles.

Scala no tiene compatibilidad binaria entre versiones menores de Scala: 2.X. Por este motivo, 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é se cambia? No lo sé por ahora , pero los binarios no son compatibles. Probablemente las últimas versiones agregan cosas que hacen que las bibliotecas no puedan utilizarse en las máquinas virtuales antiguas, probablemente cosas relacionadas con las convenciones de vinculación / asignación de nombres / parámetros.

Las versiones sucesivas de Java son incompatibles.

Java también tiene problemas con los principales lanzamientos de la JVM: 4,5,6,7,8,9. Sólo ofrecen compatibilidad con versiones anteriores. Jvm9 sabe cómo ejecutar el código compilado / dirigido (javac''s -target option) para todas las demás versiones, mientras que JVM 4 no sabe cómo ejecutar el código dirigido a JVM 5. Todo esto mientras tiene una biblioteca joda. Esta incompatibilidad vuela bajo el radar gracias a diferentes soluciones:

  1. Creación de versiones semántica: cuando las bibliotecas se enfocan en JVM superiores, generalmente cambian la versión principal.
  2. Usa JVM 4 como ABI, y estarás a salvo.
  3. Java 9 agrega una especificación sobre cómo puede incluir el bytecode para JVM específica en la misma biblioteca.

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

API y ABI son solo convenciones sobre cómo se define la compatibilidad. Las capas inferiores son genéricas con respecto a una plétora de semánticas de alto nivel. Por eso es fácil hacer algunas convenciones. El primer tipo de convenciones se refiere a la alineación de la memoria, la codificación de bytes, las convenciones de llamadas, las codificaciones endian grandes y pequeñas, etc. Además, se encuentran las convenciones ejecutables como se describe a otras, las convenciones de enlace, el código de byte intermedio como el que utiliza Java LLVM IR utilizado por GCC. En tercer lugar, obtiene convenciones sobre cómo encontrar bibliotecas, cómo cargarlas (consulte los cargadores de clases de Java). A medida que aumenta en conceptos, tiene nuevas convenciones que considera como un hecho. Por eso no llegaron al versionamiento semántico . Están implícitos o colapsados ​​en elversión mayor Podríamos enmendar las versiones semánticas con <major>-<minor>-<patch>-<platform/ABI>. Esto es lo que realmente está sucediendo ya: plataforma ya es un rpm, dll, jar(bytecode JVM), war(servidor web JVM +), apk, 2.11(versión específica Scala) y así sucesivamente. Cuando dices APK ya hablas de una parte ABI específica de tu API.

API puede ser portado a diferentes ABI

El nivel superior de una abstracción (las fuentes escritas en la API más alta se pueden compilar / portar a cualquier otra abstracción de nivel inferior).

Digamos que tengo algunas fuentes para rxscala. Si se cambian las herramientas de Scala puedo recompilarlas para eso. Si la JVM cambia, podría tener conversiones automáticas de la máquina antigua a la nueva sin molestarme con los conceptos de alto nivel. Mientras que portar puede ser difícil ayudará a cualquier otro cliente. Si se crea un nuevo sistema operativo utilizando un código de ensamblador totalmente diferente, se puede crear un traductor.

APIs portadas a través de idiomas

Hay API que se portan en varios idiomas como flujos reactivos . En general, definen asignaciones a lenguajes / plataformas específicos. Yo diría que la API es la especificación maestra definida formalmente en lenguaje humano o incluso en un lenguaje de programación específico. Todas las demás "asignaciones" son ABI en cierto sentido, o más API que la ABI habitual. Lo mismo ocurre con las interfaces REST.


Funcionalidad: un conjunto de contratos que afectan al compilador, a los escritores de ensamblados, al vinculador y al sistema operativo. Los contratos especifican cómo se diseñan las funciones, dónde se pasan los parámetros, cómo se pasan los parámetros y cómo funcionan los retornos de la función. Estos son generalmente específicos para una tupla (arquitectura del procesador, sistema operativo).

Entidades existentes: disposición de parámetros, semántica de funciones, asignación de registros. Por ejemplo, las arquitecturas ARM tienen numerosos ABI (APCS, EABI, GNU-EABI, sin importar un montón de casos históricos): el uso de un ABI mixto hará que su código simplemente no funcione cuando realice llamadas a través de los límites.

Consumidor: El compilador, los ensambladores, el sistema operativo, la arquitectura específica de la CPU.

¿Quién necesita estos detalles? El compilador, los ensambladores, los enlazadores que realizan la generación de código (o los requisitos de alineación), el sistema operativo (manejo de interrupciones, interfaz syscall). Si hiciste la programación de ensamblaje, estabas conforme a un ABI!

La manipulación de nombres en C ++ es un caso especial, es un problema centrado en el vinculador y el vinculador dinámico: si la manipulación de nombres no está estandarizada, la vinculación dinámica no funcionará. De aquí en adelante, el ABI de C ++ se llama simplemente eso, el ABI de C ++. No es un problema de nivel de vinculador, sino un problema de generación de código. Una vez que tiene un binario de C ++, no es posible hacerlo compatible con otro ABI de C ++ (manipulación de nombres, manejo de excepciones) sin volver a compilar desde la fuente.

ELF es un formato de archivo para el uso de un cargador y un enlazador dinámico. ELF es un formato 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 el sentido estricto, ya que los ejecutables de PE no son un ABI.

Todas las ABIs son específicas del conjunto de instrucciones. 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.


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 sigue con algunas variaciones leves. 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 compilador también lo sigue, entonces los binarios teóricamente se llaman mutuamente (bibliotecas API en particular) y pasan parámetros sobre la pila 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í. Sabiendo el orden de los registros / disposición de la pila, puedo juntar fácilmente diferentes programas de software escritos en ensamblajes sin muchos problemas.

Pero las API son diferentes:

Es un nombre de funciones de alto nivel, con un argumento definido, tal que, si se crean diferentes piezas de software utilizando estas API, PUEDEN ser capaces de comunicarse entre sí. Pero debe cumplirse 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 es compatible con POSIX también. Pero los binarios no se pueden mover y ejecutar de inmediato. Pero debido a que utilizaron los mismos NOMBRES en la API compatible con POSIX, puede tomar el mismo software en C, recompilarlo en los diferentes sistemas operativos e iniciarlo de inmediato.

Las API están diseñadas para facilitar la integración del software, etapa de precompilación. Entonces, después de la compilación, el software puede verse totalmente diferente, si el ABI es diferente.

Los ABI están diseñados para definir la integración exacta del software en el nivel binario / ensamblador.


Para poder llamar al código en bibliotecas compartidas, o al código de llamada entre unidades de compilación, el archivo objeto debe contener etiquetas para las llamadas. C ++ maneja los nombres de las etiquetas de los métodos para forzar la ocultación de datos y permitir métodos sobrecargados. Es por eso que no puede mezclar archivos de diferentes compiladores de C ++ a menos que admitan explícitamente el mismo ABI.


Permítame al menos responder una parte de su pregunta. Con un ejemplo de cómo la ABI de Linux afecta las llamadas al sistema y por qué es útil.

Una llamada al sistema es una forma en que un programa de espacio de usuario le pide al kernelspace algo. Funciona al colocar el código numérico para la llamada y el argumento en un registro determinado y desencadenar una interrupción. Cuando se produce un cambio en kernelspace y el kernel busca el código numérico y el argumento, maneja la solicitud, devuelve el resultado a un registro y desencadena 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 las llamadas al sistema 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 programas de tierra de usuario compilados con diferentes núcleos actualizados sin tener que volver a compilarlos. Así que tienes una interfaz utilizada por binarios precompilados, por lo tanto, ABI.


Realmente no necesitas un ABI en absoluto si ...

  • Tu programa no tiene funciones, y--
  • Su programa es un solo ejecutable que se ejecuta solo (es decir, un sistema integrado) donde literalmente es lo único que se está ejecutando y no necesita hablar con otra cosa.

Un resumen simplificado:

API: "Aquí están todas las funciones que puedes llamar".

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

El ABI es un conjunto de reglas que los compiladores y enlazadores se adhieren para compilar su programa para que funcione correctamente. ABIs cubren múltiples temas:

  • Podría decirse que la parte más grande y más importante de una ABI es el procedimiento estándar de llamada a veces conocido como la "convención de llamada". Las convenciones de llamada estandarizan cómo se traducen las "funciones" al código de ensamblaje.
  • 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 deben pasarse. Esto se llama "nombre mangling".
  • Los ABI también dictan qué tipo de tipos de datos se pueden usar, cómo deben alinearse y otros detalles de bajo nivel.

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

La propia máquina no tiene concepto de "funciones". Cuando escribe una función en un lenguaje de alto nivel como c, el compilador genera una línea de código de ensamblaje como _MyFunction1: Esta es una etiqueta , que finalmente se resolverá en una dirección por parte del 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" a esa función, lo que realmente estás haciendo es hacer que la CPU salte a la dirección de esa etiqueta y continúe ejecutándose allí.

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

  • Primero, el compilador inserta un poco de código de ensamblaje para guardar la dirección actual, de modo que cuando se realiza su "función", la CPU puede saltar al lugar correcto y continuar ejecutándose.
  • A continuación, el compilador genera código de ensamblaje para pasar los argumentos.
    • Algunas convenciones de llamada dictan que los argumentos deben ponerse en la pila ( en un orden particular, por supuesto).
    • Otras convenciones dictan que los argumentos deben colocarse en registros particulares ( dependiendo de sus tipos de datos, por supuesto).
    • Otras convenciones dictan que se debe usar una combinación específica de pila y registros.
  • Por supuesto, si antes había algo importante en esos registros, esos valores ahora se sobrescriben y se pierden para siempre, por lo que algunas convenciones de llamada pueden dictar que el compilador debe guardar algunos de esos registros antes de poner los argumentos en ellos.
  • Ahora el compilador inserta una instrucción de salto que le dice a la CPU que vaya a la etiqueta que hizo anteriormente ( _MyFunction1: . En este punto, puede considerar que la CPU está "en" su "función".
  • Al final de la función, el compilador coloca un código de ensamblaje que hará que la CPU escriba el valor de retorno en el lugar correcto. La convención de llamada determinará si el valor de retorno debe colocarse en un registro particular (dependiendo de su tipo) o en la pila.
  • Ahora es el momento de la limpieza. La convención de llamada dictará dónde el compilador coloca el código de ensamblaje de limpieza.
    • Algunas convenciones dicen que la persona que llama debe limpiar la pila. Esto significa que después de que se realiza la "función" y la CPU salta a donde estaba antes, el siguiente código que se ejecutará 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 hacia atrás.

Hay muchas convenciones de llamadas / ABI diferentes. Algunos de los 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

Here hay una gran página que realmente muestra las diferencias en el ensamblaje generado al compilar para diferentes ABI.

Otra cosa a mencionar es que una ABI no solo es relevante dentro del módulo ejecutable de su programa. También lo usa el enlazador para asegurarse de que su programa llame a las funciones de la biblioteca correctamente. Tiene múltiples bibliotecas compartidas ejecutándose en su computadora, y siempre que su compilador sepa qué ABI utilizan, puede llamar a las funciones de ellas adecuadamente sin destruir la pila.

Su compilador que comprende cómo llamar a las funciones de la biblioteca es extremadamente importante. En una plataforma alojada (es decir, una en la que un sistema operativo carga programas), su programa ni siquiera puede parpadear sin hacer una llamada al kernel.


Si conoce el ensamblaje y cómo funcionan las cosas a nivel del sistema operativo, está cumpliendo con un cierto ABI. El ABI gobierna cosas como la forma en que se pasan los parámetros, donde se colocan los valores de retorno. Para muchas plataformas solo hay una ABI para elegir, y en esos casos la ABI es simplemente "cómo funcionan las cosas".

Sin embargo, el ABI también gobierna cosas como la disposición de las clases / objetos en C ++. Esto es necesario si desea poder pasar referencias de objetos a través de los límites de los módulos o si desea mezclar código compilado con diferentes compiladores.

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

En general, cualquier código que enlace en el 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 te estás esforzando demasiado para comprimir 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) agnósticos a ISA. Algunos aspectos (como las convenciones de llamada) dependen de la ISA, mientras que otros aspectos (como el diseño de la clase C ++) no lo hacen.

Un ABI bien definido es muy importante para las personas que escriben compiladores. 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 que exporta una clase C ++, en algún lugar de ella, los métodos y las firmas de tipo deben estar codificados. Ahí es donde entra en juego el nombre de C ++.
  • La razón por la que nunca proporcionó un ABI es que la gran mayoría de los programadores nunca lo harán. Los ABI son proporcionados por las mismas personas que diseñan la plataforma (es decir, el sistema operativo), y muy pocos programadores 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 a nivel de código fuente. Solo una representación binaria es accesible / disponible.

Las ABI se pueden definir en el nivel de la arquitectura del procesador o en el nivel del sistema operativo. Los ABI son estándares a seguir por la fase del generador de código del compilador. El estándar es fijado por el sistema operativo o por el procesador.

Funcionalidad: defina el mecanismo / estándar para hacer que las llamadas de función sean independientes del lenguaje de implementación o de un compilador / 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, compilada por otro compilador o enlazada por otro enlazador).


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 funciones 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 compilada de una API (o como una API en el nivel de lenguaje de máquina). Cuando escribes el código fuente, accedes a la biblioteca a través de una API. Una vez que se compila el código, su aplicación accede a los datos binarios en la biblioteca a través del ABI. El ABI define las estructuras y los métodos que su aplicación compilada utilizará para acceder a la biblioteca externa (como lo hizo la API), solo en un nivel inferior.

Los ABI son importantes cuando se trata de aplicaciones que utilizan bibliotecas externas. Si un programa está diseñado para usar una biblioteca en particular y esa biblioteca se actualiza más adelante, no querrá tener que volver a compilar esa aplicación (y desde el punto de vista del usuario final, es posible que no tenga la fuente). Si la biblioteca actualizada utiliza el mismo ABI, no será necesario cambiar su programa. La interfaz de la biblioteca (que es todo lo que realmente le importa a tu programa) es la misma, aunque el funcionamiento interno puede haber cambiado. Dos versiones de una biblioteca que tienen el mismo ABI a veces se denominan "compatibles con binarios", ya que tienen la misma interfaz de bajo nivel (debería poder reemplazar la versión anterior con la nueva y no tener ningún problema importante).

A veces, los cambios ABI son inevitables. Cuando esto sucede, cualquier programa que use esa biblioteca no funcionará a menos que se vuelva a compilar para usar la nueva versión de la biblioteca. Si la ABI cambia pero la API no, las versiones de la biblioteca anterior y la nueva a veces se denominan "compatible con la fuente". Esto implica que si bien un programa compilado para una versión de la biblioteca no funcionará con el otro, el código fuente escrito para uno funcionará para el otro si se vuelve a compilar.

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 función (tipo y número de retorno, 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, digamos, un campo de estructura de datos de 16 bits a un campo de 32 bits, entonces el código ya compilado que usa esa estructura de datos no accederá a ese campo (o 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 compilación y si la estructura de datos cambia, estas compensaciones no apuntarán a lo que el código espera que apunten y los resultados son, en el mejor de los casos, impredecibles.

Un ABI no es necesariamente algo que proporcionará explícitamente a menos que esté esperando que las personas interactúen con su código utilizando ensamblador. 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 compilarlas.

Edición: con respecto a su pregunta sobre los capítulos relacionados con el formato de archivo ELF en los documentos de SysV ABI: la razón por la 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 programa, espera que el programa se formatee de cierta manera y (por ejemplo) espera que la primera sección del binario sea un encabezado ELF que contenga cierta información en compensaciones de memoria específicas. Así es como la aplicación comunica información importante sobre sí misma al sistema operativo. Si crea un programa en un formato binario que no es ELF (como a.out o PE), un sistema operativo que espere que las aplicaciones con formato ELF no pueda interpretar el archivo binario o ejecutar la aplicación. Esta es una de las razones principales por las que las aplicaciones de Windows no se pueden ejecutar directamente en una máquina Linux (o viceversa) sin volver a compilarlas o ejecutarlas dentro de algún tipo de capa de emulación que se puede traducir de un formato binario a otro.

IIRC, Windows actualmente usa el formato de ejecutable portátil (o PE). Hay enlaces en la sección "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 la manipulación de nombres en C ++: ABI puede definir una forma "estandarizada" para que un compilador de C ++ realice la manipulación de nombres con el propósito de compatibilidad. Es decir, si creo una biblioteca y usted desarrolla un programa que usa la biblioteca, debería poder usar un compilador diferente al que tenía yo y no tener que preocuparme de que los binarios resultantes sean incompatibles debido a los diferentes esquemas de denominación de nombres. Esto realmente solo es de uso si está definiendo un nuevo formato de archivo binario o escribiendo un compilador o enlazador.


El término ABI se utiliza para referirse a dos conceptos distintos pero relacionados.

Cuando se habla de compiladores, se refiere a las reglas utilizadas para traducir de construcciones de nivel fuente a construcciones binarias. ¿Qué tan grandes son los tipos de datos? ¿Cómo funciona la pila? ¿Cómo paso los parámetros a las funciones? ¿Qué registros deben ser guardados por la persona que llama frente a la persona que llama?

Cuando se habla de bibliotecas, se refiere a la interfaz binaria presentada por una biblioteca compilada. Esta interfaz es el resultado de una serie de factores que incluyen el código fuente de la biblioteca, las reglas utilizadas por el compilador y, en algunos casos, las definiciones recogidas de otras bibliotecas.

Los cambios en una biblioteca pueden romper la ABI sin romper la API. Considere, por ejemplo, una biblioteca con una interfaz como.

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

y el programador de aplicaciones escribe código como

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

El programador de aplicaciones no se preocupa por el tamaño o el diseño de FOO, pero el binario de la aplicación termina con un tamaño codificado de foo. Si el programador de la biblioteca agrega un campo adicional a foo y alguien utiliza el nuevo binario de la biblioteca con el binario de la aplicación anterior, entonces la biblioteca puede realizar accesos de memoria fuera de límites.

OTOH si el autor de la biblioteca había diseñado su API como.

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

y el programador de aplicaciones escribe código como

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

Entonces, el binario de la aplicación no necesita saber nada sobre la estructura de FOO, que puede estar oculto dentro de la biblioteca. Sin embargo, el precio que pagas por eso es que están involucradas las operaciones del montón.


Interfaz binaria de aplicación (ABI)

Funcionalidad:

  • Traducción del modelo del programador al tipo de datos de dominio del sistema subyacente, tamaño, alineación, la convención de llamada, 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 del sistema al sistema operativo; el esquema de gestión de nombres de los compiladores de lenguaje de alto nivel, la propagación de excepciones y la convención de llamada entre compiladores en la misma plataforma, pero no requieren compatibilidad entre plataformas ...

Entidades existentes:

  • Bloques lógicos que participan directamente en la ejecución del programa: ALU, registros de propósito general, registros de mapeo de E / S de memoria / E / S, etc.

consumidor:

  • Procesador de lenguaje enlazador, ensamblador ...

Todo el que tiene que asegurarse es necesario para que las cadenas de herramientas de construcción funcionen como un todo. Si escribe un módulo en lenguaje ensamblador, otro en Python, y en lugar de su propio cargador de arranque desea utilizar un sistema operativo, sus módulos de "aplicación" están trabajando a través de límites "binarios" y requieren un acuerdo de dicha "interfaz".

Drenaje de nombres en C ++ porque los archivos de objetos de diferentes lenguajes de alto nivel deben estar vinculados en su aplicación. Considere utilizar la biblioteca estándar de GCC para hacer llamadas al sistema de Windows construidas con Visual C ++.

ELF es una posible expectativa del enlazador de un archivo de objeto para interpretación, aunque JVM podría tener alguna otra idea.

Para una aplicación de Windows RT Store, intente buscar ARM ABI si realmente desea hacer que algunas cadenas de herramientas de compilación funcionen juntas.


También estaba tratando de entender que la respuesta de ABI y JesperE fue muy útil.

Desde una perspectiva muy simple, podemos tratar de entender ABI considerando la compatibilidad binaria.

KDE wiki define una biblioteca como binario compatible “si un programa vinculado dinámicamente a una versión anterior de la biblioteca continúa funcionando con las nuevas versiones de la biblioteca sin la necesidad de volver a compilar”. Para más información sobre la vinculación dinámica, consulte la vinculación estática vs vinculación dinámica

Ahora, tratemos de ver solo los aspectos más básicos necesarios para que una biblioteca tenga compatibilidad binaria (asumiendo que no hay cambios en el código fuente de la biblioteca):

  1. Arquitectura de conjuntos de instrucciones compatibles iguales o anteriores (instrucciones del procesador, estructura de archivos de registro, organización de pila, tipos de acceso a la memoria, junto con tamaños, diseño y alineación de los tipos de datos básicos a los que el procesador puede acceder directamente)
  2. Mismas convenciones de llamadas
  3. La misma convención de manipulación de nombres (esto podría ser necesario si, por ejemplo, un programa Fortran necesita llamar a alguna función de biblioteca de C ++).

Claro, hay muchos otros detalles, pero esto es principalmente lo que ABI también cubre.

Más específicamente para responder a su pregunta, de lo anterior, podemos deducir:

Funcionalidad ABI: compatibilidad binaria

Entidades existentes: programa / bibliotecas / OS existentes

consumidor: bibliotecas, OS

¡Espero que esto ayude!


El ABI debe ser coherente entre la persona que llama y la persona que llama para asegurarse de que la llamada sea exitosa. Uso de pila, uso de registro, pop de final de rutina. Todas estas son las partes más importantes de la ABI.


En resumen y en filosofía, solo las cosas de un tipo pueden llevarse bien, y el ABI podría verse como el tipo de software que funciona en conjunto.