Die folgenden Links erläutern die x86-32-Systemaufrufkonventionen für UNIX (BSD-Version) und Linux:
Aber wie lauten die x86-64-Systemaufrufkonventionen unter UNIX und Linux?
Die folgenden Links erläutern die x86-32-Systemaufrufkonventionen für UNIX (BSD-Version) und Linux:
Aber wie lauten die x86-64-Systemaufrufkonventionen unter UNIX und Linux?
sysret
wird durch den Rückgabewert ersetzt. Alle anderen Register bleiben auf amd64 erhalten.Antworten:
Weitere Informationen zu den folgenden Themen finden Sie hier: Der endgültige Leitfaden für Linux-Systemaufrufe
Ich habe diese mit GNU Assembler (Gas) unter Linux überprüft.
Kernel-Schnittstelle
x86-32 aka i386 Linux Systemaufrufkonvention:
In x86-32 werden Parameter für Linux-Systemaufrufe mithilfe von Registern übergeben.
%eax
für syscall_number. % ebx,% ecx,% edx,% esi,% edi,% ebp werden zum Übergeben von 6 Parametern an Systemaufrufe verwendet.Der Rückgabewert ist in
%eax
. Alle anderen Register (einschließlich EFLAGS) werden in der gesamten Region beibehaltenint $0x80
.Ich nahm folgenden Ausschnitt aus dem Linux Assembly Tutorial genommen , bin mir aber nicht sicher. Wenn jemand ein Beispiel zeigen kann, wäre es großartig.
Ein Beispiel und etwas mehr Informationen finden Sie unter http://www.int80h.org/bsdasm/#alternate-calling-convention . Ein weiteres Beispiel für eine Hello World für i386 Linux mit
int 0x80
: Hello, Welt in Assemblersprache mit Linux-Systemaufrufen?Es gibt eine schnellere Möglichkeit, 32-Bit-Systemaufrufe durchzuführen: using
sysenter
. Der Kernel ordnet jedem Prozess (dem vDSO) eine Speicherseite mit der User-Space-Seite dessysenter
Tanzes zu, die mit dem Kernel zusammenarbeiten muss, damit er die Rücksprungadresse finden kann. Die Zuordnung von Arg zu Register ist dieselbe wie fürint $0x80
. Normalerweise sollten Sie das vDSO aufrufen, anstatt essysenter
direkt zu verwenden. (Weitere Informationen zum Verknüpfen und Aufrufen von vDSO sowie weitere Informationen zu und alles andere, was mit Systemaufrufen zu tun hat, finden Sie im Definitiven Handbuch zu Linux-sysenter
Systemaufrufen.)x86-32 [Free | Open | Net | DragonFly] BSD UNIX-Systemaufrufkonvention:
Parameter werden auf dem Stapel übergeben. Schieben Sie die Parameter (letzter Parameter zuerst gedrückt) auf den Stapel. Drücken Sie dann weitere 32-Bit-Dummy-Daten (es handelt sich nicht 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:
x86-64 Mac OS X ist ähnlich, aber unterschiedlich . TODO: Überprüfen Sie, was * BSD tut.
Siehe Abschnitt: "A.2 AMD64- Linux- Kernelkonventionen" der System V-Anwendungsbinärschnittstelle AMD64 Architecture Processor Supplement . Die neuesten Versionen der psABIs von i386 und x86-64 System V finden Sie auf dieser Seite im Repo des ABI-Betreuers . (Siehe auch diex86 Tag-Wiki für aktuelle ABI-Links und viele andere gute Dinge über x86 asm.)
Hier ist der Ausschnitt aus diesem Abschnitt:
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 genau.)
Dieses 32-Bit -
int $0x80
ABI ist verwendbar in 64-Bit - Code (aber sehr nicht empfohlen). Was passiert, wenn Sie das 32-Bit-Linux-ABI int 0x80 in 64-Bit-Code verwenden? Es schneidet seine Eingaben immer noch auf 32-Bit ab, ist also für Zeiger ungeeignet und setzt r8-r11 auf Null.Benutzeroberfläche: Funktionsaufruf
x86-32 Funktionsaufrufkonvention:
In x86-32 wurden Parameter auf Stack übergeben. Der letzte Parameter wurde zuerst auf den Stapel geschoben, bis alle Parameter fertig sind, und dann wurde die
call
Anweisung ausgeführt. Dies wird zum Aufrufen von Funktionen der C-Bibliothek (libc) unter Linux aus der Assembly verwendet.Moderne Versionen des i386 System V ABI (unter Linux verwendet) erfordern eine 16-Byte-Ausrichtung
%esp
vor acall
, wie dies beim x86-64 System V ABI immer erforderlich war. Callees dürfen davon ausgehen und SSE-16-Byte-Ladevorgänge / -Speicher verwenden, die diesen Fehler bei nicht ausgerichteter Ausführung verursachen. In der Vergangenheit erforderte Linux jedoch nur eine 4-Byte-Stapelausrichtung, sodass zusätzliche Arbeit erforderlich war, um natürlich ausgerichteten Speicherplatz selbst für 8 Bytedouble
oder ähnliches zu reservieren .Einige andere moderne 32-Bit-Systeme erfordern immer noch nicht mehr als 4-Byte-Stapelausrichtung.
x86-64 System V User-Space Funktion Aufrufkonvention:
x86-64 System V übergibt Argumente in Registern, was effizienter ist als die Stapelargument-Konvention von i386 System V. Es vermeidet die Latenz und zusätzliche Anweisungen, Argumente im Speicher (Cache) zu speichern und sie dann wieder in den Angerufenen zu laden. Dies funktioniert gut, da mehr Register verfügbar sind, und ist besser für moderne Hochleistungs-CPUs geeignet, bei denen Latenz und Ausführung außerhalb der Reihenfolge von Bedeutung sind. (Der i386 ABI ist sehr alt).
In diesem neuen Mechanismus: Zuerst werden die Parameter in Klassen unterteilt. Die Klasse jedes Parameters bestimmt, wie er an die aufgerufene Funktion übergeben wird.
Vollständige Informationen finden Sie unter: "3.2 Funktionsaufrufsequenz" des AMD64-Architekturprozessor-Supplements der System V-Anwendungsbinärschnittstelle, das teilweise lautet:
Dies
%rdi, %rsi, %rdx, %rcx, %r8 and %r9
gilt auch für die Register , die verwendet werden, um Ganzzahl- / Zeigerparameter (dh die INTEGER-Klasse) von einer Assembly an eine beliebige libc-Funktion zu übergeben. % rdi wird für den ersten INTEGER-Parameter verwendet. % rsi für 2.,% rdx für 3. und so weiter. Danncall
sollte eine Anweisung gegeben werden. Der Stack (%rsp
) muss bei der Ausführung 16B-ausgerichtet seincall
.Wenn mehr als 6 INTEGER-Parameter vorhanden sind, werden der 7. INTEGER-Parameter und höher an den Stapel übergeben. (Anrufer knallt wie x86-32.)
Die ersten 8 Gleitkomma-Argumente werden später im Stapel in% xmm0-7 übergeben. Es gibt keine aufruferhaltenen Vektorregister. (Eine Funktion mit einer Mischung aus FP- und Integer-Argumenten kann insgesamt mehr als 8 Registerargumente enthalten.)
Variadische Funktionen ( wie
printf
) benötigen immer%al
= die Anzahl der FP-Registerargumente.Es gibt Regeln, wann Strukturen in Register (
rdx:rax
bei Rückgabe) oder im Speicher gepackt werden sollen . Weitere Informationen finden Sie im ABI. Überprüfen Sie die Compilerausgabe, um sicherzustellen, dass Ihr Code mit den Compilern übereinstimmt, wie etwas übergeben / zurückgegeben werden soll.Beachten Sie, dass die Windows x64-Funktionsaufrufkonvention mehrere signifikante Unterschiede zu x86-64 System V aufweist, z. B. Schattenbereich, der vom Aufrufer reserviert werden muss (anstelle einer roten Zone), und aufruferhaltenes xmm6-xmm15. Und ganz andere Regeln, für welche Argumente in welches Register gehen.
quelle
int 0x80
ABI in 64-Bit-Code verwenden, geschieht genau dies: stackoverflow.com/questions/46087730/… . Es setzt r8-r11 auf Null und funktioniert genau so, als würde es in einem 32-Bit-Prozess ausgeführt. In diesen Fragen und Antworten habe ich ein Beispiel, das zeigt, wie es funktioniert oder wie das Abschneiden eines Zeigers fehlschlägt. Und ich habe mich auch in die Kernelquelle vertieft, um zu zeigen, warum sie sich so verhält.Vielleicht suchen Sie den x86_64 ABI?
Wenn Sie nicht genau danach suchen, verwenden Sie 'x86_64 abi' in Ihrer bevorzugten Suchmaschine, um alternative Referenzen zu finden.
quelle
Aufrufkonventionen definieren, wie Parameter in den Registern übergeben werden, wenn sie von einem anderen Programm aufgerufen oder aufgerufen werden. Die beste Quelle für diese Konvention sind ABI-Standards, die für jede dieser Hardware definiert sind. Zur Vereinfachung der Kompilierung wird derselbe 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. X86-64 ABI für Windows unterscheidet sich jedoch von Linux / FreeBSD. Und im Allgemeinen unterscheidet ABI Systemaufrufe nicht von normalen "Funktionsaufrufen". Das heißt, hier ist ein spezielles Beispiel für x86_64-Aufrufkonventionen, das sowohl für den Linux-Benutzerbereich als auch für den Kernel gleich ist: 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):
Die Leistung ist einer der Gründe für diese ABI (z. B. Übergeben von Parametern über Register, anstatt in Speicherstapeln zu speichern).
Für ARM gibt es verschiedene 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 bietet einen guten Überblick über die verschiedenen Konventionen:
http://www.agner.org/optimize/calling_conventions.pdf
quelle
Linux Kernel 5.0 Quellkommentare
Ich wusste, dass x86-Details unter
arch/x86
sind und dass Syscall-Sachen untergehenarch/x86/entry
. Ein kurzer Blickgit grep rdi
in dieses Verzeichnis führt mich zu arch / x86 / entry / entry_64.S :und für 32-Bit unter arch / x86 / entry / entry_32.S :
Implementierung von glibc 2.29 Linux x86_64-Systemaufrufen
Lassen Sie uns nun betrügen, indem wir uns die wichtigsten libc-Implementierungen ansehen und sehen, was sie tun.
Was gibt es Schöneres, als in glibc zu schauen, das ich gerade verwende, während ich diese Antwort schreibe? :-)
glibc 2.29 definiert x86_64-Systemaufrufe unter
sysdeps/unix/sysv/linux/x86_64/sysdep.h
und enthält interessanten Code, z.und:
was ich für ziemlich selbsterklärend halte. Beachten Sie, wie dies anscheinend so konzipiert wurde, dass es genau der Aufrufkonvention der regulären System V AMD64 ABI-Funktionen entspricht: https://en.wikipedia.org/wiki/X86_calling_conventions#List_of_x86_calling_conventions
Schnelle Erinnerung an die Clobber:
cc
bedeutet Flaggenregister. Aber Peter Cordes sagt , dass dies hier nicht erforderlich ist.memory
bedeutet, dass ein Zeiger in der Assembly übergeben und für den Zugriff auf den Speicher verwendet werden kannEin explizites minimales ausführbares Beispiel von Grund auf finden Sie in dieser Antwort: Wie rufe ich einen Systemaufruf über sysenter in der Inline-Assembly auf?
Führen Sie einige Systemaufrufe in der Baugruppe manuell durch
Nicht sehr wissenschaftlich, aber lustig:
x86_64.S
GitHub stromaufwärts .
aarch64
Ich habe ein Beispiel für ein minimal ausführbares Userland unter /reverseengineering/16917/arm64-syscalls-table/18834#18834 gezeigt. Der TODO-Grep-Kernel-Code sollte hier einfach sein.
quelle
"cc"
Clobber ist nicht erforderlich: Linux-Systemaufrufe speichern / stellen RFLAGS wieder her (Diesyscall
/ -Anweisungensysret
tun dies mit R11, und der Kernel ändert die gespeicherten R11 / RFLAGS nur überptrace
Debugger-Systemaufrufe.) Nicht, dass dies jemals von"cc"
Bedeutung ist , da es sich um einen Clobber handelt implizit für x86 / x86-64 in GNU C Extended asm, sodass Sie nichts gewinnen können, wenn Sie es weglassen.