Weiterführende Literatur zu allen Themen hier:The Definitive Guide to Linux System Calls
Ich habe diese mit GNU Assembler (Gas) unter Linux verifiziert.
Kernel-Schnittstelle
x86-32 alias i386 Linux Systemaufrufkonvention:
In x86-32 werden Parameter für den Linux-Systemaufruf mithilfe von Registern übergeben. %eax
für syscall_number. %ebx, %ecx, %edx, %esi, %edi, %ebp werden verwendet, um 6 Parameter an Systemaufrufe zu übergeben.
Der Rückgabewert steht in %eax
. Alle anderen Register (einschließlich EFLAGS) bleiben über int $0x80
erhalten .
Ich habe das folgende Snippet aus dem Linux Assembly Tutorial genommen, aber ich habe Zweifel daran. Wenn jemand ein Beispiel zeigen kann, wäre es großartig.
Wenn es mehr als sechs Argumente gibt, %ebx
muss den Speicherort enthalten, an dem die Liste der Argumente gespeichert ist - aber machen Sie sich darüber keine Sorgen, denn es ist unwahrscheinlich, dass Sie einen Systemaufruf mit mehr als sechs Argumenten verwenden werden.
Ein Beispiel und etwas mehr Lesestoff finden Sie unter http://www.int80h.org/bsdasm/#alternate-calling-convention. Ein weiteres Beispiel für ein Hello World für i386 Linux mit int 0x80
:Hallo, Welt in Assemblersprache mit Linux-Systemaufrufen?
Es gibt einen schnelleren Weg, um 32-Bit-Systemaufrufe durchzuführen:mit sysenter
. Der Kernel ordnet jedem Prozess (dem vDSO) eine Speicherseite mit der User-Space-Seite von sysenter
zu dance, der mit dem Kernel kooperieren muss, damit er die Absenderadresse finden kann. Die Zuordnung von Arg zu Register ist dieselbe wie für int $0x80
. Sie sollten sich normalerweise in das vDSO einwählen, anstatt sysenter
zu verwenden direkt. (Siehe The Definitive Guide to Linux System Calls für Informationen zum Verbinden und Aufrufen des vDSO und für weitere Informationen zu sysenter
, und alles andere, was mit Systemaufrufen zu tun hat.)
x86-32 [Free|Open|Net|DragonFly]BSD-UNIX-Systemaufrufkonvention:
Parameter werden auf dem Stack übergeben. Schieben Sie die Parameter (der letzte Parameter zuerst) auf den Stack. Schieben Sie dann zusätzliche 32-Bit-Dummy-Daten (es handelt sich nicht wirklich um Dummy-Daten. Weitere Informationen finden Sie unter folgendem Link) und geben Sie dann eine Systemaufrufanweisung int $0x80
http://www.int80h.org/bsdasm/#default-calling-convention
x86-64 Linux-Systemaufrufkonvention:
(Hinweis:x86-64 Mac OS X ist ähnlich, unterscheidet sich aber von Linux. TODO:Prüfen Sie, was *BSD tut)
Siehe Abschnitt:„A.2 AMD64 Linux Kernel-Konventionen" von System V Application Binary Interface AMD64 Architecture Processor Supplement. Die neuesten Versionen der i386- und x86-64-System-V-psABIs finden Sie auf dieser Seite im Repo des ABI-Betreuers. (Siehe auch das x86-Tag-Wiki für Up- aktuelle ABI-Links und viele andere gute Sachen über x86 asm.)
Hier ist der Ausschnitt aus diesem Abschnitt:
- Anwendungen auf Benutzerebene verwenden %rdi, %rsi, %rdx, %rcx, %r8 und %r9 als ganzzahlige Register zum Weiterleiten dieser Sequenzen. Die Kernel-Schnittstelle verwendet %rdi, %rsi, %rdx, %r10, %r8 und %r9.
- Ein Systemaufruf erfolgt über die
syscall
Anleitung . Dies überschreibt %rcx und %r11 sowie den %rax-Rückgabewert, aber andere Register bleiben erhalten. - Die Nummer des Syscalls muss im Register %rax übergeben werden.
- Systemaufrufe sind auf sechs Argumente beschränkt, kein Argument wird direkt auf dem Stack übergeben.
- Zurück vom Systemaufruf, Register %rax enthält das Ergebnis des Systemaufrufs. Ein Wert im Bereich zwischen -4095 und -1 weist auf einen Fehler hin, es ist
-errno
. - Nur Werte der Klasse INTEGER oder der Klasse MEMORY werden an den Kernel übergeben.
Denken Sie daran, dass dies aus dem Linux-spezifischen Anhang zum ABI stammt und selbst für Linux informativ und nicht normativ ist. (Aber es ist tatsächlich richtig.)
Diese 32-Bit-int $0x80
ABI ist verwendbar in 64-Bit-Code (aber nicht empfohlen). Was passiert, wenn Sie die 32-Bit-Int 0x80-Linux-ABI in 64-Bit-Code verwenden? Es kürzt seine Eingaben immer noch auf 32-Bit, ist also für Zeiger ungeeignet und setzt r8-r11 auf Null.
Benutzeroberfläche:Funktionsaufruf
x86-32 Funktionsaufrufkonvention:
In x86-32 wurden Parameter auf dem Stack übergeben. Der letzte Parameter wurde zuerst auf den Stapel geschoben, bis alle Parameter fertig sind, und dann call
Anweisung ausgeführt wurde. Dies wird zum Aufrufen von Funktionen der C-Bibliothek (libc) unter Linux aus Assembly verwendet.
Moderne Versionen von i386 System V ABI (unter Linux verwendet) erfordern eine 16-Byte-Ausrichtung von %esp
vor einem call
, wie es das x86-64 System V ABI immer verlangt hat. Aufgerufene dürfen davon ausgehen und SSE-16-Byte-Ladevorgänge/-Speicher verwenden, die bei nicht ausgerichteten Fehlern auftreten. Historisch gesehen erforderte Linux jedoch nur eine 4-Byte-Stack-Ausrichtung, sodass es zusätzliche Arbeit erforderte, natürlich ausgerichteten Speicherplatz selbst für einen 8-Byte-double
zu reservieren oder so.
Einige andere moderne 32-Bit-Systeme benötigen immer noch nicht mehr als 4 Byte Stack-Alignment.
X86-64-System-V-User-Space-Funktion Aufrufkonvention:
x86-64 System V übergibt Argumente in Registern, was effizienter ist als die Stack-Argument-Konvention von i386 System V. Es vermeidet die Latenz und zusätzliche Anweisungen zum Speichern von Argumenten im Speicher (Cache) und dann zum erneuten Laden in den Angerufenen. Dies funktioniert gut, da mehr Register verfügbar sind, und eignet sich besser für moderne Hochleistungs-CPUs, bei denen Latenz und Ausführung außerhalb der Reihenfolge eine Rolle spielen. (Der i386 ABI ist sehr alt).
In diesem neuen Mechanismus:Zunächst werden die Parameter in Klassen eingeteilt. Die Klasse jedes Parameters bestimmt die Art und Weise, wie er an die aufgerufene Funktion übergeben wird.
Vollständige Informationen finden Sie unter:„3.2 Function Calling Sequence“ des System V Application Binary Interface AMD64 Architecture Processor Supplement, das auszugsweise lautet:
Sobald die Argumente klassifiziert sind, werden die Register (Reihenfolge von links nach rechts) für die Weitergabe wie folgt zugewiesen:
- Wenn die Klasse MEMORY ist, übergeben Sie das Argument auf dem Stack.
- Wenn die Klasse INTEGER ist, wird das nächste verfügbare Register der Sequenz %rdi, %rsi, %rdx, %rcx, %r8 und %r9 verwendet
Also %rdi, %rsi, %rdx, %rcx, %r8 and %r9
sind die Register in der Reihenfolge Wird verwendet, um Ganzzahl-/Zeiger-Parameter (d. h. INTEGER-Klasse) an jede libc-Funktion von Assembly zu übergeben. %rdi wird für den ersten INTEGER-Parameter verwendet. %rsi für 2., %rdx für 3. und so weiter. Dann call
Belehrung sollte gegeben werden. Der Stack (%rsp
) muss 16B-ausgerichtet sein, wenn call
ausführt.
Bei mehr als 6 INTEGER-Parametern wird ab dem 7. INTEGER-Parameter der Stack übergeben. (Caller erscheint, genauso wie x86-32.)
Die ersten 8 Fließkomma-Argumente werden in %xmm0-7 später auf dem Stack übergeben. Es gibt keine Call-Preserved-Vektorregister. (Eine Funktion mit einer Mischung aus FP- und Integer-Argumenten kann insgesamt mehr als 8 Registerargumente haben.)
Variadische Funktionen (wie printf
) benötigen immer %al
=die Anzahl der FP-Registerargumente.
Es gibt Regeln dafür, wann Strukturen in Register gepackt werden (rdx:rax
bei der Rückkehr) vs. im Speicher. Siehe die ABI für Details und überprüfen Sie die Compiler-Ausgabe, um sicherzustellen, dass Ihr Code mit den Compilern übereinstimmt, wie etwas übergeben/zurückgegeben werden soll.
Beachten Sie, dass die x64-Funktionsaufrufkonvention von Windows mehrere signifikante Unterschiede zu x86-64 System V aufweist, z. B. Schattenraum, der muss vom Anrufer reserviert werden (anstelle einer roten Zone) und anruferhalten xmm6-xmm15. Und sehr unterschiedliche Regeln dafür, welches Argument in welches Register kommt.
Vielleicht suchen Sie nach dem x86_64 ABI?
- www.x86-64.org/documentation/abi.pdf (404 am 24.11.2018)
- www.x86-64.org/documentation/abi.pdf (über Wayback Machine am 24.11.2018)
- Wo ist die x86-64 System V ABI dokumentiert? - https://github.com/hjl-tools/x86-psABI/wiki/X86-psABI wird aktuell gehalten (von HJ Lu, einem der ABI-Betreuer) mit Links zu PDFs der offiziellen aktuellen Version.
Wenn Sie nicht genau danach suchen, verwenden Sie 'x86_64 abi' in Ihrer bevorzugten Suchmaschine, um alternative Referenzen zu finden.
Aufrufkonventionen definieren, wie Parameter in den Registern übergeben werden, wenn ein anderes Programm aufgerufen wird oder von einem anderen Programm aufgerufen wird. Und die beste Quelle für diese Konventionen sind ABI-Standards, die für jede dieser Hardware definiert sind. Zur Vereinfachung der Kompilierung wird dieselbe ABI auch von Userspace und Kernel-Programm verwendet. Linux/Freebsd folgen demselben ABI für x86-64 und einem anderen Satz für 32-Bit. Aber x86-64 ABI für Windows unterscheidet sich von Linux/FreeBSD. Und im Allgemeinen unterscheidet ABI nicht zwischen Systemaufrufen und normalen "Funktionsaufrufen". Das heißt, hier ist ein bestimmtes Beispiel für x86_64-Aufrufkonventionen, und es ist sowohl für den Linux-Benutzerbereich als auch für den Kernel gleich:http://eli.thegreenplace.net/2011/ 09/06/stack-frame-layout-on-x86-64/ (beachten Sie die Reihenfolge a,b,c,d,e,f der Parameter):
Performance ist einer der Gründe für diese ABI (z. B. Übergabe von Parametern über Register statt Speichern in Speicherstacks)
Für ARM gibt es diverse ABI:
http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.subset.swdev.abi/index.html
https://developer.apple.com/library/ios/documentation/Xcode/Conceptual/iPhoneOSABIReference/iPhoneOSABIReference.pdf
ARM64-Konvention:
http://infocenter.arm.com/help/topic/com.arm.doc.ihi0055b/IHI0055B_aapcs64.pdf
Für Linux auf PowerPC:
http://refspecs.freestandards.org/elf/elfspec_ppc.pdf
http://www.0x04.net/doc/elf/psABI-ppc64.pdf
Und für embedded gibt es den PPC EABI:
http://www.freescale.com/files/32bit/doc/app_note/PPCEABI.pdf
Dieses Dokument ist ein guter Überblick über all die verschiedenen Konventionen:
http://www.agner.org/optimize/calling_conventions.pdf