Discussion:
von 32 bit zu 64 bit
(zu alt für eine Antwort)
Jens Kallup
2014-12-12 15:00:33 UTC
Permalink
Hallo,

gibt es da was zu beachten (Parameter, call convention (C/c++), etc...)???

Gruß
Jens
Bernhard Schornak
2014-12-12 15:54:42 UTC
Permalink
Post by Jens Kallup
Hallo,
gibt es da was zu beachten (Parameter, call convention (C/c++), etc...)???
Deutsch:

http://de.wikipedia.org/wiki/Aufrufkonvention

Englisch:

http://www.agner.org/optimize/calling_conventions.pdf

In deutscher Sprache sind relativ wenig Dokumente verfügbar.
Ich kann die Grundlagen aber in Kurzform posten, wenn Du uns
mitteilst, ob es um Linux oder Windows geht.


Schönes Wochenende

Bernhard Schornak
Jens Kallup
2014-12-12 16:01:42 UTC
Permalink
Hallo Bernhard,

schön, das wieder ein bisschen Leben hier ist ... :-)
Post by Bernhard Schornak
Post by Jens Kallup
gibt es da was zu beachten (Parameter, call convention (C/c++), etc...)???
In deutscher Sprache sind relativ wenig Dokumente verfügbar.
Ich kann die Grundlagen aber in Kurzform posten, wenn Du uns
mitteilst, ob es um Linux oder Windows geht.
Eigentlich um beide, aber vorrangig Linux.
Post by Bernhard Schornak
Schönes Wochenende
Bernhard Schornak
Ebenfalls
Jens
Bernhard Schornak
2014-12-12 18:18:27 UTC
Permalink
Post by Jens Kallup
Hallo Bernhard,
schön, das wieder ein bisschen Leben hier ist ... :-)
Hallo Jens,

in den englischsprachigen Assembler-Foren wird momentan nicht
besonders viel diskutiert, das mich wirklich interessiert, da
bleibt genug Zeit für die "de-class"... ;)
Post by Jens Kallup
Post by Bernhard Schornak
Post by Jens Kallup
gibt es da was zu beachten (Parameter, call convention (C/c++), etc...)???
In deutscher Sprache sind relativ wenig Dokumente verfügbar.
Ich kann die Grundlagen aber in Kurzform posten, wenn Du uns
mitteilst, ob es um Linux oder Windows geht.
Eigentlich um beide, aber vorrangig Linux.
Windows:

Parameterübergabe in RCX, RDX, R8 und R9, respektive XMM0 bis
XMM3. Bei gemischten Parametern - Ganzzahl oder Fliesskomma -
das jeweils zutreffende GPR- oder XMM-Register in der angege-
benen Reihenfolge, z.B. XMM0, RDX,R8, XMM3.

Müssen mehr als vier Parameter übergeben werden, werden diese
auf dem Stapel ab 0x20[RSP] (aufwärts) abgelegt. Den darunter
liegenden Stapelbereich 0x00[RSP] - 0x1F[RSP] nennt Microsoft
"Schattenraum", er gehört der aufgerufenen Funktion, die dort
ihre Daten ablegen darf (was gegebenenfalls das Anlegen eines
Stapelrahmens erspart).

Die Register R10, R11, XMM4 und XMM5 sind laut Definition als
"volatile" deklariert, ihr Inhalt kann von der -aufgerufenen-
Funktion also überschrieben werden.

Rückgabewerte generell in RAX, Fliesskommawerte in XMM0.


Linux:

Parameterübergabe - Ganzzahlen in RDI, RSI, RDX, RCX, R8, R9,
Fliesskommazahlen in XMM0 - XMM7. Reichen die sechs oder acht
Parameter nicht aus, ist der Rest auf dem Stapel ab 0x00[RSP]
(aufwärts) abzulegen. Anders als bei Windows spielt dabei die
Reihenfolge keine Rolle. Aus der oben angegebenen Reihenfolge
würde für Linux also XMM0, RDI, RSI, XMM1.

Linux hat keinen Schattenraum, das heißt, dass der Stapel wie
beim 32 Bit ABI bei 0x00[RSP] losgeht. Dafür gibt's eine rote
Zone, die zwischen-0x08[RSP] und -0x80[RSP] liegt. Diese rote
Zone ist obligatorisch und muss von der aufgerufenen Funktion
(für was auch immer...) angelegt werden.

Die Register R10, R11 und XMM8 bis XMM15 sind laut Definition
"volatile", ihr Inhalt darf von der aufgerufenen Funktion je-
derzeit überschrieben werden.

Die Rückgabe erfolgt analog zu Windows.


Generell gilt, dass man den Stapel als durchgehende Folge von
64 Bit breiten Elementen (qwords) ansehen sollte, da RSP beim
Ausführen von Call, PUSH und POP acht Byte inkrementiert oder
dekrementiert wird. Des weiteren muss der Stapel generell auf
einer durch 16 teilbaren Adresse liegen.

In meinem Blog finden sich vertiefende Informationen über den
Stapel und Programmbeispiele für 64 Bit Windows (momentan nur
in englischer Sprache):

http://st-intelligentdesign.blogspot.de/


Schönes Wochenende

Bernhard Schornak
Ralph 'rkhb' Bauer
2014-12-12 20:39:16 UTC
Permalink
Post by Bernhard Schornak
Diese rote
Zone ist obligatorisch und muss von der aufgerufenen Funktion
(für was auch immer...) angelegt werden.
Hmmm, ich denke, die rote Zone muss eben **nicht** angelegt werden. Solange
der Stack nicht anderweitig gebraucht wird (z.B. durch Funktionsaufrufe),
kann eine Funktion dort Daten ablegen ohne den Stackpointer (RSP)
modifizieren zu müssen. Dann müsste auch RBP als "general purpose register"
für andere Zwecke frei werden.

viele grüße
ralph
Bernhard Schornak
2014-12-12 21:58:49 UTC
Permalink
Diese rote Zone ist obligatorisch und muss von der
aufgerufenen Funktion (für was auch immer...)
angelegt werden.
Hmmm, ich denke, die rote Zone muss eben **nicht** angelegt werden. Solange der Stack nicht
anderweitig gebraucht wird (z.B. durch Funktionsaufrufe), kann eine Funktion dort Daten ablegen ohne
den Stackpointer (RSP) modifizieren zu müssen. Dann müsste auch RBP als "general purpose register"
für andere Zwecke frei werden.
Hierzu habe ich diesen Artikel gefunden:

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

Andererseits hatte ich die "offiziellen" Papiere schon dahin
gehend verstanden, dass die "Red Zone" angelegt werden muss,
da systemweit garantiert ist, dass dieser in der AMD-64-Spe-
zifikation definierte Bereich von keiner folgenden Funktion,
auch nicht von Interrupthandlern oder anderen OS-Funktionen,
angetastet wird. Das kann allerdings nur dann funktionieren,
wenn der -allererste- Befehl jeder Funktion vom Stapelzeiger
mindestens die geforderten 120 Byte (die anderen 8 werden ja
bereits von der Rückkehradresse belegt) abgezogen werden. Es
können natürlich mehr Bytes abgezogen werden, wenn man einen
grösseren Stapelrahmen benötigt.

Auf diese Weise kann man auch RBP/RBX von ihrer Funktion als
"Basepointer" befreien, um zwei zusätzliche Register für den
alltäglichen Bedarf zu erhalten - meine "Intelligent Design"
getaufte Programmiertechnik beschäftigt sich ausführlich da-
mit... ;)

Andererseits bin ich (aus den bereits geschilderten Gründen)
kein Linux-Programmierer und kann daher nicht ausschliessen,
dass ich die Dokumente, die ich bislang zur System-V-ABI ge-
lesenen habe, fehlerhaft interpretiere.


Schönes Wochenende

Bernhard Schornak
Jens Kallup
2014-12-13 10:44:48 UTC
Permalink
Hallo,

aber was mache ich, wenn ich einen Funktionsaufruf
mit mehr als 7 parameter habe?
Zum Beispiel bei printf("%s....",p1, ,,, p8);??
Bleibt das dann unangetstet?
muss ich dann prefetch und/oder movq verwenden?


Gruß
Jens
Bernhard Schornak
2014-12-13 15:09:36 UTC
Permalink
Post by Jens Kallup
Hallo,
aber was mache ich, wenn ich einen Funktionsaufruf
mit mehr als 7 parameter habe?
Zum Beispiel bei printf("%s....",p1, ,,, p8);??
Bleibt das dann unangetstet?
muss ich dann prefetch und/oder movq verwenden?
Die in Register passenden Parameter werden in die Register
geladen, der Rest kommt auf den Stapel. Wenn Du mit dem in
Hochsprachen üblicherweise verwendeten dynamischen Stapel-
rahmen arbeitest, wäre das dann

x = (Parameter - 7) * 8,

wobei der aktuelle Wert von RSP aber durch 16 teilbar sein
muss. Zur Not müsstest Du also ein QWORD zum "padding" der
Adresse auf einen Paragraphenanfang einfügen.

subq rsp, x
movq 0x00(rsp), PARAMETER_8
movq 0x08(rsp), PARAMETER_9
movq 0x10(rsp), PARAMETER_10
movq 0x18(rsp), PARAMETER_11
...

Ein Prefetch bringt bei Stapelrahmen nicht viel, da in der
Regel der Stapel selten an einer Cacheline-Grenze beginnt,
und die aktuelle Cacheline daher oft schon in den L1-Cache
geladen ist.

Bei Windows (fester Stapelrahmen) sähe das Ganze so aus:

http://tinyurl.com/nkuted5 (Zeilen 122 bis 136)


Schönes Wochenende

Bernhard Schornak

Loading...