2 - Aufrufkonvention [ID:36118]
50 von 104 angezeigt

agn equal sit t

Eigentlich könnten Übersetzer machen, was sie wollen,

Speicher und Register beliebig verwenden

Hauptsache das resultierende Programm funktioniert im Anschluss

Das kann allerdings dann zu einem Problem werden,

wenn wir ein Unterprogramm verwenden wollen

welches von einem anderen Übersetzer erstellt wurde

vielleicht wegen einer anderen Programmiersprache

Um Interoperabilität zu gewährleisten

müssen sich die Übersetzer an eine Aufrufkonvention halten.

Zum Beispiel, wer welche Register bei einem Funktionsaufruf sichern muss.

Der Aufrufende muss sich um die flüchtigen Register kümmern

und die aufgerufene Funktion um die nicht flüchtigen,

sofern sie diese denn überhaupt verwendet.

Aber welche Register sind nun was?

Wir sind im Long Mode auf der 64-bit x86 Architektur.

Für uns spielen weder Segmentregister noch Kontroll- oder Diebakregister eine Rolle beim Funktionsaufruf.

Und da wir uns im Betriebssystem bewegen, ignorieren wir auch die SSE-Register,

sondern konzentrieren uns primär auf die Allzweck-Register,

um berücksichtigen Statusregister und Stapel sowie Instruktionszeiger.

Nach der System-5-ABI für 64-Bit sind diese zehn blau eingefärbten Register flüchtig definiert.

Auch Scratch oder ColorSafe genannt, müssen also von der aufrufenden Funktion zuerst gesichert werden.

Die anderen Register sind nicht flüchtig,

natürlich mit Ausnahme der Sonderfälle Stapel und Instruktionszeiger.

Wenn ich nun die flüchtigen Register E bei Verwendung sichern und später

wieder herstellen muss,

dann würden sich diese doch eigenen Daten im Anschluss an die

aufzurufende Funktion zu übermitteln.

Und das wird auch gemacht.

RDI wird beispielsweise für den ersten und RSI für den zweiten

Funktionsparameter verwendet,

während der Rückgabewert in rax geschrieben wird.

Wie sieht das nun in der Praxis aus?

Bei einem Funktionsaufruf mit eben zwei Parametern.

Schauen wir uns dazu den Aufruf von Funk mit Parameter 23 und 42 an.

Der Resultieren der Assembler-Code für den Aufruf könnte in etwa wie folgt aussehen.

Dabei verwende ich hier die Intel-Syndex, wie sie auch von unserem NetWide-Assembler verwendet wird.

Bei dieser wird zuerst das Ziel, dann die Quelle genannt.

Im Gegensatz zur AT&T-Syndex, die genau anders herum ist.

Aber aufgrund zusätzlicher Zeichen leicht identifiziert werden kann, wie dem Prozentzeichen vor Registernamen.

Letzteres wird übrigens standardmäßig beim InlineAssembly und in ObjectDAMP verwendet.

Als erstes werden, sofern nötig, flüchtige Register gesichert, beispielsweise R9.

Anschließend werden die Parameter in die entsprechenden Register geschrieben.

0x2a entspricht 42 dem zweiten Parameter und wird somit in RSI geschrieben.

0x17 für 23 als ersten Parameter in RDI.

Der Compiler generiert, wie hier zu sehen, für kleinere Zahlen innerhalb der 32-Bit-Grenze

ein Move nach ESI statt RSI.

Das ist valit, da dabei automatisch die oberen 32-Bit von RSI auf Null gesetzt werden.

Der Aufruf hat also die gleiche Wirkung, es werden sich durch diese Variante jedoch

ein paar Bytes im Maschinencode gespart.

Teil einer Videoserie :
Teil eines Kapitels:
Threadumschaltung

Zugänglich über

Offener Zugang

Dauer

00:07:45 Min

Aufnahmedatum

2020-08-10

Hochgeladen am

2021-09-20 18:56:41

Sprache

de-DE

Kontextsicherung und Parameterübergabe nach System-V-ABI für Aufgabe 4 der Lehrveranstaltung Betriebssysteme.

Folien und Transkript zum Video.

Tags

betriebssysteme operating systems stubs calling convention
Einbetten
Wordpress FAU Plugin
iFrame
Teilen