829 Stimmen

Was ist ein Application Binary Interface (ABI)?

Ich habe nie genau verstanden, was ein ABI ist. Bitte verweisen Sie mich nicht auf einen Wikipedia-Artikel. Wenn ich es verstehen würde, wäre ich nicht hier und würde einen so langen Beitrag schreiben.

Das ist meine Einstellung zu verschiedenen Schnittstellen:

Eine TV-Fernbedienung ist eine Schnittstelle zwischen dem Benutzer und dem Fernsehgerät. Sie ist eine existierende Einheit, die aber an sich nutzlos ist (sie bietet keine Funktionen). Die gesamte Funktionalität für jede dieser Tasten auf der Fernbedienung ist im Fernsehgerät implementiert.

Schnittstelle: Es handelt sich um eine "existierende Entitätsebene" zwischen der functionality y consumer dieser Funktionalität. Eine Schnittstelle an sich tut nichts. Sie ruft lediglich die dahinter liegende Funktionalität auf.

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

Befehlszeilenschnittstelle (CLI) Die Befehle sind die vorhandenen Einheiten, der Verbraucher ist der Nutzer und die Funktionalität liegt dahinter.

functionality: meine Softwarefunktionalität, die einen Zweck erfüllt Zweck, für den wir diese Schnittstelle beschreiben.

existing entities: Befehle

consumer: Benutzer

Grafische Benutzeroberfläche (GUI) Fenster, Schaltflächen, etc. sind die bestehenden Entitäten, und auch hier 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, Schaltflächen usw..

consumer: Benutzer

Anwendungsprogrammierschnittstelle (API) Funktionen (oder zu sein Schnittstellen (in der schnittstellenbasierten Programmierung) sind die bestehenden Entitäten, der Verbraucher ist hier ein anderes Programm, kein Benutzer, und wieder Funktionalität liegt hinter dieser Schicht.

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

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

consumer: ein anderes Programm/eine andere Anwendung.

Anwendungsbinäre Schnittstelle (ABI) Genau hier beginnt mein Problem.

functionality: ???

existing entities: ???

consumer: ???

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

Wikipedia sagt:

ABIs umfassen Details wie

  • Datentyp, Größe und Ausrichtung;
  • die Aufrufkonvention, die steuert, wie die Argumente der Funktionen übergeben und Rückgabewerte abgerufen werden;
  • die Nummern der Systemaufrufe und wie eine Anwendung Systemaufrufe tätigen sollte an das Betriebssystem richten soll;

Andere ABIs standardisieren Details wie

  • die Verstümmelung von C++-Namen,
  • die Ausbreitung von Ausnahmen, und
  • Aufrufkonvention zwischen Compilern auf der gleichen Plattform, aber nicht keine plattformübergreifende Kompatibilität erfordern.
  • Wer braucht diese Details? Bitte sagen Sie nicht das Betriebssystem. Ich kenne Assembler-Programmierung. Ich weiß, wie Binden und Laden funktioniert. Ich weiß genau, was darin passiert.

  • Warum wurde die C++-Namensvermischung eingeführt? Ich dachte, wir sprechen auf der binären Ebene. Warum kommen Sprachen ins Spiel?

Wie auch immer, ich habe die [PDF] System V Anwendung Binäre Schnittstelle Ausgabe 4.1 (1997-03-18) um zu sehen, was genau er enthält. Nun, das meiste ergab keinen Sinn.

  • Warum enthält sie zwei Kapitel (4. und 5.), die die ELF Dateiformat? Tatsächlich sind dies die einzigen beiden wichtigen Kapitel dieser Spezifikation. Der Rest der Kapitel ist "prozessorspezifisch". Wie auch immer, ich denke, das ist ein völlig anderes Thema. Bitte sagen Sie nicht, dass die Spezifikationen des ELF-Dateiformats sind die ABI. Es qualifiziert sich nicht als ein Schnittstelle gemäß der Definition.

  • Ich weiß, da wir auf einer so niedrigen Ebene sprechen, muss es sehr spezifisch sein. Aber ich bin mir nicht sicher, wie spezifisch die "Befehlssatzarchitektur (ISA)" ist.

  • Wo kann ich das ABI von Microsoft Windows finden?

Das sind also die wichtigsten Fragen, die mich beschäftigen.

10 Stimmen

"Bitte sagen Sie nicht OS" Compiler müssen die ABI kennen. Linker müssen die ABI kennen. Der Kernel muss die ABI kennen, um das Programm im RAM einrichten zu können, damit es ordnungsgemäß läuft. Was C++ betrifft, siehe unten, so werden Bezeichnungen absichtlich in Kauderwelsch umgewandelt, weil Überladungen und private Methoden vorhanden sind, und der Linker und jeder andere Compiler müssen über eine kompatible Namensumwandlung verfügen, um damit arbeiten zu können, mit anderen Worten, über dieselbe ABI.

1 Stimmen

"Sie können eine x86-Datei nicht auf einem ARM-Prozessor ausführen, Punkt. Die auf einem x86-System verwendete ABI kann also nicht dieselbe sein, die auf einem ARM-System verwendet wird.

20 Stimmen

Ich denke, die Frage ist so klar; sie beschreibt genau, welches Antwortformat erwartet wird, und dennoch gibt es keine einzige zufriedenstellende Antwort, die akzeptiert werden kann.

13voto

snies Punkte 3365

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

Ein Systemaufruf ist eine Möglichkeit für ein Userspace-Programm, den Kernelspace um etwas zu bitten. Es funktioniert, indem es den Zahlencode für den Aufruf und das Argument in ein bestimmtes Register einträgt und einen Interrupt auslöst. Dann erfolgt ein Wechsel zum Kernelspace und der Kernel sucht nach dem Zahlencode und dem Argument, bearbeitet die Anfrage, legt das Ergebnis zurück in ein Register und löst einen Wechsel zurück zum Userspace aus. Dies ist z. B. erforderlich, wenn die Anwendung Speicher zuweisen oder eine Datei öffnen will (Syscalls "brk" und "open").

Nun haben die Syscalls Kurznamen "brk" usw. und entsprechende Opcodes, die in einer systemspezifischen Header-Datei definiert sind. Solange diese Opcodes gleich bleiben, kann man die gleichen 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.

13voto

Yann Ramin Punkte 32375

Funktionalität: Eine Reihe von Verträgen, die den Compiler, die Assembler-Schreiber, den Linker und das Betriebssystem betreffen. Die Verträge legen fest, wie Funktionen aufgebaut sind, wo Parameter übergeben werden, wie Parameter übergeben werden, wie Funktionsrückgaben funktionieren. Sie sind im Allgemeinen spezifisch für ein (Prozessorarchitektur, Betriebssystem) Tupel.

Vorhandene Einheiten: Parameterlayout, Funktionssemantik, Registerzuweisung. Zum Beispiel hat die ARM-Architektur zahlreiche ABIs (APCS, EABI, GNU-EABI, ganz zu schweigen von einer Reihe historischer Fälle) - die Verwendung einer gemischten ABI wird dazu führen, dass Ihr Code beim Aufruf über Grenzen hinweg einfach nicht funktioniert.

Verbraucher: Der Compiler, die Assembler-Schreiber, das Betriebssystem, die CPU-spezifische Architektur.

Wer braucht diese Details? Der Compiler, die Assembler-Programmierer, die Linker, die den Code generieren (oder Alignment-Anforderungen), das Betriebssystem (Interrupt-Behandlung, Syscall-Schnittstelle). Wenn Sie Assemblerprogrammierung betrieben, waren Sie konform mit einer ABI!

C++ Name Mangling ist ein Sonderfall - es ist ein Linker und dynamischer Linker zentriertes Problem - wenn Name Mangling nicht standardisiert ist, dann wird dynamisches Linken nicht funktionieren. Von nun an wird die C++ ABI genau so genannt, nämlich C++ ABI. Es handelt sich nicht um ein Problem auf Linker-Ebene, sondern um ein Problem der Codegenerierung. Sobald Sie eine C++-Binärdatei haben, ist es nicht möglich, sie mit einer anderen C++-ABI kompatibel zu machen (Namensmanipulation, Ausnahmebehandlung), ohne sie aus dem Quellcode neu zu kompilieren.

ELF ist ein Dateiformat für die Verwendung eines Laders und eines dynamischen Linkers. ELF ist ein Containerformat für Binärcode und Daten und spezifiziert als solches die ABI eines Codestücks. Ich würde ELF nicht als eine ABI im engeren Sinne betrachten, da PE-Executables keine ABI sind.

Alle ABIs sind anweisungssatzspezifisch. Ein ARM ABI macht auf einem MSP430 oder x86_64 Prozessor keinen Sinn.

Windows hat mehrere ABIs - zum Beispiel sind fastcall und stdcall zwei häufig verwendete ABIs. Die syscall ABI ist wieder anders.

10voto

raisercostin Punkte 8349

Zusammenfassung

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

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

Definieren eines Schnittstelle : ABI, API

Wenn Sie eine Bibliothek wie joda-time verwenden wollen, müssen Sie eine Abhängigkeit von joda-time-<major>.<minor>.<patch>.jar . Die Bibliothek folgt bewährten Praktiken und verwendet Semantische Versionierung . Damit wird die API-Kompatibilität auf drei Ebenen definiert:

  1. Patch - Sie brauchen Ihren Code überhaupt nicht zu ändern. Die Bibliothek behebt nur einige Bugs.
  2. Geringfügig - Sie brauchen Ihren Code nicht zu ändern, da einige Dinge hinzugefügt wurden (das Prinzip der offenen und geschlossenen Systeme wurde beibehalten)
  3. Major - Die Schnittstelle (API) wurde geändert und Sie müssen möglicherweise Ihren Code ändern.

Damit Sie eine neue Hauptversion der gleichen 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 anrufen
  • JVM-Konventionen
  • Verknüpfungskonventionen
  • Laufzeit-Konventionen All dies wird von den von uns verwendeten Tools definiert und verwaltet.

Beispiele

Java-Fallstudie

Java beispielsweise hat all diese Konventionen standardisiert, allerdings nicht in einem Tool, sondern in einer formalen JVM-Spezifikation. Die Spezifikation ermöglichte es anderen Anbietern, eine andere Reihe von Tools bereitzustellen, die kompatible Bibliotheken ausgeben können.

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

Die virtuelle Dalvik-Maschine brach die ABI

Die Dalvik VM benötigt eine andere Art von Bytecode als der Java Bytecode. Die Dalvik-Bibliotheken werden durch Konvertierung des Java-Bytecodes (mit derselben API) für Dalvik gewonnen. Auf diese Weise erhalten Sie zwei Versionen der gleichen API: definiert durch das Original joda-time-1.7.2.jar . Wir könnten es nennen joda-time-1.7.2.jar y joda-time-1.7.2-dalvik.jar . Sie verwenden eine andere ABI, eine für die stapelorientierten Standard-Java-VMs: Oracles ABI, IBMs ABI, Open Java oder eine andere; und die zweite ABI ist die von Dalvik.

Aufeinanderfolgende Scala-Versionen sind inkompatibel

Scala hat keine Binärkompatibilität zwischen kleineren Scala-Versionen: 2.X . Aus diesem Grund hat die gleiche API "io.reactivex" %% "rxscala" % "0.26.5" drei Versionen (in Zukunft mehr): für Scala 2.10, 2.11 und 2.12. Was wurde geändert? Ich weiß es im Moment 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 im Zusammenhang mit Linking/Naming/Parameter-Konventionen.

Aufeinanderfolgende Java-Versionen sind inkompatibel

Java hat auch Probleme mit den Hauptversionen der JVM: 4,5,6,7,8,9. Sie bieten nur Abwärtskompatibilität. Jvm9 weiß, wie man kompilierten/gezielten Code ausführt (javac's -target Option) für alle anderen Versionen, während JVM 4 nicht weiß, wie man Code ausführt, der für JVM 5 bestimmt ist. Und das alles, obwohl Sie nur eine JVM-Bibliothek haben. Diese Inkompatibilität fliegt unter dem Radar dank der verschiedenen Lösungen:

  1. Semantische Versionierung: Wenn Bibliotheken auf höhere JVMs 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 JVMs in dieselbe Bibliothek einbinden können.

Warum habe ich mit der API-Definition begonnen?

API und ABI sind lediglich Konventionen darüber, wie man Kompatibilität definiert. Die unteren Schichten sind generisch in Bezug auf eine Fülle von Semantiken auf hoher Ebene. Deshalb ist es einfach, einige Konventionen zu treffen. Die erste Art von Konventionen betrifft Speicherausrichtung, Byte-Kodierung, Aufrufkonventionen, Big- und Little-Endian-Kodierungen usw. Darüber hinaus gibt es die Konventionen für ausführbare Programme, wie sie bereits von anderen beschrieben wurden, sowie Konventionen für das Linken, Zwischen-Byte-Code wie die von Java oder LLVM IR, die von GCC verwendet wird. Drittens gibt es Konventionen, wie man Bibliotheken findet und wie man sie lädt (siehe Java Classloader). Je weiter man in den Konzepten voranschreitet, desto mehr Konventionen werden als gegeben angesehen. Das ist der Grund, warum sie es nicht in die semantische Versionierung . Sie sind implizit oder kollabiert in der Haupt Version. Wir könnten die semantische Versionierung mit <major>-<minor>-<patch>-<platform/ABI> . Dies ist, was tatsächlich bereits geschieht: Plattform ist bereits eine rpm , dll , jar (JVM Bytecode), war (jvm+Web-Server), apk , 2.11 (bestimmte Scala-Version) und so weiter. Wenn Sie APK sagen, sprechen Sie bereits über einen bestimmten ABI-Teil Ihrer API.

API kann auf verschiedene ABI portiert werden

Die oberste Ebene einer Abstraktion (die Quellen, die für die höchste API geschrieben wurden, können für jede andere, niedrigere Abstraktionsebene rekompiliert/portiert werden.

Nehmen wir an, ich habe einige Quellen für rxscala. Wenn die Scala-Tools geändert werden, kann ich sie dafür neu kompilieren. Wenn sich die JVM ändert, könnte ich automatische Konvertierungen von der alten auf die neue Maschine vornehmen, ohne mich um die High-Level-Konzepte kümmern zu müssen. Die Portierung mag zwar schwierig sein, aber sie hilft jedem anderen Kunden. Wenn ein neues Betriebssystem entwickelt wird, das einen völlig anderen Assembler-Code verwendet, kann ein Übersetzer erstellt werden.

Sprachübergreifend portierte APIs

Es gibt APIs, die in mehrere Sprachen portiert werden, wie reaktive Ströme . Im Allgemeinen definieren sie Zuordnungen zu bestimmten Sprachen/Plattformen. Ich würde behaupten, dass die API die Hauptspezifikation ist, die formell in menschlicher Sprache oder sogar in einer bestimmten Programmiersprache definiert ist. Alle anderen "Zuordnungen" sind in gewissem Sinne ABI, also mehr API als die übliche ABI. Das Gleiche geschieht mit den REST-Schnittstellen.

7voto

Justin Smith Punkte 2373

Um Code in gemeinsam genutzten Bibliotheken aufzurufen oder Code zwischen Kompilierungseinheiten aufzurufen, muss die Objektdatei Bezeichnungen für die Aufrufe enthalten. C++ verfälscht die Namen der Methodenbezeichnungen, um Data Hiding zu erzwingen und überladene Methoden zu ermöglichen. Aus diesem Grund können Sie keine Dateien von verschiedenen C++-Compilern mischen, es sei denn, sie unterstützen ausdrücklich dieselbe ABI.

7voto

Peter Teoh Punkte 5838

Der beste Weg, um zwischen ABI und API zu unterscheiden, ist zu wissen, warum und wofür sie verwendet werden:

Für x86-64 gibt es im Allgemeinen eine ABI (und für x86 32-Bit gibt es eine weitere):

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 ihm mit einigen leichten Abweichungen. Und Windows x64 hat seine eigene ABI:

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

Wenn man die ABI kennt und davon ausgeht, dass andere Compiler sie ebenfalls befolgen, dann wissen die Binärdateien theoretisch, wie sie sich gegenseitig aufrufen können (insbesondere die API der Bibliotheken) und wie sie Parameter über den Stack oder durch Register usw. übergeben können. Oder welche Register beim Aufruf der Funktionen geändert werden usw. Im Grunde genommen hilft dieses Wissen der Software, sich ineinander zu integrieren. Wenn ich die Reihenfolge der Register bzw. das Stack-Layout kenne, kann ich verschiedene, in Assemblies geschriebene Software ohne große Probleme zusammenfügen.

Aber API sind anders:

Es ist ein High-Level-Funktionen Namen, mit Argument definiert, so dass, wenn verschiedene Software-Stücke bauen mit diesen API, KÖNNEN in der Lage sein, in einem anderen aufrufen. Aber eine zusätzliche Anforderung von SAME ABI muss beachtet werden.

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

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

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

Und Linux ist ebenfalls POSIX-konform. Aber die Binärdateien können nicht einfach rübergeschoben und sofort ausgeführt werden. Da aber in der POSIX-kompatiblen API dieselben NAMEN verwendet werden, kann man dieselbe Software in C nehmen, sie für das andere Betriebssystem neu kompilieren und sofort zum Laufen bringen.

API sind dazu gedacht, die Integration von Software zu erleichtern - Phase vor der Kompilierung. Nach der Kompilierung kann die Software also völlig anders aussehen - wenn die ABI unterschiedlich ist.

ABI sollen die genaue Integration von Software auf der Binär-/Assemblierebene definieren.

0 Stimmen

Die Windows x86-64-Aufrufkonvention verwendet nicht die SysV-Aufrufkonvention, die alle anderen x86-64-Betriebssysteme verwenden. Linux/OS X/FreeBSD verwenden alle die gleiche Aufrufkonvention, aber sie no den vollständigen ABI teilen. Die ABI eines Betriebssystems enthält Systemaufrufnummern, z. B. freebsd.org/doc/de_US.ISO8859-1/books/developers-handbook/ sagt, dass SYS_execve ist 11 unter 32bit-Linux, aber 59 unter FreeBSD.

0 Stimmen

Danke für Ihren Kommentar, ich habe meinen Kommentar geändert, um den Unterschied zwischen ABI und API besser zu erklären.

0 Stimmen

Sie verstehen immer noch nicht den Unterschied zwischen einer Aufrufkonvention und einer vollständigen ABI (Systemaufrufe und alles). Sie können einige FreeBSD-Binärdateien unter Linux ausführen, weil Linux (der Kernel) eine FreeBSD-Kompatibilitätsschicht bereitstellt. Selbst dann ist dies auf Binärdateien beschränkt, die nicht versuchen, einen Teil der FreeBSD-ABI zu verwenden, den Linux nicht zur Verfügung stellt. (z.B. alle Systemaufrufe, die es nur unter FreeBSD gibt). ABI-kompatibel bedeutet, dass Sie die gleiche Binärdatei auf beiden Systemen laufen lassen können, nicht nur, dass sie sich ähnlich kompilieren lassen.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X