2016-07-25 50 views
10

Die Funktionsaufrufkonventiondefiniert ganzzahliges Argument # 4, das in dem rcx-Register übergeben werden soll. Der Linux-Kernel syscall ABI hingegen verwendet r10 für denselben Zweck. Alle anderen Argumente werden in denselben Registern für Funktionen und Syscalls übergeben.Unterschied in ABI zwischen x86_64 Linux-Funktionen und syscalls

Dies führt zu einigen seltsamen Dingen. Schauen Sie sich zum Beispiel die Umsetzung von mmap in glibc für die x32-Plattform (für die die gleiche Diskrepanz existiert):

00432ce0 <__mmap>: 
    432ce0:  49 89 ca    mov %rcx,%r10 
    432ce3:  b8 09 00 00 40   mov $0x40000009,%eax 
    432ce8:  0f 05     syscall 

So alle Register sind bereits vorhanden, es sei denn wir rcx zu r10 bewegen.

Ich wundere mich, warum nicht die syscall ABI zu der gleichen wie der Funktionsaufruf ABI zu definieren, unter Berücksichtigung, dass sie bereits so ähnlich sind.

+0

In [einer anderen ABI-Antwort] (http://stackoverflow.com/a/35619528/224132) habe ich einige Links zu AMD-Mailinglisten-Posts von AMD-Architekten und Linux-Kernel-Entwicklern ausgegraben, bevor das erste AMD64-Silicon veröffentlicht wurde . Es gibt einige interessante Dinge dort, wie die experimentellen Ergebnisse (aus dem Zusammenstellen von SPECint und dem Betrachten von Code-Größe und Anzahl von Anweisungen), die zu den x86-64 SysV ABI's Auswahlmöglichkeiten führten, welches Register für welchen Zweck verwendet werden sollte. –

Antwort

5

Die syscall instruction bietet eine schnellere Methode zur Eingabe von Ring-0, um einen Systemaufruf auszuführen. Dies ist eine Verbesserung gegenüber der alten Methode, die einen Software-Interrupt auslösen soll (int 0x80 unter Linux).

Ein Teil des Grundes, warum die Anweisung schneller ist, ist, weil sie den Speicher nicht ändert oder sogar rsp ändert, um auf einen Kernel-Stack zu zeigen. Im Gegensatz zu einem Software-Interrupt, bei dem die CPU gezwungen ist, das Betriebssystem wieder in Betrieb zu setzen, ohne etwas zu überlisten, darf die CPU für diesen Befehl annehmen, dass die Software weiß, dass hier etwas passiert.

Insbesondere speichert syscall zwei Teile des Benutzer-Space-Status in Registern. Die RIP, zu der nach dem Anruf zurückzukehren ist, wird in rcx gespeichert und die Flags werden in R11 (because RFLAGS is masked with a kernel-supplied value before entry to the kernel) gespeichert. Dies bedeutet, dass beide Register von der Anweisung geplagt werden.

Da sie geplottert sind, verwendet der syscall ABI ein anderes Register anstelle von rcx, daher die Verwendung von r10 für das 4. Argument.

r10 ist eine natürliche Wahl, da in the x86-64 SystemV ABI es für das Bestehen Funktion args nicht verwendet wird, und Funktionen müssen nicht ihren Anrufer Wert von r10 zu bewahren. So kann eine Syscall-Wrapper-Funktion mov %rcx, %r10 ohne Speichern/Wiederherstellen. Dies wäre mit keinem anderen Register möglich, für 6-Arg-Syscalls und die SysV-ABI-Funktionsaufrufkonvention.


BTW, das 32-Bit-System aufrufen ABI mit sysenter auch zugänglich ist, die Zusammenarbeit zwischen den User-Space und Kernel-Space, um nach einem sysenter User-Space zu ermöglichen Rückkehr erfordert. (d. h. Speichern eines Zustands im Benutzerbereich vor dem Ausführen von sysenter). Dies ist eine höhere Leistung als int 0x80, aber peinlich. Trotzdem verwendet glibc es (indem es zu User-Space-Code auf den vdso-Seiten springt, die der Kernel in den Adressraum jedes Prozesses abbildet).

AMDs syscall ist eine andere Herangehensweise an die gleiche Idee wie Intels sysenter: um den Ein-/Ausstieg aus dem Kernel weniger teuer zu machen, indem man nicht unbedingt alles erhält.

+0

Es ist subtiler als nur ein paar Läden mit Register-Register Moves zu ersetzen. Es ändert nicht 'rsp', um auf den Kernel-Stack zu zeigen, also würde es keinen vernünftigen Ort geben, um irgendetwas zu schieben, das es speichern wollte. Kernel-Code am Einstiegspunkt muss das selbst tun. (mit 'swapgs' kann '[gs: absolute_address]' aktiviert werden, um auf Kernel-Daten pro Aufgabe zuzugreifen). Die CPU speichert intern auch keinen Kernel-Stack-Pointer für 'syscall', nur einen gespeicherten' gs'-Wert. Ich denke, hier kommt die Reduzierung der Implementierungskomplexität zustande. (Und das 'swackgs' ist eine separate Anweisung). –

+0

Der Teil über C/C++, der 'r10' nicht verwendet, ist bedeutungslos. Der Kernel darf nicht annehmen, welche Sprache den Aufruf ausführt. –

+0

Das ist in Ordnung. Ich habe es bearbeitet. –

4

AMDs syscall Clobbers das rcx Register, also r10 wird stattdessen verwendet.

+1

Und 'r10' ist ein reines Scratch-Register: nicht für die Funktion arg-passing verwendet und nicht rufkonserviert. Dadurch können andere Wrapper-Funktionen wie Dynamic-Linker-Stubs es als temporär verwenden und trotzdem mit einem 'jmp' anstelle eines' call'/'pop' /' ret' tail-calling können. So ist 'r10' eine gute Wahl für syscalls. 'syscall' /' sysret' verwendet auch r11. –

+0

Hoppla, ich dachte an 'r11'. Der ABI sagt "r10" wird verwendet, um einen "statischen Kettenzeiger" zu übergeben. C/C++ benutzt das nicht, also ist 'r10' in der Praxis auch ein reines Scratch-Register. –