Je n'ai jamais clairement compris ce qu'est un ABI. Veuillez ne pas me diriger vers un article Wikipedia. Si je pouvais comprendre, je ne serais pas ici pour publier un article aussi long.
C'est mon état d'esprit sur les différentes interfaces:
Une télécommande TV est une interface entre l'utilisateur et le téléviseur. C'est une entité existante, mais inutile (ne fournit aucune fonctionnalité) par elle-même. Toutes les fonctionnalités de chacun de ces boutons de la télécommande sont implémentées dans le téléviseur.
Interface: Il s'agit d'une couche "entité existante" entre le
functionality
etconsumer
de cette fonctionnalité. Une interface seule ne fait rien. Il invoque simplement la fonctionnalité qui se trouve derrière.Maintenant, selon qui est l'utilisateur, il existe différents types d'interfaces.
Les commandes CLI (Interface de ligne de commande) sont les entités existantes, le consommateur est l'utilisateur et la fonctionnalité est derrière.
functionality:
ma fonctionnalité logicielle qui résout un objectif auquel nous décrivons cette interface.
existing entities:
commandes
consumer:
utilisateurLa fenêtre de l' interface utilisateur graphique (GUI) , les boutons, etc. sont les entités existantes, et encore une fois le consommateur est l'utilisateur et la fonctionnalité est derrière.
functionality:
ma fonctionnalité logicielle qui résout un problème auquel nous décrivons cette interface.
existing entities:
fenêtre, boutons etc.
consumer:
utilisateurLes fonctions d'interface de programmation d'application (API) (ou, pour être plus correct), les interfaces (dans la programmation basée sur l'interface) sont les entités existantes, le consommateur est un autre programme et non un utilisateur, et là encore, la fonctionnalité se trouve derrière cette couche.
functionality:
ma fonctionnalité logicielle qui résout un problème auquel nous décrivons cette interface.
existing entities:
fonctions, Interfaces (tableau de fonctions).
consumer:
un autre programme / application.Interface binaire d'application (ABI) Voici où commence mon problème.
functionality:
???
existing entities:
???
consumer:
???
- J'ai écrit des logiciels dans différentes langues et fourni différents types d'interfaces (CLI, GUI et API), mais je ne sais pas si j'ai déjà fourni une ABI.
Les ABI couvrent des détails tels que
- type, taille et alignement des données;
- la convention d'appel, qui contrôle la façon dont les arguments des fonctions sont passés et les valeurs de retour récupérées;
- les numéros d'appel système et comment une application doit effectuer des appels système vers le système d'exploitation;
D'autres ABI standardisent des détails tels que
- le changement de nom C ++,
- propagation d'exception, et
- convention d'appel entre compilateurs sur la même plate-forme, mais ne nécessite pas de compatibilité entre plates-formes.
Qui a besoin de ces détails? Veuillez ne pas dire le système d'exploitation. Je connais la programmation d'assemblage. Je sais comment fonctionne le lien et le chargement. Je sais exactement ce qui se passe à l'intérieur.
Pourquoi le changement de nom C ++ est-il entré? Je pensais que nous parlons au niveau binaire. Pourquoi les langues entrent-elles?
Quoi qu'il en soit, j'ai téléchargé [PDF] System V Application Binary Interface Edition 4.1 (1997-03-18) pour voir ce qu'il contient exactement. Eh bien, la plupart n'avaient aucun sens.
Pourquoi contient-il deux chapitres (4e et 5e) pour décrire le format de fichier ELF ? En fait, ce sont les deux seuls chapitres importants de cette spécification. Les autres chapitres sont "spécifiques au processeur". Quoi qu'il en soit, je pensais que c'était un sujet complètement différent. Veuillez ne pas dire que les spécifications du format de fichier ELF sont l'ABI. Il ne remplit pas les conditions requises pour être une interface selon la définition.
Je sais, puisque nous parlons à un niveau aussi bas, cela doit être très précis. Mais je ne sais pas en quoi est-ce que "l'architecture de jeu d'instructions (ISA)" est spécifique?
Où puis-je trouver l'ABI de Microsoft Windows?
Ce sont donc les principales requêtes qui me dérangent.
la source
Réponses:
Une façon simple de comprendre "ABI" est de le comparer à "API".
Vous connaissez déjà le concept d'une API. Si vous souhaitez utiliser les fonctionnalités de, disons, une bibliothèque ou votre système d'exploitation, vous programmez contre une API. L'API se compose de types / structures de données, constantes, fonctions, etc. que vous pouvez utiliser dans votre code pour accéder aux fonctionnalités de ce composant externe.
Un ABI est très similaire. Considérez-le comme la version compilée d'une API (ou comme une API au niveau du langage machine). Lorsque vous écrivez du code source, vous accédez à la bibliothèque via une API. Une fois le code compilé, votre application accède aux données binaires de la bibliothèque via l'ABI. L'ABI définit les structures et les méthodes que votre application compilée utilisera pour accéder à la bibliothèque externe (comme l'a fait l'API), uniquement à un niveau inférieur. Votre API définit l'ordre dans lequel vous passez des arguments à une fonction. Votre ABI définit la mécanique de la façon dontces arguments sont passés (registres, pile, etc.). Votre API définit les fonctions qui font partie de votre bibliothèque. Votre ABI définit comment votre code est stocké dans le fichier de bibliothèque, de sorte que tout programme utilisant votre bibliothèque puisse localiser la fonction souhaitée et l'exécuter.
Les ABI sont importants en ce qui concerne les applications qui utilisent des bibliothèques externes. Les bibliothèques sont pleines de code et d'autres ressources, mais votre programme doit savoir comment localiser ce dont il a besoin dans le fichier de bibliothèque. Votre ABI définit la façon dont le contenu d'une bibliothèque est stocké dans le fichier, et votre programme utilise l'ABI pour rechercher dans le fichier et trouver ce dont il a besoin. Si tout dans votre système est conforme au même ABI, n'importe quel programme peut fonctionner avec n'importe quel fichier de bibliothèque, peu importe qui les a créés. Linux et Windows utilisent des ABI différents, donc un programme Windows ne saura pas comment accéder à une bibliothèque compilée pour Linux.
Parfois, les changements ABI sont inévitables. Lorsque cela se produit, tous les programmes qui utilisent cette bibliothèque ne fonctionneront pas, sauf s'ils sont recompilés pour utiliser la nouvelle version de la bibliothèque. Si l'ABI change mais l'API ne change pas, alors l'ancienne et la nouvelle version de la bibliothèque sont parfois appelées "compatible avec la source". Cela implique que même si un programme compilé pour une version de bibliothèque ne fonctionnera pas avec l'autre, le code source écrit pour l'une fonctionnera pour l'autre s'il est recompilé.
Pour cette raison, les développeurs ont tendance à essayer de maintenir leur ABI stable (pour minimiser les perturbations). Garder un ABI stable signifie ne pas changer les interfaces de fonction (type et nombre de retour, types et ordre des arguments), les définitions des types de données ou des structures de données, les constantes définies, etc. De nouvelles fonctions et types de données peuvent être ajoutés, mais les existants doivent rester le même. Si, par exemple, votre bibliothèque utilise des entiers 32 bits pour indiquer le décalage d'une fonction et que vous basculez vers des entiers 64 bits, le code déjà compilé qui utilise cette bibliothèque n'accédera pas correctement à ce champ (ou à tout autre suivi) . L'accès aux membres de la structure de données est converti en adresses de mémoire et décalages lors de la compilation et si la structure de données change,
Un ABI n'est pas nécessairement quelque chose que vous fournirez explicitement, sauf si vous effectuez un travail de conception de systèmes de très bas niveau. Il n'est pas spécifique au langage non plus, car (par exemple) une application C et une application Pascal peuvent utiliser le même ABI après leur compilation.
Éditer:Concernant votre question sur les chapitres concernant le format de fichier ELF dans les documents SysV ABI: La raison pour laquelle ces informations sont incluses est que le format ELF définit l'interface entre le système d'exploitation et l'application. Lorsque vous dites au système d'exploitation d'exécuter un programme, il s'attend à ce que le programme soit formaté d'une certaine manière et (par exemple) s'attend à ce que la première section du binaire soit un en-tête ELF contenant certaines informations à des décalages de mémoire spécifiques. C'est ainsi que l'application communique des informations importantes sur elle-même au système d'exploitation. Si vous créez un programme dans un format binaire non ELF (tel que a.out ou PE), un système d'exploitation qui attend des applications au format ELF ne pourra pas interpréter le fichier binaire ni exécuter l'application.
IIRC, Windows utilise actuellement le format Portable Executable (ou PE). Il y a des liens dans la section "liens externes" de cette page Wikipedia avec plus d'informations sur le format PE.
En outre, en ce qui concerne votre remarque sur le changement de nom C ++: lors de la localisation d'une fonction dans un fichier de bibliothèque, la fonction est généralement recherchée par son nom. C ++ vous permet de surcharger les noms de fonctions, donc le nom seul n'est pas suffisant pour identifier une fonction. Les compilateurs C ++ ont leurs propres façons de gérer cela en interne, appelés mangling de nom . Un ABI peut définir une manière standard d'encoder le nom d'une fonction afin que les programmes construits avec un langage ou un compilateur différent puissent localiser ce dont ils ont besoin. Lorsque vous utilisez
extern "c"
dans un programme C ++, vous demandez au compilateur d'utiliser une manière standardisée d'enregistrer des noms compréhensibles par d'autres logiciels.la source
Si vous connaissez l'assemblage et comment les choses fonctionnent au niveau du système d'exploitation, vous vous conformez à un certain ABI. L'ABI régit des choses comme la façon dont les paramètres sont passés, où les valeurs de retour sont placées. Pour de nombreuses plates-formes, il n'y a qu'un seul ABI parmi lequel choisir, et dans ces cas, l'ABI est simplement "comment les choses fonctionnent".
Cependant, l'ABI régit également des choses comme la façon dont les classes / objets sont disposés en C ++. Ceci est nécessaire si vous voulez pouvoir passer des références d'objet à travers les limites du module ou si vous voulez mélanger du code compilé avec différents compilateurs.
De plus, si vous avez un système d'exploitation 64 bits qui peut exécuter des binaires 32 bits, vous aurez différents ABI pour le code 32 et 64 bits.
En général, tout code que vous liez au même exécutable doit être conforme au même ABI. Si vous souhaitez communiquer entre du code utilisant différents ABI, vous devez utiliser une certaine forme de protocole RPC ou de sérialisation.
Je pense que vous essayez trop fort de presser différents types d'interfaces dans un ensemble fixe de caractéristiques. Par exemple, une interface ne doit pas nécessairement être divisée en consommateurs et en producteurs. Une interface n'est qu'une convention par laquelle deux entités interagissent.
Les ABI peuvent être (partiellement) agnostiques à l'ISA. Certains aspects (tels que les conventions d'appel) dépendent de l'ISA, tandis que d'autres aspects (tels que la disposition des classes C ++) n'en dépendent pas.
Un ABI bien défini est très important pour les personnes qui écrivent des compilateurs. Sans un ABI bien défini, il serait impossible de générer du code interopérable.
EDIT: Quelques notes pour clarifier:
la source
En fait, vous n'avez pas du tout besoin d'un ABI si ...
Un résumé trop simplifié:
L'ABI est un ensemble de règles auxquelles les compilateurs et les éditeurs de liens adhèrent afin de compiler votre programme afin que cela fonctionne correctement. Les ABI couvrent plusieurs sujets:
Un examen plus approfondi de la convention d'appel, que je considère comme le cœur d'un ABI:
La machine elle-même n'a pas de concept de "fonctions". Lorsque vous écrivez une fonction dans un langage de haut niveau comme c, le compilateur génère une ligne de code assembleur comme
_MyFunction1:
. Il s'agit d'une étiquette qui sera finalement résolue en une adresse par l'assembleur. Cette étiquette marque le "début" de votre "fonction" dans le code d'assemblage. Dans le code de haut niveau, lorsque vous "appelez" cette fonction, ce que vous faites vraiment, c'est que le CPU saute à l'adresse de cette étiquette et continue de s'exécuter là-bas.En préparation pour le saut, le compilateur doit faire un tas de choses importantes. La convention d'appel est comme une liste de contrôle que le compilateur suit pour faire tout cela:
_MyFunction1:
). À ce stade, vous pouvez considérer que le CPU est "dans" votre "fonction".Il existe de nombreuses ABI / conventions d'appel différentes. Certains principaux sont:
Ici une excellente page qui montre réellement les différences dans l'assembly généré lors de la compilation pour différents ABI.
Une autre chose à mentionner est qu'un ABI n'est pas seulement pertinent à l' intérieur du module exécutable de votre programme. Il est également utilisé par l'éditeur de liens pour s'assurer que votre programme appelle correctement les fonctions de la bibliothèque. Vous avez plusieurs bibliothèques partagées en cours d'exécution sur votre ordinateur, et tant que votre compilateur sait quel ABI il utilise, il peut appeler des fonctions correctement sans faire exploser la pile.
Votre compilateur comprenant comment appeler les fonctions de bibliothèque est extrêmement important. Sur une plate-forme hébergée (c'est-à-dire une plate-forme où un système d'exploitation charge des programmes), votre programme ne peut même pas clignoter sans effectuer d'appel du noyau.
la source
Une interface binaire d'application (ABI) est similaire à une API, mais la fonction n'est pas accessible à l'appelant au niveau du code source. Seule une représentation binaire est accessible / disponible.
Les ABI peuvent être définis au niveau de l'architecture du processeur ou au niveau du système d'exploitation. Les ABI sont des normes à suivre par la phase de génération de code du compilateur. La norme est fixée soit par l'OS soit par le processeur.
Fonctionnalité: Définissez le mécanisme / la norme pour effectuer des appels de fonction indépendamment du langage d'implémentation ou d'un compilateur / éditeur de liens / chaîne d'outils spécifique. Fournissez le mécanisme qui autorise JNI, ou une interface Python-C, etc.
Entités existantes: fonctions sous forme de code machine.
Consommateur: autre fonction (dont une dans une autre langue, compilée par un autre compilateur ou liée par un autre éditeur de liens).
la source
Fonctionnalité: ensemble de contrats affectant le compilateur, les rédacteurs d'assemblage, l'éditeur de liens et le système d'exploitation. Les contrats spécifient comment les fonctions sont disposées, où les paramètres sont passés, comment les paramètres sont passés, comment les retours de fonction fonctionnent. Celles-ci sont généralement spécifiques à un tuple (architecture de processeur, système d'exploitation).
Entités existantes: disposition des paramètres, sémantique des fonctions, allocation des registres. Par exemple, les architectures ARM ont de nombreux ABI (APCS, EABI, GNU-EABI, sans parler d'un tas de cas historiques) - en utilisant un ABI mixte, votre code ne fonctionnera tout simplement pas lors d'appels au-delà des frontières.
Consommateur: compilateur, rédacteurs d'assemblage, système d'exploitation, architecture spécifique au processeur.
Qui a besoin de ces détails? Le compilateur, les rédacteurs d'assemblage, les éditeurs de liens qui génèrent du code (ou les exigences d'alignement), le système d'exploitation (gestion des interruptions, interface syscall). Si vous faisiez de la programmation d'assemblage, vous vous conformiez à un ABI!
Le mangling de nom C ++ est un cas spécial - c'est un problème lié à l'éditeur de liens et au linker dynamique - si le mangling de nom n'est pas standardisé, alors la liaison dynamique ne fonctionnera pas. Désormais, le C ++ ABI est appelé juste ainsi, le C ++ ABI. Ce n'est pas un problème au niveau de l'éditeur de liens, mais plutôt un problème de génération de code. Une fois que vous avez un binaire C ++, il n'est pas possible de le rendre compatible avec un autre ABI C ++ (gestion de nom, gestion des exceptions) sans recompilation à partir de la source.
ELF est un format de fichier pour l'utilisation d'un chargeur et d'un éditeur de liens dynamique. ELF est un format de conteneur pour le code binaire et les données, et en tant que tel spécifie l'ABI d'un morceau de code. Je ne considérerais pas ELF comme un ABI au sens strict, car les exécutables PE ne sont pas un ABI.
Tous les ABI sont spécifiques au jeu d'instructions. Un ARM ABI n'a aucun sens sur un processeur MSP430 ou x86_64.
Windows a plusieurs ABI - par exemple, fastcall et stdcall sont deux ABI d'usage courant. Le syscall ABI est à nouveau différent.
la source
Permettez-moi au moins de répondre à une partie de votre question. Avec un exemple de la façon dont l'ABI Linux affecte les appels système et pourquoi cela est utile.
Un appel système est un moyen pour un programme de l'espace utilisateur de demander quelque chose au noyau. Il fonctionne en plaçant le code numérique de l'appel et de l'argument dans un certain registre et en déclenchant une interruption. Ensuite, un basculement se produit vers kernelspace et le noyau recherche le code numérique et l'argument, gère la demande, remet le résultat dans un registre et déclenche un basculement vers l'espace utilisateur. Cela est nécessaire par exemple lorsque l'application souhaite allouer de la mémoire ou ouvrir un fichier (syscalls "brk" et "open").
Maintenant, les appels système ont des noms courts "brk", etc. et les opcodes correspondants, ils sont définis dans un fichier d'en-tête spécifique au système. Tant que ces opcodes restent les mêmes, vous pouvez exécuter les mêmes programmes utilisateur compilés avec différents noyaux mis à jour sans avoir à recompiler. Vous avez donc une interface utilisée par les binaires précompilés, d'où ABI.
la source
Pour appeler du code dans des bibliothèques partagées ou du code d'appel entre des unités de compilation, le fichier objet doit contenir des étiquettes pour les appels. C ++ modifie les noms des étiquettes de méthode afin d'imposer le masquage des données et de permettre des méthodes surchargées. C'est pourquoi vous ne pouvez pas mélanger des fichiers de différents compilateurs C ++ à moins qu'ils ne prennent explicitement en charge le même ABI.
la source
La meilleure façon de faire la différence entre ABI et API est de savoir pourquoi et à quoi sert-elle:
Pour x86-64, il y a généralement un ABI (et pour x86 32 bits, il y en a un autre):
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 le suivent avec quelques légères variations. Et Windows x64 possède son propre ABI:
http://eli.thegreenplace.net/2011/09/06/stack-frame-layout-on-x86-64/
Connaître l'ABI et supposer qu'un autre compilateur le suit également, alors les binaires savent théoriquement comment s'appeler (API de bibliothèques en particulier) et passer des paramètres sur la pile ou par des registres, etc. Ou quels registres seront modifiés lors de l'appel des fonctions, etc. Ces connaissances aideront essentiellement les logiciels à s'intégrer les uns aux autres. Connaissant l'ordre de la disposition des registres / piles, je peux facilement assembler différents logiciels écrits en assemblages sans trop de problème.
Mais les API sont différentes:
Il s'agit d'un nom de fonctions de haut niveau, avec un argument défini, de sorte que si différentes pièces de logiciel sont construites à l'aide de ces API, PEUVENT être capables de s'appeler les unes les autres. Mais une exigence supplémentaire de SAME ABI doit être respectée.
Par exemple, Windows était auparavant compatible avec l'API POSIX:
https://en.wikipedia.org/wiki/Windows_Services_for_UNIX
https://en.wikipedia.org/wiki/POSIX
Et Linux est également compatible POSIX. Mais les binaires ne peuvent pas être simplement déplacés et exécutés immédiatement. Mais comme ils utilisaient les mêmes NOMS dans l'API compatible POSIX, vous pouvez prendre le même logiciel en C, le recompiler dans les différents OS et le faire fonctionner immédiatement.
Les API visent à faciliter l'intégration des logiciels - étape de pré-compilation. Ainsi, après la compilation, le logiciel peut être totalement différent - si les ABI sont différents.
Les ABI sont destinés à définir l'intégration exacte des logiciels au niveau binaire / assembleur.
la source
SYS_execve
c'est 11 sur 32 bits linux, mais 59 sur FreeBSD.personality(2)
peut définirPER_BSD
. Je pense que je me souviens d'avoir vupersonality(PER_LINUX)
enstrace
sortie tout le temps, mais les binaires Linux 64 bits modernes ne le font plus.Exemple ABI exécutable minimal de bibliothèque partagée Linux
Dans le contexte des bibliothèques partagées, l'implication la plus importante d '"avoir un ABI stable" est que vous n'avez pas besoin de recompiler vos programmes après les changements de bibliothèque.
Ainsi, par exemple:
si vous vendez une bibliothèque partagée, vous évitez à vos utilisateurs l'ennui de recompiler tout ce qui dépend de votre bibliothèque pour chaque nouvelle version
si vous vendez un programme source fermé qui dépend d'une bibliothèque partagée présente dans la distribution de l'utilisateur, vous pouvez libérer et tester moins de préconstructions si vous êtes certain qu'ABI est stable sur certaines versions du système d'exploitation cible.
Ceci est particulièrement important dans le cas de la bibliothèque standard C, à laquelle de nombreux programmes de votre système sont liés.
Maintenant, je veux en fournir un exemple exécutable concret minimal.
principal c
mylib.c
mylib.h
Compile et fonctionne bien avec:
Supposons maintenant que pour la v2 de la bibliothèque, nous voulons ajouter un nouveau champ à
mylib_mystruct
appelénew_field
.Si nous avons ajouté le champ avant
old_field
comme dans:et reconstruit la bibliothèque mais pas
main.out
, alors l'assertion échoue!C'est parce que la ligne:
avait généré l'assembly qui tente d'accéder au tout premier
int
de la structure, qui est maintenantnew_field
au lieu de l'attenduold_field
.Par conséquent, ce changement a brisé l'ABI.
Si, cependant, nous ajoutons
new_field
aprèsold_field
:puis l'ancien assemblage généré accède toujours au premier
int
de la structure, et le programme fonctionne toujours, car nous avons maintenu l'ABI stable.Voici une version entièrement automatisée de cet exemple sur GitHub .
Une autre façon de maintenir cet ABI stable aurait été de traiter
mylib_mystruct
comme une structure opaque et d'accéder uniquement à ses champs via des assistants de méthode. Cela facilite la stabilité de l'ABI, mais entraînerait une surcharge de performances car nous ferions plus d'appels de fonctions.API vs ABI
Dans l'exemple précédent, il est intéressant de noter que l'ajout de l'
new_field
avantold_field
ne cassait que l'ABI, mais pas l'API.Ce que cela signifie, c'est que si nous avions recompilé notre
main.c
programme par rapport à la bibliothèque, cela aurait fonctionné malgré tout.Nous aurions également cassé l'API si nous avions changé par exemple la signature de la fonction:
puisque dans ce cas,
main.c
arrêterait complètement la compilation.API sémantique vs API de programmation
Nous pouvons également classer les changements d'API dans un troisième type: les changements sémantiques.
L'API sémantique est généralement une description en langage naturel de ce que l'API est censée faire, généralement incluse dans la documentation de l'API.
Il est donc possible de casser l'API sémantique sans casser la construction du programme lui-même.
Par exemple, si nous avions modifié
à:
alors cela n'aurait brisé ni l'API de programmation, ni l'ABI, mais
main.c
l'API sémantique se briserait.Il existe deux façons de vérifier par programme l'API de contrat:
vérification formelle . Plus difficile à faire, mais produit une preuve mathématique d'exactitude, unifiant essentiellement la documentation et les tests d'une manière vérifiable "humaine" / machine! Tant qu'il n'y a pas de bug dans votre description formelle bien sûr ;-)
Ce concept est étroitement lié à la formalisation des mathématiques elles-mêmes: /math/53969/what-does-formal-mean/3297537#3297537
Liste de tout ce qui casse les ABI des bibliothèques partagées C / C ++
TODO: trouvez / créez la liste ultime:
Exemple exécutable Java minimal
Qu'est-ce que la compatibilité binaire en Java?
Testé dans Ubuntu 18.10, GCC 8.2.0.
la source
L'ABI doit être cohérent entre l'appelant et l'appelé pour être certain que l'appel réussit. Utilisation de pile, utilisation de registre, pop de pile de fin de routine. Ce sont toutes les parties les plus importantes de l'ABI.
la source
Sommaire
Il existe différentes interprétations et opinions fortes sur la couche exacte qui définissent une ABI (interface binaire d'application).
À mon avis, un ABI est une convention subjective de ce qui est considéré comme une plate-forme donnée pour une API spécifique. L'ABI est le "reste" des conventions qui "ne changeront pas" pour une API spécifique ou qui seront traitées par l'environnement d'exécution: exécuteurs, outils, éditeurs de liens, compilateurs, jvm et OS.
Définition d'une interface : ABI, API
Si vous souhaitez utiliser une bibliothèque comme joda-time, vous devez déclarer une dépendance sur
joda-time-<major>.<minor>.<patch>.jar
. La bibliothèque suit les meilleures pratiques et utilise le versionnage sémantique . Cela définit la compatibilité de l'API à trois niveaux:Afin que vous puissiez utiliser une nouvelle version majeure de la même bibliothèque, de nombreuses autres conventions doivent encore être respectées:
Exemples
Étude de cas Java
Par exemple, Java a normalisé toutes ces conventions, non pas dans un outil, mais dans une spécification JVM formelle. La spécification a permis à d'autres fournisseurs de fournir un ensemble différent d'outils pouvant produire des bibliothèques compatibles.
Java fournit deux autres études de cas intéressantes pour ABI: les versions Scala et la machine virtuelle Dalvik .
La machine virtuelle Dalvik a cassé l'ABI
La machine virtuelle Dalvik a besoin d'un type de bytecode différent du bytecode Java. Les bibliothèques Dalvik sont obtenues en convertissant le bytecode Java (avec la même API) pour Dalvik. De cette façon, vous pouvez obtenir deux versions de la même API: définies par l'original
joda-time-1.7.2.jar
. On pourrait m'appelerjoda-time-1.7.2.jar
etjoda-time-1.7.2-dalvik.jar
. Ils utilisent un ABI différent pour les vms Java standard orientés pile: celui d'Oracle, celui d'IBM, Java ouvert ou tout autre; et le deuxième ABI est celui autour de Dalvik.Les versions successives de Scala sont incompatibles
Scala n'a pas de compatibilité binaire entre les versions mineures de Scala: 2.X. Pour cette raison, la même API "io.reactivex" %% "rxscala"% "0.26.5" a trois versions (à l'avenir plus): pour Scala 2.10, 2.11 et 2.12. Qu'est-ce qui a changé? Je ne sais pas pour l'instant , mais les binaires ne sont pas compatibles. Les dernières versions ajoutent probablement des éléments qui rendent les bibliothèques inutilisables sur les anciennes machines virtuelles, probablement des éléments liés aux conventions de liaison / d'attribution de noms / de paramètres.
Les versions successives de Java sont incompatibles
Java a également des problèmes avec les principales versions de la JVM: 4,5,6,7,8,9. Ils n'offrent qu'une compatibilité descendante. Jvm9 sait comment exécuter du code compilé / ciblé (
-target
option de javac ) pour toutes les autres versions, tandis que JVM 4 ne sait pas comment exécuter du code ciblé pour JVM 5. Tout cela pendant que vous avez une bibliothèque joda. Cette incompatibilité passe sous le radar grâce à différentes solutions:Pourquoi ai-je commencé avec la définition de l'API?
L'API et l'ABI ne sont que des conventions sur la façon dont vous définissez la compatibilité. Les couches inférieures sont génériques en ce qui concerne une pléthore de sémantique de haut niveau. C'est pourquoi il est facile de faire des conventions. Le premier type de conventions sont sur le point d' alignement de la mémoire, l' encodage des octets, les conventions d' appel, petits et grands encodages endian, etc. En plus d'entre eux vous obtenez les conventions exécutables comme les autres décrites, conventions liant, code binaire intermédiaire comme celui utilisé par Java ou LLVM IR utilisé par GCC. Troisièmement, vous obtenez des conventions sur la façon de trouver des bibliothèques, comment les charger (voir Chargeurs de classes Java). Au fur et à mesure que vous progressez dans les concepts, vous avez de nouvelles conventions que vous considérez comme une donnée. C'est pourquoi ils ne sont pas arrivés au versioning sémantique . Ils sont implicites ou effondrés dans leversion. Nous pourrions modifier le versioning sémantique avec
<major>-<minor>-<patch>-<platform/ABI>
. C'est ce qui se passe en fait déjà: la plate - forme est déjàrpm
,dll
,jar
(bytecode JVM),war
(+ jvm serveur web),apk
,2.11
(spécifiques à une version Scala) et ainsi de suite. Lorsque vous dites APK, vous parlez déjà d'une partie ABI spécifique de votre API.L'API peut être portée sur différents ABI
Le niveau supérieur d'une abstraction (les sources écrites par rapport à l'API la plus élevée peuvent être recompilées / portées vers toute autre abstraction de niveau inférieur.
Disons que j'ai quelques sources pour rxscala. Si les outils Scala sont modifiés, je peux les recompiler. Si la JVM change, je pourrais avoir des conversions automatiques de l'ancienne machine vers la nouvelle sans me soucier des concepts de haut niveau. Bien que le portage puisse être difficile, cela aidera tout autre client. Si un nouveau système d'exploitation est créé à l'aide d'un code assembleur totalement différent, un traducteur peut être créé.
API portées dans plusieurs langues
Il existe des API qui sont portées dans plusieurs langues comme les flux réactifs . En général, ils définissent des mappages à des langues / plates-formes spécifiques. Je dirais que l'API est la spécification principale formellement définie en langage humain ou même en langage de programmation spécifique. Tous les autres "mappages" sont en quelque sorte ABI, sinon plus d'API que l'ABI habituel. La même chose se produit avec les interfaces REST.
la source
En bref et en philosophie, seules les choses d'un genre peuvent bien s'entendre, et l'ABI pourrait être considéré comme le type de logiciel qui fonctionne ensemble.
la source
J'essayais également de comprendre ABI et la réponse de JesperE était très utile.
D'un point de vue très simple, nous pouvons essayer de comprendre ABI en considérant la compatibilité binaire.
KDE wiki définit une bibliothèque comme compatible binaire «si un programme lié dynamiquement à une ancienne version de la bibliothèque continue de fonctionner avec les nouvelles versions de la bibliothèque sans avoir besoin de recompiler.» Pour plus d'informations sur la liaison dynamique, reportez-vous à la section Liaison statique vs liaison dynamique
Maintenant, essayons de regarder uniquement les aspects les plus élémentaires nécessaires à la compatibilité binaire d'une bibliothèque (en supposant qu'il n'y a pas de changement de code source dans la bibliothèque):
Bien sûr, il existe de nombreux autres détails, mais c'est surtout ce que l'ABI couvre également.
Plus précisément pour répondre à votre question, de ce qui précède, nous pouvons déduire:
J'espère que cela t'aides!
la source
Interface binaire d'application (ABI)
Fonctionnalité:
Entités existantes:
consommateur:
Ceux qui en ont besoin doivent s'assurer que les chaînes d'outils de construction fonctionnent dans leur ensemble. Si vous écrivez un module en langage assembleur, un autre en Python, et au lieu que votre propre chargeur de démarrage veuille utiliser un système d'exploitation, alors vos modules "application" fonctionnent à travers les frontières "binaires" et nécessitent l'accord d'une telle "interface".
Manipulation des noms C ++ car il peut être nécessaire de lier des fichiers objets de différentes langues de haut niveau dans votre application. Envisagez d'utiliser la bibliothèque standard GCC pour effectuer des appels système vers Windows créés avec Visual C ++.
ELF est une attente possible de l'éditeur de liens à partir d'un fichier objet pour l'interprétation, bien que JVM puisse avoir une autre idée.
Pour une application Windows RT Store, essayez de rechercher ARM ABI si vous souhaitez vraiment faire fonctionner ensemble une chaîne d'outils de construction.
la source
Le terme ABI est utilisé pour désigner deux concepts distincts mais liés.
Quand on parle de compilateurs, il fait référence aux règles utilisées pour traduire des constructions de niveau source en constructions binaires. Quelle est la taille des types de données? comment fonctionne la pile? comment passer des paramètres aux fonctions? quels registres doivent être sauvegardés par l'appelant par rapport à l'appelé?
Quand on parle de bibliothèques, il fait référence à l'interface binaire présentée par une bibliothèque compilée. Cette interface est le résultat d'un certain nombre de facteurs, notamment le code source de la bibliothèque, les règles utilisées par le compilateur et, dans certains cas, les définitions extraites d'autres bibliothèques.
Les modifications apportées à une bibliothèque peuvent casser l'ABI sans casser l'API. Prenons par exemple une bibliothèque avec une interface comme.
et le programmeur d'application écrit du code comme
Le programmeur d'application ne se soucie pas de la taille ou de la disposition de FOO, mais le binaire d'application se retrouve avec une taille codée en dur de foo. Si le programmeur de bibliothèque ajoute un champ supplémentaire à foo et que quelqu'un utilise le nouveau binaire de bibliothèque avec l'ancien binaire d'application, alors la bibliothèque peut faire des accès mémoire hors limites.
OTOH si l'auteur de la bibliothèque avait conçu son API comme.
et le programmeur d'application écrit du code comme
Ensuite, le binaire de l'application n'a besoin de rien savoir de la structure de FOO, qui peut tous être cachés à l'intérieur de la bibliothèque. Le prix à payer pour cela est que les opérations de tas sont impliquées.
la source
ABI
-Application Binary Interface
concerne une communication de code machine en cours d' exécution entre deux parties de programme binaire comme - application, bibliothèque, OS ...ABI
décrit comment les objets sont enregistrés en mémoire et comment les fonctions sont appelées (calling convention
)Un bon exemple d'API et d'ABI est l'écosystème iOS avec le langage Swift .
Application
- Lorsque vous créez une application en utilisant différentes langues. Par exemple, vous pouvez créer une application en utilisantSwift
etObjective-C
[Mixage Swift et Objective-C]Application - OS
- runtime -Swift runtime
etstandard libraries
font partie du système d'exploitation et ne doivent pas être inclus dans chaque bundle (par exemple, application, framework). C'est la même chose que les utilisations d'Objective-CLibrary
-Module Stability
case - temps de compilation - vous pourrez importer un framework qui a été construit avec une autre version du compilateur de Swift. Cela signifie qu'il est sécuritaire de créer un binaire de source fermée (pré-build) qui sera consommé par une version différente du compilateur (.swiftinterface
utilisé avec.swiftmodule
) et vous n'obtiendrez pasLibrary
-Library Evolution
étui[API vs ABI]
la source