Nunca entendí claramente qué es un ABI. Por favor, no me señales un artículo de Wikipedia. Si pudiera entenderlo, no estaría aquí publicando una publicación tan larga.
Esta es mi mentalidad sobre las diferentes interfaces:
Un control remoto de TV es una interfaz entre el usuario y el 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
functionality
yconsumer
de esa funcionalidad. Una interfaz por sí sola 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:
usuarioLa ventana, los botones, etc. de la interfaz gráfica de usuario (GUI) son las entidades existentes, y nuevamente el consumidor es el usuario y la funcionalidad se encuentra detrás.
functionality:
mi funcionalidad de software que resuelve algún problema al que estamos describiendo esta interfaz.
existing entities:
ventana, botones etc.
consumer:
usuarioLas funciones de la interfaz de programación de aplicaciones (API) (o para ser más correctos) (en la programación basada en interfaz) son las entidades existentes, el consumidor aquí es otro programa, no un usuario, y nuevamente la funcionalidad se encuentra detrás de esta capa.
functionality:
mi funcionalidad de software que resuelve algún problema al que estamos describiendo esta interfaz.
existing entities:
funciones, Interfaces (conjunto 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 de si alguna vez proporcioné alguna ABI.
Los ABI cubren detalles 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 devueltos;
- los números de llamada del sistema y cómo una aplicación debe hacer llamadas del sistema al sistema operativo;
Otros ABI estandarizan detalles como
- el cambio de nombre de C ++,
- propagación de excepciones, y
- llamada de convención entre compiladores en la misma plataforma, pero no requiere compatibilidad multiplataforma.
¿Quién necesita estos detalles? Por favor no digas el sistema operativo. Sé programación de ensamblaje. Sé cómo funciona la vinculación y carga. Sé exactamente lo que pasa adentro.
¿Por qué entró el cambio de nombre en C ++? Pensé que estamos hablando a nivel binario. ¿Por qué entran los idiomas?
De todos modos, descargué la [ 4.1 ] Interfaz binaria de la aplicación System V Edición 4.1 (1997-03-18) para ver qué contiene exactamente. 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 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. No diga que las especificaciones de formato de archivo ELF son 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ífica la "arquitectura de conjunto de instrucciones (ISA)".
¿Dónde puedo encontrar el ABI de Microsoft Windows?
Entonces, estas son las principales consultas que me están molestando.
Respuestas:
Una manera fácil de entender "ABI" es compararlo con "API".
Ya estás familiarizado con el concepto de una API. Si desea utilizar las funciones de, por ejemplo, alguna biblioteca o su sistema operativo, programará contra una API. La API consta de tipos / estructuras de datos, 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 escribe el código fuente, accede 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 usará su aplicación compilada para acceder a la biblioteca externa (tal como lo hizo la API), solo en un nivel inferior. Su API define el orden en que pasa los argumentos a una función. Su ABI define la mecánica de cómoSe pasan estos argumentos (registros, pila, etc.). Su API define qué funciones son parte de su biblioteca. Su ABI define cómo se almacena su código dentro del archivo de la biblioteca, de modo que cualquier programa que use su biblioteca pueda localizar la función deseada y ejecutarla.
Las ABI son importantes cuando se trata de aplicaciones que usan bibliotecas externas. Las bibliotecas están llenas de código y otros recursos, pero su programa debe saber cómo ubicar lo que necesita dentro del archivo de la biblioteca. Su ABI define cómo se almacenan los contenidos de una biblioteca dentro del archivo, y su programa usa la ABI para buscar a través del archivo y encontrar lo que necesita. Si todo en su sistema se ajusta a la misma ABI, entonces cualquier programa puede trabajar con cualquier archivo de biblioteca, sin importar quién los creó. Linux y Windows usan diferentes ABI, por lo que un programa de Windows no sabrá cómo acceder a una biblioteca compilada para Linux.
A veces, los cambios de ABI son inevitables. Cuando esto sucede, los programas que usan esa biblioteca no funcionarán a menos que se vuelvan a compilar para usar la nueva versión de la biblioteca. Si la ABI cambia pero la API no, entonces las versiones de biblioteca nuevas y antiguas a veces se denominan "fuente compatible". Esto implica que si bien un programa compilado 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 compilar.
Por esta razón, los desarrolladores tienden a tratar de mantener su ABI estable (para minimizar las interrupciones). 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, por ejemplo, su biblioteca usa enteros de 32 bits para indicar el desplazamiento de una función y cambia a enteros de 64 bits, entonces el código ya compilado que usa esa biblioteca no accederá a ese campo (o ninguno lo seguirá) 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,
Un ABI no es necesariamente algo que proporcionará explícitamente a menos que esté haciendo un trabajo de diseño de sistemas de muy bajo nivel. Tampoco es específico del lenguaje, ya que (por ejemplo) una aplicación C y una aplicación Pascal pueden usar el mismo ABI después de que se compilan.
Editar:Con respecto a su pregunta sobre los capítulos sobre el formato de archivo ELF en los documentos ABI de SysV: 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 no ELF (como a.out o PE), entonces un sistema operativo que espera aplicaciones con formato ELF no podrá interpretar el archivo binario ni ejecutar la aplicación.
IIRC, Windows actualmente usa el formato ejecutable portátil (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 ++: Al ubicar una función en un archivo de biblioteca, la función generalmente se busca por nombre. C ++ le permite sobrecargar los nombres de las funciones, por lo que solo el nombre no es suficiente para identificar una función. Los compiladores de C ++ tienen sus propias formas de lidiar con esto internamente, llamado cambio de nombre . Un ABI puede definir una forma estándar de codificar el nombre de una función para que los programas creados con un lenguaje o compilador diferente puedan localizar lo que necesitan. Cuando lo usa
extern "c"
en un programa C ++, le indica al compilador que use una forma estandarizada de grabar nombres que sea comprensible para otro software.fuente
Si conoce el ensamblaje y cómo funcionan las cosas a nivel del sistema operativo, se está ajustando a un cierto 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 un ABI para elegir, y en esos casos el ABI es simplemente "cómo funcionan las cosas".
Sin embargo, el ABI también gobierna cosas como cómo se presentan las clases / objetos 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 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 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á tratando demasiado de introducir 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 por la cual dos entidades interactúan.
Los ABI pueden ser (parcialmente) independientes de ISA. Algunos aspectos (como las convenciones de llamadas) dependen del ISA, mientras que otros aspectos (como el diseño de clase C ++) no.
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:
fuente
En realidad , no necesitas un ABI si--
Un resumen demasiado simplificado:
El ABI es un conjunto de reglas a las que se adhieren los compiladores y vinculadores para compilar su programa para que funcione correctamente. Los ABI cubren múltiples temas:
Echando un vistazo más profundo a la convención de llamadas, que considero el núcleo de un ABI:
La máquina en sí 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 eventualmente el ensamblador resolverá en una dirección. Esta etiqueta marca el "inicio" de su "función" en el código de ensamblaje. En el código de alto nivel, cuando "llama" a esa función, lo que realmente está 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 compilador debe hacer un montón de cosas importantes. La convención de llamadas es como una lista de verificación que el compilador sigue para hacer todo esto:
_MyFunction1:
). En este punto, puede considerar que la CPU está "en" su "función".Existen muchas ABI / convenciones de llamada diferentes. Algunos principales son:
Aquí hay una gran página que muestra las diferencias en el ensamblaje generado al compilar diferentes ABI.
Otra cosa a mencionar es que un ABI no solo es relevante dentro del módulo ejecutable de su programa. El enlazador también lo utiliza 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 usan cada una, puede llamar a sus funciones correctamente sin explotar 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 núcleo.
fuente
Una interfaz binaria de aplicación (ABI) es similar a una API, pero la persona que llama no puede acceder a la función a nivel de código fuente. Solo una representación binaria es accesible / disponible.
Las ABI se pueden definir a nivel de arquitectura de procesador o a nivel de sistema operativo. Los ABI son estándares a seguir por la fase del generador de código del compilador. El estándar lo fija el sistema operativo o el procesador.
Funcionalidad: defina el mecanismo / estándar para hacer llamadas de función independientes del lenguaje de implementación o 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 de máquina.
Consumidor: otra función (incluida una en otro idioma, compilada por otro compilador o vinculada por otro vinculador).
fuente
Funcionalidad: conjunto de contratos que afectan al compilador, a los redactores de ensamblajes, al vinculador y al sistema operativo. Los contratos especifican cómo se presentan las funciones, dónde se pasan los parámetros, cómo se pasan los parámetros, cómo funcionan los retornos de función. Generalmente son específicos de una tupla (arquitectura de procesador, sistema operativo).
Entidades existentes: diseño de parámetros, semántica de funciones, asignación de registros. Por ejemplo, las arquitecturas ARM tienen numerosas ABI (APCS, EABI, GNU-EABI, no importa un montón de casos históricos): el uso de una ABI mixta hará que su código simplemente no funcione al llamar a través de los límites.
Consumidor: El compilador, los escritores de ensamblaje, el sistema operativo, la arquitectura específica de la CPU.
¿Quién necesita estos detalles? El compilador, los escritores de ensamblajes, los enlazadores que generan código (o los requisitos de alineación), el sistema operativo (manejo de interrupciones, interfaz syscall). Si hiciste la programación de ensamblaje, ¡te estabas ajustando a un ABI!
El cambio de nombre de C ++ es un caso especial, es un tema centrado en el enlazador y el enlazador dinámico, si el cambio de nombre no está estandarizado, entonces el enlace dinámico no funcionará. 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 tenga un binario C ++, no es posible hacerlo compatible con otro ABI C ++ (cambio de nombre, manejo de excepciones) sin volver a compilar desde la fuente.
ELF es un formato de archivo para el uso de un cargador y un vinculador 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 ELF como un ABI en sentido estricto, ya que los ejecutables de PE no son un ABI.
Todas las ABI son específicas del conjunto de instrucciones. Un ABM ARM no tendrá sentido en un procesador MSP430 o x86_64.
Windows tiene varias ABI, por ejemplo, fastcall y stdcall son dos ABI de uso común. El syscall ABI es diferente de nuevo.
fuente
Al menos déjame responder una parte de tu pregunta. Con un ejemplo de cómo la ABI de Linux afecta las llamadas al sistema y por qué eso es útil.
Una llamada al sistema es una forma para que un programa de espacio de usuario le pida algo al kernelspace. Funciona colocando el código numérico para la llamada y el argumento en un determinado registro y desencadenando una interrupción. Entonces, se produce un cambio al kernelspace y el kernel busca el código numérico y el argumento, maneja la solicitud, coloca el resultado nuevamente en un registro y activa un cambio nuevamente al espacio de usuario. Esto es necesario, por ejemplo, cuando la aplicación quiere 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, que se definen en un archivo de encabezado específico del sistema. Mientras estos códigos de operación permanezcan igual, puede ejecutar los mismos programas compilados de usuario con diferentes núcleos actualizados sin tener que volver a compilar. Entonces tiene una interfaz utilizada por binarios precompilados, de ahí ABI.
fuente
Para llamar al código en bibliotecas compartidas, o código de llamada entre unidades de compilación, el archivo objeto debe contener etiquetas para las llamadas. C ++ manipula los nombres de las etiquetas de 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.
fuente
La mejor manera de diferenciar entre ABI y API es saber por qué y para qué se utiliza:
Para x86-64 generalmente hay una 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/
Conocer el ABI y asumir que otro compilador también lo sigue, luego los binarios teóricamente saben cómo llamarse entre sí (bibliotecas API en particular) y pasar parámetros sobre la pila o por registros, etc. O qué registros se cambiarán al llamar a las funciones, etc. Esencialmente, este conocimiento ayudará al software a integrarse entre sí. Conociendo el orden de los registros / diseño de la pila, puedo armar fácilmente diferentes programas escritos en ensamblajes sin mucho problema.
Pero las API son diferentes:
Es un nombre de funciones de alto nivel, con un argumento definido, de modo que si se compilan diferentes piezas de software utilizando estas API, PUEDEN poder llamarse entre sí. Pero se debe cumplir un requisito adicional de SAME ABI.
Por ejemplo, Windows solía ser compatible con la API POSIX:
https://en.wikipedia.org/wiki/Windows_Services_for_UNIX
https://en.wikipedia.org/wiki/POSIX
Y Linux también es compatible con POSIX. Pero los archivos binarios no se pueden mover y ejecutar de inmediato. Pero debido a que usaron los mismos NOMBRES en la API compatible con POSIX, puede tomar el mismo software en C, recompilarlo en los diferentes sistemas operativos e inmediatamente ejecutarlo.
Las API están destinadas a facilitar la integración del software: etapa de precompilación. Entonces, después de la compilación, el software puede verse totalmente diferente, si los ABI son diferentes.
ABI está destinado a definir la integración exacta de software a nivel binario / ensamblador.
fuente
SYS_execve
es 11 en Linux de 32 bits, pero 59 en FreeBSD.personality(2)
puede establecerPER_BSD
. Creo que recuerdo haber vistopersonality(PER_LINUX)
en lastrace
salida todo el tiempo, pero los binarios modernos de Linux de 64 bits ya no lo hacen.Ejemplo de ABI ejecutable mínimo de biblioteca compartida de Linux
En el contexto de las bibliotecas compartidas, la implicación más importante de "tener un ABI estable" es que no necesita volver a compilar sus programas después de que la biblioteca cambie.
Así por ejemplo:
si está vendiendo una biblioteca compartida, le ahorra a sus usuarios la molestia de volver a compilar todo lo que depende de su biblioteca para cada nueva versión
si está vendiendo un programa de código cerrado que depende de una biblioteca compartida presente en la distribución del usuario, puede liberar y probar menos precompilaciones si está seguro de que ABI es estable en ciertas versiones del sistema operativo de destino.
Esto es especialmente importante en el caso de la biblioteca estándar C, a la que se vinculan muchos programas en su sistema.
Ahora quiero proporcionar un ejemplo ejecutable concreto mínimo de esto.
C Principal
mylib.c
mylib.h
Compila y funciona bien con:
Ahora, supongamos que para v2 de la biblioteca, queremos agregar un nuevo campo a
mylib_mystruct
llamadonew_field
.Si agregamos el campo antes
old_field
como en:y reconstruyó la biblioteca pero no
main.out
, ¡entonces la afirmación falla!Esto es porque la línea:
había generado un ensamblaje que intenta acceder al primero
int
de la estructura, que ahora es ennew_field
lugar del esperadoold_field
.Por lo tanto, este cambio rompió el ABI.
Sin embargo, si agregamos
new_field
después deold_field
:entonces el antiguo ensamblado generado aún accede al primero
int
de la estructura, y el programa aún funciona, porque mantuvimos el ABI estable.Aquí hay una versión completamente automatizada de este ejemplo en GitHub .
Otra forma de mantener esta ABI estable habría sido tratarla
mylib_mystruct
como una estructura opaca , y solo acceder a sus campos a través de métodos auxiliares. Esto hace que sea más fácil mantener estable la ABI, pero incurriría en una sobrecarga de rendimiento ya que haríamos más llamadas a funciones.API vs ABI
En el ejemplo anterior, es interesante notar que agregar el
new_field
anteriorold_field
solo rompió la ABI, pero no la API.Lo que esto significa es que si hubiéramos compilado nuestro
main.c
programa contra la biblioteca, habría funcionado de todos modos.Sin embargo, también habríamos roto la API si hubiéramos cambiado, por ejemplo, la firma de la función:
ya que en ese caso,
main.c
dejaría de compilarse por completo.API semántica vs API de programación
También podemos clasificar los cambios de API en un tercer tipo: cambios semánticos.
La API semántica, por lo general, es una descripción en lenguaje natural de lo que se supone que debe hacer la API, generalmente incluida en la documentación de la API.
Por lo tanto, es posible romper la API semántica sin romper la compilación del programa.
Por ejemplo, si hubiéramos modificado
a:
entonces esto no habría roto ni la API de programación ni ABI, pero
main.c
la API semántica se rompería.Hay dos formas de verificar mediante programación la API del contrato:
verificación formal . Es más difícil de hacer, pero produce una prueba matemática de corrección, esencialmente unificando la documentación y las pruebas en una forma "humana" / máquina verificable. Siempre que no haya un error en su descripción formal, por supuesto ;-)
Este concepto está estrechamente relacionado con la formalización de las matemáticas en sí: /math/53969/what-does-formal-mean/3297537#3297537
Lista de todo lo que rompe las ABI de la biblioteca compartida de C / C ++
TODO: encuentra / crea la lista definitiva:
Ejemplo ejecutable mínimo de Java
¿Qué es la compatibilidad binaria en Java?
Probado en Ubuntu 18.10, GCC 8.2.0.
fuente
La ABI debe ser coherente entre la persona que llama y la persona que llama para asegurarse de que la llamada tiene éxito. Uso de pila, uso de registro, pop de pila al final de la rutina. Todas estas son las partes más importantes del ABI.
fuente
Resumen
Existen diversas interpretaciones y opiniones firmes sobre la capa exacta que define una ABI (interfaz binaria de aplicación).
En mi opinión, un ABI es una convención subjetiva de lo que se considera una plataforma / dado para una API específica. El ABI es el "resto" de convenciones que "no cambiarán" para una API específica o que serán atendidas por el entorno de ejecución: ejecutores, herramientas, vinculadores, compiladores, jvm y OS.
Definición de una interfaz : ABI, API
Si desea utilizar una biblioteca como joda-time, debe declarar una dependencia
joda-time-<major>.<minor>.<patch>.jar
. La biblioteca sigue las mejores prácticas y utiliza el control de versiones semántico . Esto define la compatibilidad API en tres niveles:Para que pueda utilizar una nueva versión principal de la misma biblioteca, aún se deben respetar muchas otras convenciones:
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 estudios de caso interesantes para ABI: las versiones de Scala y la máquina virtual Dalvik .
Dalvik virtual machine rompió el ABI
Dalvik VM necesita un tipo diferente de bytecode que el bytecode de Java. Las bibliotecas de Dalvik se obtienen convirtiendo el código de bytes 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 llamarmejoda-time-1.7.2.jar
yjoda-time-1.7.2-dalvik.jar
. Utilizan un ABI diferente para el Java vms estándar orientado a la pila: el de Oracle, el de IBM, Java abierto o cualquier otro; y el segundo ABI es el que está alrededor de Dalvik.Los sucesivos lanzamientos 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 ahora , pero los binarios no son compatibles. Probablemente, las últimas versiones agregan elementos que hacen que las bibliotecas sean inutilizables en las máquinas virtuales antiguas, probablemente elementos relacionados con las convenciones de vinculación / nomenclatura / parámetros.
Las versiones sucesivas de Java son incompatibles
Java también tiene problemas con las principales versiones de la JVM: 4,5,6,7,8,9. Ofrecen solo compatibilidad con versiones anteriores. Jvm9 sabe cómo ejecutar código compilado / dirigido (
-target
opción de javac ) para todas las demás versiones, mientras que JVM 4 no sabe cómo ejecutar código dirigido para JVM 5. Todo esto mientras tiene una biblioteca joda. Esta incompatibilidad vuela por debajo del radar gracias a diferentes soluciones:¿Por qué comencé con la definición de 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ántica de alto nivel. Por eso es fácil hacer algunas convenciones. El primer tipo de convenciones se trata de la alineación de la memoria, la codificación de bytes, las convenciones de llamada, las codificaciones endian grandes y pequeñas, etc. Además de ellas, se obtienen las convenciones ejecutables como otras descritas, las convenciones de enlace, el código de bytes intermedios como el utilizado por Java o LLVM IR utilizado por GCC. Tercero, obtienes convenciones sobre cómo encontrar bibliotecas, cómo cargarlas (ver cargadores de clases Java). A medida que avanzas más y más en conceptos, tienes nuevas convenciones que consideras como un hecho. Es por eso que no llegaron a las versiones semánticas .versión. Podríamos modificar las versiones semánticas con
<major>-<minor>-<patch>-<platform/ABI>
. Esto es lo que realmente está sucediendo ya: plataforma ya es unrpm
,dll
,jar
(JVM bytecode),war
(JVM + servidor web),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 se puede portar a diferentes ABI
El nivel superior de una abstracción (las fuentes escritas en la API más alta se pueden volver a 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 volver a compilarlas. Si la JVM cambia, podría tener conversiones automáticas de la máquina vieja a la nueva sin molestarme con los conceptos de alto nivel. Si bien la transferencia 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.
API portadas a través de idiomas
Hay API que se portan en varios idiomas, como secuencias reactivas . 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 un lenguaje de programación específico. Todos los otros "mapeos" son ABI en cierto sentido, más API que el ABI habitual. Lo mismo está sucediendo con las interfaces REST.
fuente
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 cosas de software que funcionan juntas.
fuente
También estaba tratando de entender ABI y la respuesta de JesperE fue muy útil.
Desde una perspectiva muy simple, podemos tratar de entender ABI considerando la compatibilidad binaria.
El wiki de KDE define una biblioteca como compatible con binarios "si un programa vinculado dinámicamente a una versión anterior de la biblioteca continúa ejecutándose con versiones más nuevas de la biblioteca sin la necesidad de volver a compilar". Para más información sobre el enlace dinámico, consulte Enlace estático vs enlace dinámico
Ahora, tratemos de ver solo los aspectos más básicos necesarios para que una biblioteca tenga compatibilidad binaria (suponiendo que no haya cambios en el código fuente de la biblioteca):
Claro, hay muchos otros detalles, pero esto es principalmente lo que cubre el ABI.
Más específicamente para responder a su pregunta, de lo anterior, podemos deducir:
¡Espero que esto ayude!
fuente
Interfaz binaria de aplicación (ABI)
Funcionalidad
Entidades existentes:
consumidor:
Quienes los necesiten deben asegurarse de 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 que su propio cargador de arranque quiera usar un sistema operativo, sus módulos de "aplicación" están trabajando a través de límites "binarios" y requieren el acuerdo de dicha "interfaz".
Cambio de nombre de C ++ porque los archivos de objetos de diferentes lenguajes de alto nivel pueden estar obligados a vincularse en su aplicación. Considere usar la biblioteca estándar de GCC para realizar llamadas al sistema a Windows creadas con Visual C ++.
ELF es una expectativa posible del vinculador desde 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 que algunas cadenas de herramientas de compilación funcionen juntas.
fuente
El término ABI se usa 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.
y el programador de aplicaciones escribe código como
El programador de la aplicación 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 usa el nuevo binario de la biblioteca con el viejo binario de la aplicación, entonces la biblioteca puede hacer accesos de memoria fuera de los límites.
OTOH si el autor de la biblioteca hubiera diseñado su API como.
y el programador de aplicaciones escribe código como
Entonces, el binario de la aplicación no necesita saber nada sobre la estructura de FOO, todo eso puede estar oculto dentro de la biblioteca. Sin embargo, el precio que paga por eso es que están involucradas las operaciones de almacenamiento dinámico.
fuente
ABI
-Application Binary Interface
se trata de una comunicación de código de máquina en tiempo de ejecución entre dos partes de programas binarios como - aplicación, biblioteca, sistema operativo ...ABI
describe cómo se guardan los objetos en la memoria y cómo se llaman las funciones (calling convention
)Un buen ejemplo de API y ABI es el ecosistema iOS con lenguaje Swift .
Application
- Cuando crea una aplicación usando diferentes idiomas. Por ejemplo, puede crear aplicaciones usandoSwift
yObjective-C
[Mezclando Swift y Objective-C]Application - OS
- tiempo de ejecución -Swift runtime
ystandard libraries
son partes del sistema operativo y no deben incluirse en cada paquete (por ejemplo, aplicación, marco). Es lo mismo que utiliza Objective-CLibrary
-Module Stability
caso - tiempo de compilación - podrá importar un marco creado con otra versión del compilador de Swift. Significa que es seguro crear un binario de código cerrado (precompilación) que será consumido por una versión diferente del compilador (.swiftinterface
se usa con.swiftmodule
) y no obtendráLibrary
-Library Evolution
caso[API vs ABI]
fuente