Was ist eine Application Binary Interface (ABI)?


493

Ich habe nie klar verstanden, was ein ABI ist. Bitte verweisen Sie mich nicht auf einen Wikipedia-Artikel. Wenn ich es verstehen könnte, wäre ich nicht hier, um einen so langen Beitrag zu veröffentlichen.

Dies ist meine Einstellung zu verschiedenen Schnittstellen:

Eine TV-Fernbedienung ist eine Schnittstelle zwischen dem Benutzer und dem Fernseher. Es ist eine vorhandene Entität, aber für sich genommen nutzlos (bietet keine Funktionalität). Alle Funktionen für jede dieser Tasten auf der Fernbedienung sind im Fernsehgerät implementiert.

Schnittstelle: Es handelt sich um eine "vorhandene Entität" zwischen der functionalityund consumerdieser Funktionalität. Eine Schnittstelle an sich macht nichts. Es ruft nur die dahinter liegende Funktionalität auf.

Je nachdem, wer der Benutzer ist, gibt es nun verschiedene Arten von Schnittstellen.

CLI- Befehle (Command Line Interface) sind die vorhandenen Entitäten, der Verbraucher ist der Benutzer und die Funktionalität steckt dahinter.

functionality: meine Softwarefunktionalität, die einen Zweck löst, für den wir diese Schnittstelle beschreiben.

existing entities: Befehle

consumer: Benutzer

Fenster, Schaltflächen usw. der grafischen Benutzeroberfläche (GUI) sind die vorhandenen Entitäten, und wiederum ist der Verbraucher der Benutzer, und die Funktionalität liegt dahinter.

functionality: meine Softwarefunktionalität, die ein Problem löst, für das wir diese Schnittstelle beschreiben.

existing entities: Fenster, Knöpfe etc ..

consumer: Benutzer

API- Funktionen ( Application Programming Interface) (oder genauer gesagt) Schnittstellen (bei der Schnittstellenprogrammierung) sind die vorhandenen Entitäten, der Verbraucher ist hier ein anderes Programm, kein Benutzer, und wiederum liegt die Funktionalität hinter dieser Schicht.

functionality: meine Softwarefunktionalität, die ein Problem löst, für das wir diese Schnittstelle beschreiben.

existing entities: Funktionen, Schnittstellen (Array von Funktionen).

consumer: ein anderes Programm / eine andere Anwendung.

Application Binary Interface (ABI) Hier beginnt mein Problem.

functionality: ???

existing entities: ???

consumer: ???

  • Ich habe Software in verschiedenen Sprachen geschrieben und verschiedene Arten von Schnittstellen (CLI, GUI und API) bereitgestellt, bin mir aber nicht sicher, ob ich jemals ein ABI bereitgestellt habe.

Wikipedia sagt:

ABIs decken Details wie

  • Datentyp, Größe und Ausrichtung;
  • die aufrufende Konvention, die steuert, wie die Argumente von Funktionen übergeben und Rückgabewerte abgerufen werden;
  • die Systemrufnummern und wie eine Anwendung Systemaufrufe an das Betriebssystem tätigen soll;

Andere ABIs standardisieren Details wie

  • der C ++ Name Mangling,
  • Ausnahmeverbreitung und
  • Aufrufkonvention zwischen Compilern auf derselben Plattform, jedoch keine plattformübergreifende Kompatibilität erforderlich.
  • Wer braucht diese Details? Bitte sagen Sie nicht das Betriebssystem. Ich kenne Assembler-Programmierung. Ich weiß, wie das Verknüpfen und Laden funktioniert. Ich weiß genau, was drinnen passiert.

  • Warum kam C ++ Name Mangling ins Spiel? Ich dachte, wir reden auf binärer Ebene. Warum kommen Sprachen herein?

Wie auch immer, ich habe die [PDF] System V Application Binary Interface Edition 4.1 (1997-03-18) heruntergeladen, um zu sehen, was genau sie enthält. Nun, das meiste ergab keinen Sinn.

  • Warum enthält es zwei Kapitel (4. und 5.), um das ELF- Dateiformat zu beschreiben ? Tatsächlich sind dies die einzigen zwei wesentlichen Kapitel dieser Spezifikation. Der Rest der Kapitel ist "prozessorspezifisch". Jedenfalls denke ich, dass es ein ganz anderes Thema ist. Bitte sagen Sie nicht , dass ELF - Dateiformat - Spezifikationen sind die ABI. Es ist keine Schnittstelle gemäß der Definition.

  • Ich weiß, da wir auf so niedrigem Niveau sprechen, muss es sehr spezifisch sein. Aber ich bin nicht sicher, wie es "Befehlssatzarchitektur (ISA)" spezifisch ist?

  • Wo finde ich das ABI von Microsoft Windows?

Das sind also die Hauptfragen, die mich nerven.


7
"Bitte nicht sagen, OS" Compiler müssen den ABI kennen. Linker müssen den ABI kennen. Der Kernel muss den ABI kennen, um das Programm im RAM einzurichten, damit es ordnungsgemäß ausgeführt werden kann. Wie in C ++ (siehe unten) werden Labels aufgrund von Überladung und privaten Methoden absichtlich in Kauderwelsch umgewandelt, und der Linker und jeder andere Compiler benötigen eine kompatible Namensverknüpfung, um damit arbeiten zu können, dh dasselbe ABI.
Justin Smith

8
Ich denke, die Frage ist so klar; genau beschreiben, was das erwartete Antwortformat ist und dennoch keine einzige zufriedenstellende Antwort das, was akzeptiert werden kann.
Legends2k

3
@ legends2k Ich gehe davon aus, dass OP zwar weiß, was ein ABI ist, dies aber nicht erkennt. Die überwiegende Mehrheit der Programmierer wird niemals einen ABI entwerfen oder bereitstellen, da dies die Aufgabe von OS / Plattform-Designern ist.
JesperE

4
@JesperE: Ich stimme Ihrem Punkt zu. Aber wahrscheinlich möchte das OP es klar wissen, in dem Format, das er / sie für richtig hält, obwohl er / sie möglicherweise keinen ABI bereitstellen muss.
Legends2k

2
Ich war unwissend. Vor kurzem während der Arbeit mit all diesen Dingen. Mir wurde klar, was ABI eigentlich ist. Ja, ich stimme zu, dass meine Vorlage fehlerhaft ist. Es ist nicht angemessen, ABI in meine Vorlage zu integrieren. Danke @ JasperE. Es hat nur Berufserfahrung gekostet, um Ihre Antwort zu realisieren.
Krallen

Antworten:


534

Eine einfache Möglichkeit, "ABI" zu verstehen, besteht darin, es mit "API" zu vergleichen.

Sie kennen das Konzept einer API bereits. Wenn Sie beispielsweise die Funktionen einer Bibliothek oder Ihres Betriebssystems verwenden möchten, programmieren Sie gegen eine API. Die API besteht aus Datentypen / Strukturen, Konstanten, Funktionen usw., die Sie in Ihrem Code verwenden können, um auf die Funktionen dieser externen Komponente zuzugreifen.

Ein ABI ist sehr ähnlich. Stellen Sie sich das als kompilierte Version einer API vor (oder als API auf Maschinensprachenebene). Wenn Sie Quellcode schreiben, greifen Sie über eine API auf die Bibliothek zu. Sobald der Code kompiliert ist, greift Ihre Anwendung über das ABI auf die Binärdaten in der Bibliothek zu. Das ABI definiert die Strukturen und Methoden, mit denen Ihre kompilierte Anwendung auf die externe Bibliothek zugreift (genau wie die API), nur auf einer niedrigeren Ebene. Ihre API definiert die Reihenfolge, in der Sie Argumente an eine Funktion übergeben. Ihr ABI definiert die Mechanik, wieDiese Argumente werden übergeben (Register, Stapel usw.). Ihre API definiert, welche Funktionen Teil Ihrer Bibliothek sind. Ihr ABI definiert, wie Ihr Code in der Bibliotheksdatei gespeichert wird, damit jedes Programm, das Ihre Bibliothek verwendet, die gewünschte Funktion finden und ausführen kann.

ABIs sind wichtig, wenn es um Anwendungen geht, die externe Bibliotheken verwenden. Bibliotheken sind voll mit Code und anderen Ressourcen, aber Ihr Programm muss wissen, wie es die benötigten Informationen in der Bibliotheksdatei findet. Ihr ABI definiert, wie der Inhalt einer Bibliothek in der Datei gespeichert wird, und Ihr Programm verwendet das ABI, um die Datei zu durchsuchen und zu finden, was es benötigt. Wenn alles in Ihrem System dem gleichen ABI entspricht, kann jedes Programm mit jeder Bibliotheksdatei arbeiten, unabhängig davon, wer sie erstellt hat. Linux und Windows verwenden unterschiedliche ABIs, sodass ein Windows-Programm nicht weiß, wie es auf eine für Linux kompilierte Bibliothek zugreifen soll.

Manchmal sind ABI-Änderungen unvermeidlich. In diesem Fall funktionieren alle Programme, die diese Bibliothek verwenden, nur, wenn sie für die Verwendung der neuen Version der Bibliothek neu kompiliert wurden. Wenn sich der ABI ändert, die API jedoch nicht, werden die alten und neuen Bibliotheksversionen manchmal als "quellkompatibel" bezeichnet. Dies bedeutet, dass ein für eine Bibliotheksversion kompiliertes Programm zwar nicht mit der anderen funktioniert, der für eine geschriebene Quellcode jedoch für die andere funktioniert, wenn er neu kompiliert wird.

Aus diesem Grund neigen Entwickler dazu, ihren ABI stabil zu halten (um Störungen zu minimieren). Um einen ABI stabil zu halten, müssen die Funktionsschnittstellen (Rückgabetyp und -nummer, Typen und Reihenfolge der Argumente), Definitionen von Datentypen oder Datenstrukturen, definierte Konstanten usw. nicht geändert werden. Neue Funktionen und Datentypen können hinzugefügt werden, vorhandene müssen jedoch erhalten bleiben das Gleiche. Wenn Ihre Bibliothek beispielsweise 32-Bit-Ganzzahlen verwendet, um den Offset einer Funktion anzugeben, und Sie zu 64-Bit-Ganzzahlen wechseln, greift bereits kompilierter Code, der diese Bibliothek verwendet, nicht korrekt auf dieses Feld (oder einen darauf folgenden) zu . Der Zugriff auf Datenstrukturelemente wird während der Kompilierung in Speicheradressen und Offsets konvertiert. Wenn sich die Datenstruktur ändert,

Ein ABI ist nicht unbedingt etwas, das Sie explizit bereitstellen, es sei denn, Sie führen sehr einfache Systemdesignarbeiten durch. Es ist auch nicht sprachspezifisch, da (zum Beispiel) eine C-Anwendung und eine Pascal-Anwendung nach dem Kompilieren denselben ABI verwenden können.

Bearbeiten:Zu Ihrer Frage zu den Kapiteln zum ELF-Dateiformat in den SysV ABI-Dokumenten: Diese Informationen sind enthalten, weil das ELF-Format die Schnittstelle zwischen Betriebssystem und Anwendung definiert. Wenn Sie das Betriebssystem anweisen, ein Programm auszuführen, erwartet es, dass das Programm auf eine bestimmte Weise formatiert wird, und erwartet beispielsweise, dass der erste Abschnitt der Binärdatei ein ELF-Header ist, der bestimmte Informationen bei bestimmten Speicherversätzen enthält. Auf diese Weise übermittelt die Anwendung wichtige Informationen über sich selbst an das Betriebssystem. Wenn Sie ein Programm in einem Nicht-ELF-Binärformat (z. B. a.out oder PE) erstellen, kann ein Betriebssystem, das ELF-formatierte Anwendungen erwartet, die Binärdatei nicht interpretieren oder die Anwendung nicht ausführen.

IIRC, Windows verwendet derzeit das Format Portable Executable (oder PE). Es gibt Links im Abschnitt "Externe Links" dieser Wikipedia-Seite mit weiteren Informationen zum PE-Format.

In Bezug auf Ihren Hinweis zur C ++ - Namensverknüpfung: Wenn Sie eine Funktion in einer Bibliotheksdatei suchen, wird die Funktion normalerweise nach Namen gesucht. In C ++ können Sie Funktionsnamen überladen, sodass der Name allein nicht ausreicht, um eine Funktion zu identifizieren. C ++ - Compiler haben ihre eigenen Möglichkeiten, intern damit umzugehen, die so genannte Name Mangling . Ein ABI kann eine Standardmethode zum Codieren des Namens einer Funktion definieren, damit Programme, die mit einer anderen Sprache oder einem anderen Compiler erstellt wurden, das finden, was sie benötigen. Wenn Sie extern "c"in einem C ++ - Programm verwenden, weisen Sie den Compiler an, eine standardisierte Methode zum Aufzeichnen von Namen zu verwenden, die für andere Software verständlich ist.


2
@bta, Danke für die tolle Antwort. Ist Calling Convention eine Art ABI? Danke
Camino

37
Gute Antwort. Nur dass dies nicht das ist, was ein ABI ist. Ein ABI ist eine Reihe von Regeln, die die Aufrufkonvention und Regeln für das Layout von Strukturen bestimmen. Pascal übergibt Argumente auf dem Stapel in umgekehrter Reihenfolge von C-Anwendungen, sodass Pascal- und C-Compiler NICHT mit demselben ABI kompiliert werden. Die jeweiligen Standards für C- und Pascal-Compiler stellen implizit sicher, dass dies der Fall ist. C ++ - Compiler können keine "Standard" -Methode zum Mangeln von Namen definieren, da es keine Standardmethode gibt. C ++ - Namensveränderungskonventionen waren zwischen C ++ - Compilern nicht kompatibel, wenn es unter Windows konkurrierende C ++ - Compiler gab.
Robin Davies


1
@RobinDavies: Auf Plattformen, auf denen Pascal-Compiler Funktionen-Pop-Argumente aufgerufen hätten, die von ihren Aufrufern angegeben wurden, würden C-Compiler im Allgemeinen Mittel definieren, mit denen ein Programmierer angeben könnte, dass bestimmte Funktionen dieselben Aufrufkonventionen wie die verwenden sollen oder von denen erwartet werden sollte Pascal-Compiler, obwohl C-Compiler im Allgemeinen standardmäßig eine Konvention verwenden, bei der aufgerufene Funktionen alles auf dem Stapel belassen, was von ihren Aufrufern dort platziert wurde.
Supercat

Kann ich sagen, dass vom C-Compiler generierte obj-Dateien ABIs enthalten?
Mitu Raj

144

Wenn Sie sich mit Assembly und der Funktionsweise auf Betriebssystemebene auskennen, entsprechen Sie einem bestimmten ABI. Der ABI regelt beispielsweise, wie Parameter übergeben werden und wo Rückgabewerte platziert werden. Für viele Plattformen steht nur ein ABI zur Auswahl, und in diesen Fällen ist der ABI nur "wie die Dinge funktionieren".

Das ABI regelt jedoch auch Dinge wie das Layout von Klassen / Objekten in C ++. Dies ist erforderlich, wenn Sie Objektreferenzen über Modulgrenzen hinweg übergeben möchten oder wenn Sie mit verschiedenen Compilern kompilierten Code mischen möchten.

Wenn Sie ein 64-Bit-Betriebssystem haben, das 32-Bit-Binärdateien ausführen kann, haben Sie verschiedene ABIs für 32- und 64-Bit-Code.

Im Allgemeinen muss jeder Code, den Sie mit derselben ausführbaren Datei verknüpfen, demselben ABI entsprechen. Wenn Sie mit verschiedenen ABIs zwischen Code kommunizieren möchten, müssen Sie eine Form von RPC- oder Serialisierungsprotokollen verwenden.

Ich denke, Sie sind zu sehr bemüht, verschiedene Arten von Schnittstellen in einen festen Satz von Merkmalen zu integrieren. Beispielsweise muss eine Schnittstelle nicht unbedingt in Verbraucher und Hersteller aufgeteilt werden. Eine Schnittstelle ist nur eine Konvention, nach der zwei Entitäten interagieren.

ABIs können (teilweise) ISA-agnostisch sein. Einige Aspekte (z. B. Aufrufkonventionen) hängen von der ISA ab, andere (z. B. das Layout der C ++ - Klasse) nicht.

Ein gut definierter ABI ist sehr wichtig für Leute, die Compiler schreiben. Ohne einen genau definierten ABI wäre es unmöglich, interoperablen Code zu generieren.

EDIT: Einige Anmerkungen zur Verdeutlichung:

  • "Binär" in ABI schließt die Verwendung von Zeichenfolgen oder Text nicht aus. Wenn Sie eine DLL verknüpfen möchten, die eine C ++ - Klasse exportiert, müssen irgendwo darin die Methoden und Typensignaturen codiert werden. Hier kommt C ++ Name Mangling ins Spiel.
  • Der Grund, warum Sie nie einen ABI bereitgestellt haben, ist, dass die überwiegende Mehrheit der Programmierer dies niemals tun wird. ABIs werden von denselben Personen bereitgestellt, die die Plattform (dh das Betriebssystem) entwerfen, und nur sehr wenige Programmierer werden jemals das Privileg haben, ein weit verbreitetes ABI zu entwerfen.

Ich bin überhaupt nicht davon überzeugt, dass meine Vorlage fehlerhaft ist. Denn überall dort, wo diese Vorlage für die Schnittstelle gilt. Also, ja, ich möchte, dass ABI auch in diese Vorlage passt, aber das ist es nicht. WICHTIG ist, dass ich es immer noch nicht verstehe. Ich weiß nicht, ob ich so dumm bin oder etwas anderes, aber es kommt mir einfach nicht in den Kopf. Ich kann die Antworten und den Wiki-Artikel nicht realisieren.
Krallen

2
@jesperE, "Der ABI regelt Dinge wie die Übergabe von Parametern, wo Rückgabewerte platziert werden." bezieht sich auf "cdecl, stdcall, fastcall, pascal", oder?
Camino

3
Ja. Der eigentliche Name lautet "Calling Convention" und ist Teil des ABI. en.wikipedia.org/wiki/X86_calling_conventions
JesperE

4
Dies ist die richtige und präzise Antwort ohne die Ausführlichkeit (eher Lärm )!
Nawaz

Ich empfehle, ein bisschen Montage zu schreiben. Dies wird den Menschen helfen, ABI greifbarer zu verstehen.
KunYu Tsai

40

Sie brauchen eigentlich überhaupt keinen ABI, wenn ...

  • Ihr Programm hat keine Funktionen und--
  • Ihr Programm ist eine einzelne ausführbare Datei, die alleine ausgeführt wird (dh ein eingebettetes System), in der buchstäblich nur das Programm ausgeführt wird und mit nichts anderem gesprochen werden muss.

Eine vereinfachte Zusammenfassung:

API: "Hier sind alle Funktionen, die Sie aufrufen können."

ABI: „Das ist wie eine Funktion aufzurufen.“

Das ABI besteht aus Regeln, die Compiler und Linker einhalten, um Ihr Programm so zu kompilieren, dass es ordnungsgemäß funktioniert. ABIs decken mehrere Themen ab:

  • Der wohl größte und wichtigste Teil eines ABI ist der Prozeduraufrufstandard, der manchmal als "Aufrufkonvention" bezeichnet wird. Aufrufkonventionen standardisieren, wie "Funktionen" in Assembler-Code übersetzt werden.
  • ABIs bestimmen auch, wie die Namen der exponierten Funktionen in Bibliotheken dargestellt werden sollen, damit anderer Code diese Bibliotheken aufrufen und wissen kann, welche Argumente übergeben werden sollen. Dies nennt man "Name Mangling".
  • ABIs bestimmen auch, welche Art von Datentypen verwendet werden können, wie sie ausgerichtet werden müssen und andere Details auf niedriger Ebene.

Ein genauerer Blick auf die Calling Convention, die ich als Kern eines ABI betrachte:

Die Maschine selbst hat kein Konzept von "Funktionen". Wenn Sie eine Funktion in einer höheren Sprache wie c schreiben, generiert der Compiler eine Zeile mit Assembler-Code wie _MyFunction1:. Dies ist eine Bezeichnung , die vom Assembler schließlich in eine Adresse aufgelöst wird. Dieses Etikett markiert den "Start" Ihrer "Funktion" im Assembler-Code. Wenn Sie im High-Level-Code diese Funktion "aufrufen", veranlasst die CPU tatsächlich , zur Adresse dieses Labels zu springen und dort weiter auszuführen.

In Vorbereitung auf den Sprung muss der Compiler eine Reihe wichtiger Dinge tun. Die Aufrufkonvention ist wie eine Checkliste, der der Compiler folgt, um all diese Dinge zu tun:

  • Zunächst fügt der Compiler ein wenig Assembler-Code ein, um die aktuelle Adresse zu speichern, sodass die CPU nach Abschluss Ihrer "Funktion" an die richtige Stelle zurückspringen und die Ausführung fortsetzen kann.
  • Als Nächstes generiert der Compiler Assemblycode, um die Argumente zu übergeben.
    • Einige Aufrufkonventionen schreiben vor, dass Argumente auf den Stapel gelegt werden sollen ( natürlich in einer bestimmten Reihenfolge ).
    • Andere Konventionen schreiben vor, dass die Argumente in bestimmten Registern abgelegt werden sollten ( natürlich abhängig von ihren Datentypen ).
    • Wieder andere Konventionen schreiben vor, dass eine bestimmte Kombination aus Stapel und Registern verwendet werden sollte.
  • Wenn in diesen Registern zuvor etwas Wichtiges vorhanden war, werden diese Werte jetzt überschrieben und gehen für immer verloren. Einige Aufrufkonventionen können daher vorschreiben, dass der Compiler einige dieser Register speichern muss, bevor die Argumente in sie eingefügt werden.
  • Jetzt fügt der Compiler eine Sprunganweisung ein, die die CPU auffordert, zu dem zuvor erstellten Label zu wechseln ( _MyFunction1:). An diesem Punkt können Sie die CPU als "in" Ihrer "Funktion" betrachten.
  • Am Ende der Funktion fügt der Compiler einen Assemblycode ein, mit dem die CPU den Rückgabewert an die richtige Stelle schreibt. Die aufrufende Konvention bestimmt, ob der Rückgabewert in ein bestimmtes Register (abhängig von seinem Typ) oder auf den Stapel gestellt werden soll.
  • Jetzt ist es Zeit für Aufräumarbeiten. Die aufrufende Konvention bestimmt, wo der Compiler den Bereinigungsassemblycode platziert.
    • Einige Konventionen besagen, dass der Aufrufer den Stapel bereinigen muss. Dies bedeutet, dass, nachdem die "Funktion" ausgeführt wurde und die CPU zu ihrem vorherigen Stand zurückspringt, der nächste auszuführende Code ein sehr spezifischer Bereinigungscode sein sollte.
    • Andere Konventionen sagen , dass die einige bestimmte Teile des Bereinigungscode am Ende der „Funktion“ sollte vor dem Sprung zurück.

Es gibt viele verschiedene ABIs / Aufrufkonventionen. Einige der wichtigsten sind:

  • Für die x86- oder x86-64-CPU (32-Bit-Umgebung):
    • CDECL
    • STDCALL
    • FASTCALL
    • VECTORCALL
    • DIESER ANRUF
  • Für die x86-64-CPU (64-Bit-Umgebung):
    • SYSTEMV
    • MSNATIVE
    • VECTORCALL
  • Für die ARM-CPU (32-Bit)
    • AAPCS
  • Für die ARM-CPU (64-Bit)
    • AAPCS64

Hier ist eine großartige Seite, die tatsächlich die Unterschiede in der Assembly zeigt, die beim Kompilieren für verschiedene ABIs generiert wurden.

Eine andere Sache zu erwähnen ist, dass ein ABI nicht nur innerhalb des ausführbaren Moduls Ihres Programms relevant ist . Es wird auch vom Linker verwendet, um sicherzustellen, dass Ihr Programm die Bibliotheksfunktionen korrekt aufruft. Auf Ihrem Computer werden mehrere gemeinsam genutzte Bibliotheken ausgeführt. Solange Ihr Compiler weiß, welche ABI er jeweils verwendet, kann er Funktionen von ihnen ordnungsgemäß aufrufen, ohne den Stapel in die Luft zu jagen.

Es ist äußerst wichtig, dass Ihr Compiler versteht, wie Bibliotheksfunktionen aufgerufen werden . Auf einer gehosteten Plattform (dh einer Plattform, auf der ein Betriebssystem Programme lädt) kann Ihr Programm nicht einmal blinken, ohne einen Kernelaufruf zu tätigen.


19

Eine Application Binary Interface (ABI) ähnelt einer API, aber die Funktion ist für den Aufrufer auf Quellcodeebene nicht zugänglich. Nur eine binäre Darstellung ist zugänglich / verfügbar.

ABIs können auf der Ebene der Prozessorarchitektur oder auf der Ebene des Betriebssystems definiert werden. Die ABIs sind Standards, denen die Codegeneratorphase des Compilers folgen muss. Der Standard wird entweder vom Betriebssystem oder vom Prozessor festgelegt.

Funktionalität: Definieren Sie den Mechanismus / Standard, um Funktionsaufrufe unabhängig von der Implementierungssprache oder einem bestimmten Compiler / Linker / Toolchain zu machen. Stellen Sie den Mechanismus bereit, der JNI oder eine Python-C-Schnittstelle usw. ermöglicht.

Bestehende Entitäten: Funktionen in Maschinencodeform.

Consumer: Eine andere Funktion (einschließlich einer in einer anderen Sprache, die von einem anderen Compiler kompiliert oder von einem anderen Linker verknüpft wurde).


Warum sollte der ABI durch die Architektur definiert werden? Warum können unterschiedliche Betriebssysteme auf derselben Architektur keine unterschiedlichen ABIs definieren?
Andreas Haferburg

10

Funktionalität: Eine Reihe von Verträgen, die sich auf den Compiler, Assembly Writer, den Linker und das Betriebssystem auswirken. Die Verträge legen fest, wie Funktionen angeordnet sind, wo Parameter übergeben werden, wie Parameter übergeben werden, wie Funktionsrückgaben funktionieren. Diese sind im Allgemeinen spezifisch für ein Tupel (Prozessorarchitektur, Betriebssystem).

Bestehende Entitäten: Parameterlayout, Funktionssemantik, Registerzuordnung. Beispielsweise verfügt die ARM-Architektur über zahlreiche ABIs (APCS, EABI, GNU-EABI, unabhängig von einer Reihe historischer Fälle). Die Verwendung eines gemischten ABI führt dazu, dass Ihr Code beim Aufrufen über Grenzen hinweg einfach nicht funktioniert.

Consumer: Der Compiler, Assembly Writer, Betriebssystem, CPU-spezifische Architektur.

Wer braucht diese Details? Der Compiler, Assembly Writer, Linker, die Code generieren (oder Alignment-Anforderungen), Betriebssystem (Interrupt-Behandlung, Syscall-Schnittstelle). Wenn Sie Baugruppenprogrammierung durchgeführt haben, haben Sie sich an einen ABI angepasst!

C ++ - Namensverknüpfung ist ein Sonderfall - es ist ein auf Linker und dynamische Linker zentriertes Problem - wenn die Namensverknüpfung nicht standardisiert ist, funktioniert die dynamische Verknüpfung nicht. Von nun an heißt das C ++ ABI genau das, das C ++ ABI. Es handelt sich nicht um ein Problem auf Linker-Ebene, sondern um ein Problem bei der Codegenerierung. Sobald Sie eine C ++ - Binärdatei haben, ist es nicht möglich, sie mit einem anderen C ++ - ABI (Name Mangling, Ausnahmebehandlung) kompatibel zu machen, ohne sie aus dem Quellcode neu zu kompilieren.

ELF ist ein Dateiformat für die Verwendung eines Loaders und eines dynamischen Linkers. ELF ist ein Containerformat für Binärcode und Daten und gibt als solches den ABI eines Codeteils an. Ich würde ELF nicht als ABI im engeren Sinne betrachten, da ausführbare PE-Dateien kein ABI sind.

Alle ABIs sind befehlssatzspezifisch. Ein ARM-ABI ist auf einem MSP430- oder x86_64-Prozessor nicht sinnvoll.

Windows verfügt über mehrere ABIs. Fastcall und stdcall sind beispielsweise zwei häufig verwendete ABIs. Der Syscall ABI ist wieder anders.


9

Lassen Sie mich zumindest einen Teil Ihrer Frage beantworten. Mit einem Beispiel, wie sich das Linux-ABI auf die Systemaufrufe auswirkt und warum dies nützlich ist.

Ein Systemaufruf ist eine Möglichkeit für ein Userspace-Programm, den Kernelspace um etwas zu bitten. Es funktioniert, indem der numerische Code für den Aufruf und das Argument in ein bestimmtes Register gestellt und ein Interrupt ausgelöst wird. Dann erfolgt ein Wechsel zum Kernelspace und der Kernel sucht nach dem numerischen Code und dem Argument, verarbeitet die Anforderung, legt das Ergebnis wieder in einem Register ab und löst einen Wechsel zurück zum Userspace aus. Dies ist beispielsweise erforderlich, wenn die Anwendung Speicher zuweisen oder eine Datei öffnen möchte (syscalls "brk" und "open").

Jetzt haben die Systemaufrufe Kurznamen "brk" usw. und entsprechende Opcodes, die in einer systemspezifischen Header-Datei definiert sind. Solange diese Opcodes gleich bleiben, können Sie dieselben kompilierten Userland-Programme mit verschiedenen aktualisierten Kerneln ausführen, ohne sie neu kompilieren zu müssen. Sie haben also eine Schnittstelle, die von vorkompilierten Binärdateien verwendet wird, daher ABI.


4

Um Code in gemeinsam genutzten Bibliotheken oder Code zwischen Kompilierungseinheiten aufzurufen, muss die Objektdatei Beschriftungen für die Aufrufe enthalten. C ++ entstellt die Namen von Methodenbezeichnungen, um das Ausblenden von Daten zu erzwingen und überladene Methoden zuzulassen. Aus diesem Grund können Sie keine Dateien von verschiedenen C ++ - Compilern mischen, es sei denn, sie unterstützen explizit dasselbe ABI.


4

Der beste Weg, um zwischen ABI und API zu unterscheiden, besteht darin, zu wissen, warum und wofür es verwendet wird:

Für x86-64 gibt es im Allgemeinen einen ABI (und für x86 32-Bit gibt es einen anderen Satz):

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 folgen mit einigen geringfügigen Abweichungen. Und Windows x64 hat einen eigenen ABI:

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

Wenn Sie den ABI kennen und davon ausgehen, dass auch ein anderer Compiler ihm folgt, wissen die Binärdateien theoretisch, wie sie sich gegenseitig aufrufen (insbesondere die Bibliotheks-API) und Parameter über den Stapel oder über Register usw. übergeben. Oder welche Register beim Aufrufen der Funktionen usw. Geändert werden Im Wesentlichen hilft dieses Wissen der Software, sich miteinander zu integrieren. Wenn ich die Reihenfolge der Register / des Stapellayouts kenne, kann ich problemlos verschiedene Software, die in Baugruppen geschrieben ist, problemlos zusammensetzen.

Aber API sind anders:

Es handelt sich um Funktionsnamen auf hoher Ebene, für die ein Argument definiert ist. Wenn verschiedene Softwareteile mithilfe dieser API erstellt werden, können sie möglicherweise ineinander aufrufen. Eine zusätzliche Anforderung von SAME ABI muss jedoch eingehalten werden.

Beispielsweise war Windows früher POSIX-API-kompatibel:

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

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

Und Linux ist auch POSIX-kompatibel. Die Binärdateien können jedoch nicht einfach verschoben und sofort ausgeführt werden. Da sie jedoch dieselben NAMEN in der POSIX-kompatiblen API verwendet haben, können Sie dieselbe Software in C verwenden, sie in den verschiedenen Betriebssystemen neu kompilieren und sofort zum Laufen bringen.

APIs sollen die Integration von Software erleichtern - Vorkompilierungsphase. Nach der Kompilierung kann die Software also völlig anders aussehen - wenn die ABI unterschiedlich sind.

ABI sollen die genaue Integration von Software auf Binär- / Assembly-Ebene definieren.


Die Windows x86-64-Aufrufkonvention verwendet nicht die SysV-Aufrufkonvention, die alle anderen x86-64-Betriebssysteme verwenden. Linux / OS X / FreeBSD verwenden alle dieselbe Aufrufkonvention, jedoch nicht die vollständige ABI. Das ABI eines Betriebssystems enthält Systemrufnummern. Beispiel: freebsd.org/doc/en_US.ISO8859-1/books/developers-handbook/… sagt, dass dies SYS_execve11 unter 32-Bit-Linux ist, aber 59 unter FreeBSD.
Peter Cordes

Vielen Dank für Ihren Kommentar. Ich habe meinen Kommentar geändert, um den Unterschied zwischen ABI und API besser zu beantworten.
Peter Teoh

Sie vermissen immer noch den Unterschied zwischen einer Anrufkonvention und einem vollständigen ABI (Systemaufrufe und alles). Sie können einige FreeBSD-Binärdateien unter Linux ausführen, da Linux (der Kernel) eine FreeBSD-Kompatibilitätsschicht bietet. Selbst dann ist dies auf Binärdateien beschränkt, die nicht versuchen, einen Teil des FreeBSD-ABI zu verwenden, den Linux nicht bereitstellt. (zB ein Systemaufruf nur für FreeBSD). ABI-kompatibel bedeutet, dass Sie auf beiden Systemen dieselbe Binärdatei ausführen können, nicht nur, dass sie ähnlich kompiliert werden.
Peter Cordes

"FreeBSD-Kompatibilitätsschicht", davon noch nie gehört. Können Sie auf den relevanten Quellcode des Linux-Kernels verweisen? Es gibt jedoch das Gegenteil: freebsd.org/doc/en_US.ISO8859-1/books/handbook/linuxemu.html .
Peter Teoh

Ich benutze es nicht. Ich dachte, so etwas gibt es, aber vielleicht nicht mehr. tldp.org/HOWTO/Linux+FreeBSD-6.html sagt, dass es nicht gewartet wird und dass das Howto aus dem Jahr 2000 stammt. xD. unix.stackexchange.com/questions/172038/… bestätigt, dass es aufgegeben und nie wieder gemacht wurde (da niemand es dringend genug wollte, um es zu erledigen). personality(2)einstellen kann PER_BSD. Ich denke, ich erinnere mich, dass ich die ganze Zeit personality(PER_LINUX)in der straceAusgabe gesehen habe, aber moderne 64-Bit-Linux-Binärdateien tun das nicht mehr.
Peter Cordes

4

Beispiel für eine minimal ausführbare Linux-gemeinsam genutzte Linux-Bibliothek

Im Zusammenhang mit gemeinsam genutzten Bibliotheken besteht die wichtigste Auswirkung eines "stabilen ABI" darin, dass Sie Ihre Programme nach dem Ändern der Bibliothek nicht neu kompilieren müssen.

Also zum Beispiel:

  • Wenn Sie eine gemeinsam genutzte Bibliothek verkaufen, ersparen Sie Ihren Benutzern den Ärger, bei jeder neuen Version alles neu zu kompilieren, was von Ihrer Bibliothek abhängt

  • Wenn Sie ein Closed-Source-Programm verkaufen, das von einer gemeinsam genutzten Bibliothek in der Distribution des Benutzers abhängt, können Sie weniger Prebuilts veröffentlichen und testen, wenn Sie sicher sind, dass ABI für bestimmte Versionen des Zielbetriebssystems stabil ist.

    Dies ist besonders wichtig im Fall der C-Standardbibliothek, mit der viele, viele Programme in Ihrem System verknüpft sind.

Jetzt möchte ich ein minimales konkretes lauffähiges Beispiel dafür liefern.

Haupt c

#include <assert.h>
#include <stdlib.h>

#include "mylib.h"

int main(void) {
    mylib_mystruct *myobject = mylib_init(1);
    assert(myobject->old_field == 1);
    free(myobject);
    return EXIT_SUCCESS;
}

mylib.c

#include <stdlib.h>

#include "mylib.h"

mylib_mystruct* mylib_init(int old_field) {
    mylib_mystruct *myobject;
    myobject = malloc(sizeof(mylib_mystruct));
    myobject->old_field = old_field;
    return myobject;
}

mylib.h

#ifndef MYLIB_H
#define MYLIB_H

typedef struct {
    int old_field;
} mylib_mystruct;

mylib_mystruct* mylib_init(int old_field);

#endif

Kompiliert und läuft gut mit:

cc='gcc -pedantic-errors -std=c89 -Wall -Wextra'
$cc -fPIC -c -o mylib.o mylib.c
$cc -L . -shared -o libmylib.so mylib.o
$cc -L . -o main.out main.c -lmylib
LD_LIBRARY_PATH=. ./main.out

Angenommen, wir möchten für Version 2 der Bibliothek ein neues Feld zum mylib_mystructAufruf hinzufügen new_field.

Wenn wir das Feld zuvor old_fieldwie folgt hinzugefügt haben :

typedef struct {
    int new_field;
    int old_field;
} mylib_mystruct;

und die Bibliothek neu aufgebaut, aber nicht main.out, dann schlägt die Behauptung fehl!

Dies liegt daran, dass die Zeile:

myobject->old_field == 1

hatte eine Assembly generiert, die versucht, auf die allererste intStruktur zuzugreifen , die jetzt new_fieldanstelle der erwarteten ist old_field.

Daher hat diese Änderung den ABI gebrochen.

Wenn wir jedoch new_fieldnachher hinzufügen old_field:

typedef struct {
    int old_field;
    int new_field;
} mylib_mystruct;

Dann greift die alte generierte Assembly immer noch auf die erste intStruktur zu, und das Programm funktioniert weiterhin, da wir den ABI stabil gehalten haben.

Hier ist eine vollautomatische Version dieses Beispiels auf GitHub .

Eine andere Möglichkeit, diesen ABI stabil zu halten, wäre gewesen, ihn mylib_mystructals undurchsichtige Struktur zu behandeln und nur über Methodenhelfer auf seine Felder zuzugreifen. Dies macht es einfacher, den ABI stabil zu halten, würde jedoch einen Leistungsaufwand verursachen, da wir mehr Funktionsaufrufe ausführen würden.

API gegen ABI

Im vorherigen Beispiel ist es interessant , dass die beachten Zugabe new_fieldvor old_field, nur das ABI brach, aber nicht die API.

Dies bedeutet, dass wenn wir unser main.cProgramm gegen die Bibliothek neu kompiliert hätten, es trotzdem funktioniert hätte.

Wir hätten die API jedoch auch beschädigt, wenn wir zum Beispiel die Funktionssignatur geändert hätten:

mylib_mystruct* mylib_init(int old_field, int new_field);

da würde in diesem Fall main.cdas Kompilieren ganz aufhören.

Semantische API vs Programmier-API

Wir können API-Änderungen auch in einen dritten Typ einteilen: semantische Änderungen.

Die semantische API ist normalerweise eine natürliche Beschreibung dessen, was die API tun soll, normalerweise in der API-Dokumentation enthalten.

Es ist daher möglich, die semantische API zu unterbrechen, ohne den Programmerstellung selbst zu unterbrechen.

Zum Beispiel, wenn wir geändert hätten

myobject->old_field = old_field;

zu:

myobject->old_field = old_field + 1;

dann hätte dies weder die Programmier-API noch ABI beschädigt, aber main.cdie semantische API würde beschädigt.

Es gibt zwei Möglichkeiten, die Vertrags-API programmgesteuert zu überprüfen:

  • Testen Sie eine Reihe von Eckkoffern. Einfach zu machen, aber Sie könnten immer einen verpassen.
  • formale Überprüfung . Schwieriger zu machen, aber einen mathematischen Korrektheitsnachweis zu erbringen, der Dokumentation und Tests auf eine "menschlich" / maschinenprüfbare Weise vereinheitlicht! Solange deine formale Beschreibung natürlich keinen Fehler enthält ;-)

    Dieses Konzept steht in engem Zusammenhang mit der Formalisierung der Mathematik selbst: /math/53969/what-does-formal-mean/3297537#3297537

Liste aller Elemente, die C / C ++ - ABIs für gemeinsam genutzte Bibliotheken beschädigen

TODO: Finde / erstelle die ultimative Liste:

Java minimal lauffähiges Beispiel

Was ist Binärkompatibilität in Java?

Getestet in Ubuntu 18.10, GCC 8.2.0.


3

Der ABI muss zwischen Anrufer und Angerufenen konsistent sein, um sicherzustellen, dass der Anruf erfolgreich ist. Stack-Verwendung, Register-Verwendung, Stack-Pop am Ende der Routine. All dies sind die wichtigsten Teile des ABI.


3

Zusammenfassung

Es gibt verschiedene Interpretationen und starke Meinungen über die genaue Schicht, die eine ABI (Application Binary Interface) definieren.

Meiner Ansicht nach ist ein ABI eine subjektive Konvention dessen, was als gegeben / Plattform für eine bestimmte API angesehen wird. Der ABI ist der "Rest" von Konventionen, die sich für eine bestimmte API "nicht ändern" oder die von der Laufzeitumgebung behandelt werden: Executoren, Tools, Linker, Compiler, JVM und Betriebssystem.

Definieren einer Schnittstelle : ABI, API

Wenn Sie eine Bibliothek wie joda-time verwenden möchten, müssen Sie eine Abhängigkeit von deklarieren joda-time-<major>.<minor>.<patch>.jar. Die Bibliothek folgt Best Practices und verwendet die semantische Versionierung . Dies definiert die API-Kompatibilität auf drei Ebenen:

  1. Patch - Sie müssen Ihren Code überhaupt nicht ändern. Die Bibliothek behebt nur einige Fehler.
  2. Klein - Sie müssen Ihren Code seit den Ergänzungen nicht mehr ändern
  3. Major - Die Schnittstelle (API) wurde geändert und Sie müssen möglicherweise Ihren Code ändern.

Damit Sie eine neue Hauptversion derselben Bibliothek verwenden können, müssen noch viele andere Konventionen beachtet werden:

  • Die für die Bibliotheken verwendete Binärsprache (in Java-Fällen die JVM-Zielversion, die den Java-Bytecode definiert)
  • Konventionen aufrufen
  • JVM-Konventionen
  • Konventionen verknüpfen
  • Laufzeitkonventionen All diese werden von den von uns verwendeten Tools definiert und verwaltet.

Beispiele

Java-Fallstudie

Beispielsweise standardisierte Java alle diese Konventionen nicht in einem Tool, sondern in einer formalen JVM-Spezifikation. Die Spezifikation ermöglichte es anderen Anbietern, andere Tools bereitzustellen, mit denen kompatible Bibliotheken ausgegeben werden können.

Java bietet zwei weitere interessante Fallstudien für ABI: Scala-Versionen und Dalvik Virtual Machine.

Die virtuelle Maschine von Dalvik hat den ABI gebrochen

Die Dalvik-VM benötigt einen anderen Bytecode als den Java-Bytecode. Die Dalvik-Bibliotheken werden durch Konvertieren des Java-Bytecodes (mit derselben API) für Dalvik erhalten. Auf diese Weise können Sie zwei Versionen derselben API erhalten: definiert durch das Original joda-time-1.7.2.jar. Wir könnten mich anrufen joda-time-1.7.2.jarund joda-time-1.7.2-dalvik.jar. Sie verwenden eine andere ABI für die stapelorientierten Standard-Java-VMs: die von Oracle, die von IBM, Open Java oder eine andere; und der zweite ABI ist der um Dalvik.

Aufeinanderfolgende Scala-Versionen sind nicht kompatibel

Scala hat keine binäre Kompatibilität zwischen kleineren Scala-Versionen: 2.X. Aus diesem Grund hat dieselbe API "io.reactivex" %% "rxscala"% "0.26.5" drei Versionen (in Zukunft mehr): für Scala 2.10, 2.11 und 2.12. Was ist geändert? Ich weiß es noch nicht , aber die Binärdateien sind nicht kompatibel. Wahrscheinlich fügen die neuesten Versionen Dinge hinzu, die die Bibliotheken auf den alten virtuellen Maschinen unbrauchbar machen, wahrscheinlich Dinge, die mit Verknüpfungs- / Namens- / Parameterkonventionen zusammenhängen.

Aufeinanderfolgende Java-Versionen sind nicht kompatibel

Java hat auch Probleme mit den Hauptversionen der JVM: 4,5,6,7,8,9. Sie bieten nur Abwärtskompatibilität. Jvm9 kann kompilierten / zielgerichteten Code ( -targetOption von javac ) für alle anderen Versionen ausführen, während JVM 4 nicht weiß, wie Code ausgeführt wird, der auf JVM 5 ausgerichtet ist. All dies, während Sie über eine joda-Bibliothek verfügen. Diese Inkompatibilität fliegt dank verschiedener Lösungen unter dem Radar:

  1. Semantische Versionierung: Wenn Bibliotheken auf eine höhere JVM abzielen, ändern sie normalerweise die Hauptversion.
  2. Verwenden Sie JVM 4 als ABI, und Sie sind sicher.
  3. Java 9 fügt eine Spezifikation hinzu, wie Sie Bytecode für bestimmte Ziel-JVM in dieselbe Bibliothek aufnehmen können.

Warum habe ich mit der API-Definition begonnen?

API und ABI sind nur Konventionen zur Definition der Kompatibilität. Die unteren Schichten sind generisch in Bezug auf eine Vielzahl von Semantiken auf hoher Ebene. Deshalb ist es einfach, einige Konventionen zu treffen. Die erste Art von Konventionen betrifft Speicherausrichtung, Bytecodierung, Aufrufkonventionen, Big- und Little-Endian-Codierungen usw. Darüber hinaus erhalten Sie die ausführbaren Konventionen wie die anderen beschriebenen, Verknüpfungskonventionen, Zwischenbytecode wie den von Java oder LLVM IR von GCC verwendet. Drittens erhalten Sie Konventionen zum Auffinden und Laden von Bibliotheken (siehe Java-Klassenladeprogramme). Wenn Sie in Konzepten immer höher gehen, haben Sie neue Konventionen, die Sie als gegeben betrachten. Deshalb haben sie es nicht bis zur semantischen Versionierung geschafft .Ausführung. Wir könnten die semantische Versionierung mit ändern <major>-<minor>-<patch>-<platform/ABI>. Das ist , was tatsächlich geschieht bereits: Plattform ist bereits ein rpm, dll, jar(JVM Bytecode), war(JVM + Web - Server), apk, 2.11(spezifische Scala - Version) und so weiter. Wenn Sie APK sagen, sprechen Sie bereits über einen bestimmten ABI-Teil Ihrer API.

Die API kann auf verschiedene ABI portiert werden

Die oberste Ebene einer Abstraktion (die für die höchste API geschriebenen Quellen können neu kompiliert / auf jede andere untergeordnete Abstraktion portiert werden.

Nehmen wir an, ich habe einige Quellen für rxscala. Wenn die Scala-Tools geändert werden, kann ich sie neu kompilieren. Wenn sich die JVM ändert, könnte ich automatisch von der alten auf die neue Maschine konvertieren, ohne mich um die Konzepte auf hoher Ebene zu kümmern. Während die Portierung schwierig sein kann, hilft dies jedem anderen Client. Wenn ein neues Betriebssystem mit einem völlig anderen Assembler-Code erstellt wird, kann ein Übersetzer erstellt werden.

Sprachübergreifend portierte APIs

Es gibt APIs, die in mehreren Sprachen portiert sind, z. B. reaktive Streams . Im Allgemeinen definieren sie Zuordnungen zu bestimmten Sprachen / Plattformen. Ich würde argumentieren, dass die API die Master-Spezifikation ist, die formal in der menschlichen Sprache oder sogar in einer bestimmten Programmiersprache definiert ist. Alle anderen "Zuordnungen" sind gewissermaßen ABI, ansonsten mehr API als das übliche ABI. Gleiches gilt für die REST-Schnittstellen.


1

Kurz gesagt und in der Philosophie können nur Dinge einer Art gut miteinander auskommen, und der ABI könnte als der angesehen werden Art von Software angesehen werden, die zusammenarbeiten.


1

Ich habe auch versucht, ABI zu verstehen, und JesperEs Antwort war sehr hilfreich.

Aus einer sehr einfachen Perspektive können wir versuchen, ABI zu verstehen, indem wir die Binärkompatibilität berücksichtigen.

Das KDE-Wiki definiert eine Bibliothek als binär kompatibel, "wenn ein Programm, das dynamisch mit einer früheren Version der Bibliothek verknüpft ist, weiterhin mit neueren Versionen der Bibliothek ausgeführt wird, ohne dass eine Neukompilierung erforderlich ist." Weitere Informationen zur dynamischen Verknüpfung finden Sie unter Statische Verknüpfung und dynamische Verknüpfung

Lassen Sie uns nun versuchen, nur die grundlegendsten Aspekte zu betrachten, die für eine Binärkompatibilität einer Bibliothek erforderlich sind (vorausgesetzt, es gibt keine Änderungen am Quellcode der Bibliothek):

  1. Gleiche / abwärtskompatible Befehlssatzarchitektur (Prozessorbefehle, Registerdateistruktur, Stapelorganisation, Speicherzugriffstypen sowie Größe, Layout und Ausrichtung der grundlegenden Datentypen, auf die der Prozessor direkt zugreifen kann)
  2. Gleiche Aufrufkonventionen
  3. Mangelnde Konvention mit demselben Namen (dies kann erforderlich sein, wenn beispielsweise ein Fortran-Programm eine C ++ - Bibliotheksfunktion aufrufen muss).

Sicher, es gibt viele andere Details, aber dies ist meistens das, was der ABI auch abdeckt.

Um Ihre Frage genauer zu beantworten, können wir aus dem oben Gesagten Folgendes ableiten:

ABI-Funktionalität: Binärkompatibilität

vorhandene Entitäten: vorhandenes Programm / Bibliotheken / Betriebssystem

Verbraucher: Bibliotheken, Betriebssystem

Hoffe das hilft!


1

Application Binary Interface (ABI)

Funktionalität:

  • Übersetzung vom Modell des Programmierers in den Domänen-Datentyp, die Größe, die Ausrichtung und die aufrufende Konvention des zugrunde liegenden Systems, die steuert, wie die Argumente der Funktionen übergeben und die Rückgabewerte abgerufen werden. die Systemrufnummern und wie eine Anwendung Systemaufrufe an das Betriebssystem tätigen soll; Das Name-Mangling-Schema der Hochsprachen-Compiler, die Weitergabe von Ausnahmen und die Aufrufkonvention zwischen Compilern auf derselben Plattform erfordern jedoch keine plattformübergreifende Kompatibilität ...

Bestehende Einheiten:

  • Logische Blöcke, die direkt an der Programmausführung beteiligt sind: ALU, Allzweckregister, Register für die Speicher- / E / A-Zuordnung von E / A usw.

Verbraucher:

  • Sprachprozessoren Linker, Assembler ...

Diese werden von jedem benötigt, der sicherstellen muss, dass Build-Tool-Ketten als Ganzes funktionieren. Wenn Sie ein Modul in Assemblersprache und ein anderes in Python schreiben und anstelle Ihres eigenen Bootloaders ein Betriebssystem verwenden möchten, arbeiten Ihre "Anwendungs" -Module über "binäre" Grenzen hinweg und erfordern die Zustimmung einer solchen "Schnittstelle".

Mangeln von C ++ - Namen, da möglicherweise Objektdateien aus verschiedenen Hochsprachen in Ihrer Anwendung verknüpft werden müssen. Erwägen Sie die Verwendung der GCC-Standardbibliothek für Systemaufrufe an Windows, die mit Visual C ++ erstellt wurden.

ELF ist eine mögliche Erwartung des Linkers an eine Objektdatei zur Interpretation, obwohl JVM möglicherweise eine andere Idee hat.

Versuchen Sie für eine Windows RT Store-App, nach ARM ABI zu suchen, wenn Sie wirklich möchten, dass einige Build-Toolketten zusammenarbeiten.


1

Der Begriff ABI bezieht sich auf zwei unterschiedliche, aber verwandte Konzepte.

Wenn es um Compiler geht, bezieht es sich auf die Regeln, die zum Übersetzen von Konstrukten auf Quellenebene in binäre Konstrukte verwendet werden. Wie groß sind die Datentypen? Wie funktioniert der Stack? Wie übergebe ich Parameter an Funktionen? Welche Register sollte der Anrufer gegenüber dem Angerufenen speichern?

Wenn es um Bibliotheken geht, bezieht es sich auf die binäre Schnittstelle, die von einer kompilierten Bibliothek dargestellt wird. Diese Schnittstelle ist das Ergebnis einer Reihe von Faktoren, einschließlich des Quellcodes der Bibliothek, der vom Compiler verwendeten Regeln und in einigen Fällen Definitionen, die aus anderen Bibliotheken übernommen wurden.

Änderungen an einer Bibliothek können den ABI beschädigen, ohne die API zu beschädigen. Stellen Sie sich zum Beispiel eine Bibliothek mit einer Schnittstelle wie vor.

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

und der Anwendungsprogrammierer schreibt Code wie

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

Der Anwendungsprogrammierer kümmert sich nicht um die Größe oder das Layout von FOO, aber die Anwendungsbinärdatei erhält eine fest codierte Größe von foo. Wenn der Bibliotheksprogrammierer foo ein zusätzliches Feld hinzufügt und jemand die neue Bibliotheksbinärdatei mit der alten Anwendungsbinärdatei verwendet, kann die Bibliothek Speicherzugriffe außerhalb der Grenzen durchführen.

OTOH, wenn der Bibliotheksautor seine API wie folgt gestaltet hat.

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

und der Anwendungsprogrammierer schreibt Code wie

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

Dann muss die Anwendungsbinärdatei nichts über die Struktur von FOO wissen, die alle in der Bibliothek versteckt sein können. Der Preis, den Sie dafür zahlen, ist jedoch, dass Heap-Operationen beteiligt sind.


0

ABI- Application Binary Interfaceist über eine Kommunikationsmaschinencode in der Laufzeit zwischen zwei binären Programmteilen wie - Anwendung, einer Bibliothek, OS ... beschreibt , wie Objekte im Speicher gespeichert werden und wie die Funktionen genannt werden (ABIcalling convention )

Ein gutes Beispiel für API und ABI ist das iOS-Ökosystem mit Swift-Sprache .

  • Application- Wenn Sie eine Anwendung in verschiedenen Sprachen erstellen. Zum Beispiel können Sie eine Anwendung mit Swiftund [Mischen von Swift und Objective-C] erstellen.Objective-C

  • Application - OS- Laufzeit - Swift runtimeund standard librariessind Teile des Betriebssystems und sollten nicht in jedem Bundle enthalten sein (z. B. App, Framework). Es ist dasselbe wie das, was Objective-C verwendet

  • Library- Module Stabilitycase - compile time - Sie können ein Framework importieren, das mit einer anderen Version von Swifts Compiler erstellt wurde. Dies bedeutet, dass es sicher ist, eine Closed-Source-Binärdatei (Pre-Build) zu erstellen, die von einer anderen Version des Compilers verwendet wird ( .swiftinterfacemit der verwendet wird .swiftmodule), und die Sie nicht erhalten

    Module compiled with _ cannot be imported by the _ compiler
    
  • Library- Library EvolutionFall

    1. Kompilierungszeit - Wenn eine Abhängigkeit geändert wurde, muss ein Client nicht neu kompiliert werden.
    2. Laufzeit - Eine Systembibliothek oder ein dynamisches Framework kann durch ein neues im laufenden Betrieb ausgetauscht werden.

[API vs ABI]

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.