Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Delphi Frage zum Stack in Assembler (https://www.delphipraxis.net/83638-frage-zum-stack-assembler.html)

Tormentor32 5. Jan 2007 11:49


Frage zum Stack in Assembler
 
Hallo DPler!

Ich habe mir den Assembler Crashkur von [user]Balu der Bär[/user] angeschaut (sehr gutes Tutorial übrigens :thumb: ), aber bin beim rumprobieren auf ein Problem gestoßen (Ja, ich habe die Suche verwendet :wink: )

ich habe folgende Funktion:

Delphi-Quellcode:
function Addiere4(A,B,C,D: integer): integer;
asm
          ADD  EAX, B
          ADD  EAX, C
          ADD  EAX, D
end;
Da ich aber gerne mehr über den Stack wissen würde, und folgendes Gilt
Code:
EAX = A | EDX = B | ECX = C
müsste D ja auf dem Stack liegen, also sollte
Delphi-Quellcode:
function Addiere4(A,B,C,D: integer): integer;
asm
          ADD  EAX, B
          ADD  EAX, C
          POP  EDX
          ADD  EAX, EDX
end;
doch auch funktionieren oder?

Tuts aber nicht, ich kriege nur eine AV bei Adresse 0x00000000 ! Wo liegt mein Denkfehler? (ich dachte ich hätte es verstanden :cry: )

Gruß Richard

Gruber_Hans_12345 5. Jan 2007 11:58

Re: Frage zum Stack in Assembler
 
hast du dir mal im CPU Window vom Delphi angeschaut, wie deine Funktion aufgerufen wird?
Also, ob wirklich die variablen als Register übergeben werden, denn irgendwann gehen dir die registerplätze aus, und delphi muß dann die parameter per stack übergeben und nicht mehr per register.
Also einfach mal nen Breakpoint zum aufruf deiner funktion und dann das cpu fenster einblenden, da müßtest sehen, wie die parameter übergeben werden.

Tormentor32 5. Jan 2007 12:07

Re: Frage zum Stack in Assembler
 
Hmm... Was auch immer auf dem Stack liegt, es ist nicht D... ich übergebe die Zahl 4 und auf dem Stack liegt 1242668
Wie ist das denn dann mit dem Stack, komme ich, außer mit zb
Code:
ADD EAX, D
nicht anders an D?

Kedariodakon 5. Jan 2007 12:10

Re: Frage zum Stack in Assembler
 
Ich hab jetzt nicht soviel Ahnung von ASM, aber so wie es aussieht, werden die ersten 3 Zahlen per Register übergeben und die letzte Zahl auf den stack gelegt...

Am anfang der Funktion wird och eine Adresse (Welche auch immer) auf den Stack gelegt...
Dann beginnt das Rechnen... mit dem POP und holst du dir nun aber die Adresse die da hochgelegt wurde...
Schlussendlich will er sich die auf dem Stack gelegte Adresse hollen, die ja nicht mehr da ist, da du diese ja gePOPed hast :zwinker: und bekommt deine 4te Zahl...
Da springt er dann hin...

Da da aber nicht das is was sein sollte, macht er kräftig aua...


Ich hoffe das war so richtig :wink:


Bye

Robert Marquardt 5. Jan 2007 12:14

Re: Frage zum Stack in Assembler
 
POP ist immer falsch. Damit verschiebst du ja den Stackpointer und alles geht den Bach runter.

Tormentor32 5. Jan 2007 12:16

Re: Frage zum Stack in Assembler
 
Du Meinst der Holt Die Adresse, die er eigentlich braucht, um zurückzukehren vorher raus, und nimmt stattdessen meinen Wert von D als Rücksprungadresse?

Finde ich aber blöd, wäre es nicht sinnvoller, wenn er die Die Rücksprungadresse als erstes auf den Stack legt, schließlich handelt es sich ja um FIFO


Zitat:

POP ist immer falsch. Damit verschiebst du ja den Stackpointer und alles geht den Bach runter.
aber wie komme ich sonst an den Stack? Und den Wert von D (ohne das von oben zu benutzen)

edit: So muss jetzt leider weg, werde nachher nochmal reinschauen

sveni2211 5. Jan 2007 12:28

Re: Frage zum Stack in Assembler
 
Zitat:

Zitat von Tormentor32
Du Meinst der Holt Die Adresse, die er eigentlich braucht, um zurückzukehren vorher raus, und nimmt stattdessen meinen Wert von D als Rücksprungadresse?

Genau das macht er. Wenn du ASM aufrufst unterbricht das dein ganzes Programm, die Register werden auf den Stack gelegt und als letztes kommt da die Rücksprungadresse drauf. Steht die am Ende deiner Assemblerfunktion nicht wieder ganz oben auf dem Stack, dann springt das Programm irgendwo hin. EDX ist dabei eine Ausnahme. Ich glaube, das lag daran, das EDX für Sprungberechnungen gebraucht wird.

Zitat:

Zitat von Tormentor32
Finde ich aber blöd, wäre es nicht sinnvoller, wenn er die Die Rücksprungadresse als erstes auf den Stack legt, schließlich handelt es sich ja um FIFO

FIFO? FIFO wäre die serielle Schnittstelle. Der Stack ist LIFO. Der Wert, denn du zuletzt ablegst, liegt oben und wird zuerst wieder geholt. Also Last In First Out.


Zitat:

Zitat von Tormentor32
aber wie komme ich sonst an den Stack?

Werte vom Stack werden immer mit POP abgerufen und mit PUSH abgelegt.

Wenn du jetzt wüsstest, dass der Wert von D auf dem Stack genau VOR der Zieladresse steht, kämst du mit zweimal POP dran. Müsstest dann aber die Zieladresse zurückschreiben.
Also z.B.

POP EDX
POP ECX
PUSH EDX

Dann steht der Wert von vor der Rücksprungadresse auf ECX. Dann wäre aber natürlich der Wert von C weg. ;-)

Ich denke aber nicht, dass das funktioniert. Weil vor der Rücksprungadresse liegen die alten Registerwerte. Direkt vor der Rücksprungadresse müsste also EPI liegt. Du müsstest also die ganzen alten Registerwerte erstmal weg- POPen und dann zurück. Wäre also recht sinnlos, vor allem, wo du nur 4 Register hast (ohne Speicherzugriffe). Hätte dann so ein wenig was von den Türmen von Hanoi. ;-)

JasonDX 5. Jan 2007 12:33

Re: Frage zum Stack in Assembler
 
Zitat:

Zitat von sveni2211
Werte vom Stack werden immer mit PUSH abgerufen. Bedenk dann aber, dass, wenn du ein PUSH aufrufst ohne vorher POP benutzt zu haben, du die Rücksprungadresse vom Stack ziehst und die dann dort nicht mehr vorhanden ist.

Du Verwechselst Push und Pop. Zudem: Man kann den Stack wie einen ganz normalen Speicher verwenden, inklusive Random Access:
Delphi-Quellcode:
MOV EAX, [ESP + 12]
bspw.

greetz
Mike

sirius 5. Jan 2007 12:57

Re: Frage zum Stack in Assembler
 
Der Compiler von Delphi gestaltet den Funktionsaufruf recht unterschiedlich.
Dabei spielen folgende Kriterien eine Rolle
-Ist die funktion vollständiger Assembler (beginnt gleich mit asm)
-gibt es lokale Variablen (d gilt hierbei auch irgendwie als lokale Variable, da sie auf dem Stack liegt
-wie sieht die Compilerdirektive aus ('stdcall', 'pascal' ,...)

In deinem fall hast du Recht:
Die ersten 3 Variablen liegen in den "veränderbaren Registern" (eax,edx,ecx), das sind quasi die Register, bei denen es (abgesehen von Variablenübergabe) egal ist, wie sie vor bzw. nach dem Funktionsaufruf aussehen. Alle anderen Variablen landen auf dem Stack (bei 'stdcall' landen immer alle Variablen auf dem Stack)
Am besten immer im Einzelfall prüfen, was der Delphi-Compiler fabriziert.

Was passiert noch?
-also d wird auf den Stack gepusht
-->d liegt bei [esp]
-dann folgt der Funktionsaufruf mit Call (und Call legt gleich die Rücksprungadresse mit auf den Stack)
-->also liegt d jetzt bei [esp+4];
zu Beginn der Funktion (der Compiler sieht lokale Variablen (d) auf dem Stack) rettet er sich noch schnell das EBP-Register (push ebp) und setzt dir freundlicherweise diesen Base-Pointer sozusagen als Referenz
-->also liegt d jetzt bei [esp+8] bzw. bei [ebp+8]

===> übergebene Parameter liegen jetzt bei [ebp+8], [ebp+12],... lokale Variablen kannst du dir auf [ebp-4],[ebp-8],... legen. Solltest vorher aber noch den Stackpointer um die entsprechende Anzahl verschieben, falls du mal Register auf den Stack pushen willst oder aus deiner Funktion Call's tätigst.

Tormentor32 5. Jan 2007 14:20

Re: Frage zum Stack in Assembler
 
Zitat:

Zitat von sveni2211
Zitat:

Zitat von tormentor32

Finde ich aber blöd, wäre es nicht sinnvoller, wenn er die Die Rücksprungadresse als erstes auf den Stack legt, schließlich handelt es sich ja um FIFO

FIFO? FIFO wäre die serielle Schnittstelle. Der Stack ist LIFO. Der Wert, denn du zuletzt ablegst, liegt oben und wird zuerst wieder geholt. Also Last In First Out.

Die verwechsel ich immer... :oops:

Also hab ich das jetzt richtig verstanden:

Die ersten drei Parameter landen in EAX, EDX und ECX
Der Rest landet auf dem Stack, wobei Ganz oben auf dem Stack die Rücksprungadresse liegt,
erst an DRITTER (?) Stelle (also +8 ) [EDIT: DER MACHT DA JA NEN SMILIE DRAUS, WENN ICH DA KEIN LEERZEICHEN EINFÜGE]
des Stacks befindet sich der vierte Parameter der Funktion,
ich kann ihn über [ebp+8 ] ansprechen

Dieses Verhalten ist standardmäßig bei ein Funktion mit folgenden Eigenschaften

1. Es ist eine reine Assemblerfunktion
2. Sie hat mindestens 4 Parameter
3. Es gibt keine (?) lokalen variablen in der Funktion
4. Nochwas?

edit: Diese blöden Quote tags...
edit3: schon wieder diese tags...

sirius 5. Jan 2007 15:43

Re: Frage zum Stack in Assembler
 
Zitat:

Dieses Verhalten ist standardmäßig bei ein Funktion mit folgenden Eigenschaften

1. Es ist eine reine Assemblerfunktion
2. Sie hat mindestens 4 Parameter
3. Es gibt keine (?) lokalen variablen in der Funktion
4. Nochwas?
Jein!

Als standardmäßig würde ich sowieso nichts erwarten. Bsw. kann Delphi auch mal eine lokale Variable (z.B. Zählvariable) nur im Register vorhalten und gar nicht erst auf den Stack legen. Dadurch können wieder ganz andere Sachen entstehen...
Wenn was nicht klappt kann man eh mit dem Debugger dürber gehen und sich ansehen, was der Compiler so hergestellt hat. [Strg]+[Alt]+[C] während des Debuggens hilft da Wunder.

1. Gehen wir davon aus, du baust eine reine Assemblerfunktion. Da musst du dir ja deinen Speicherplatz für lokale Variablen selber organisieren (sub esp, Anzahl_bytes).
2. Du übergibst nur 32 Bit-Variablen (Pointer oder Integer) - Boolean müsste auch gehen
(so, das müssten die Bedingungen gewesen sein)

--> Dan liegen die ersten 3 Parameter in den Registern und die nächsten (verkehrtrum) im Stack

also für
Delphi-Quellcode:
function test(m,n,o,p,q,r:integer):integer;
liegt
m -->eax
n -->edx
o -->ecx
p -->[ebp+16]
q -->[ebp+12]
r -->[ebp+8]
Das liegt an der Aufrufkonvention register (die man nicht angeben muss->standard)

Und jetzt für Aufrufkonvention stdcall
Delphi-Quellcode:
function test(m,n,o,p,q,r:integer):integer;stdcall;
m -->[ebp+8]
n -->[ebp+12]
o -->[ebp+16]
p -->[ebp+20]
q -->[ebp+24]
r -->[ebp+28]
(Die Reihenfolge ist genau andersrum und nix liegt in den Registern)
Das sind auch die unter Delphi gebräuchlichsten Aufrufkonventionen.

Was macht Delphi und warum ist das so (Beispiel für register):
Delphi-Quellcode:
//Pseudocode
push p
push q
push r
mov eax,m
mov eax,n
mov eax,o
call test

//-->Sprung zu test
push ebp
mov ebp,esp
.
.
//dein Code
.
.
pop ebp
ret
(für stdcall pusht er einfach die Register von hinten nach vorne durch vor dem Aufruf von call)

Demnach liegen auf dem Stack:
-p
-q
-r
-[Rücksprungadresse -->nächste Adresse unterhalb von call]
-ebp (gerettet, denn auch die aufrufende Funktion will nachher ihr Basis (Referenz)-Register für lokale Variablen wiederhaben)
(für stdcall liegen alle Parameter und alle andersrum im Stack)
Und das ist alles. Jetzt ist noch zu Beachten, das der stack (Stapel) auf dem Kopf liegt:
-die Adressen werden von oben nach unten kleiner
-Es wird immer unten angefügt

Ansonsten ist ein Stack ganz normal über Pointer adressierbar, man sollte nur wissen, wo man rumschreiben darf [esp+x] (lokale Variablen) und wo man maximal lesen darf [esp-x] (Parameter und alles andere)


Ich hoffe das war alles und es war alles richtig. Angaben sind ohne Gewähr! Bitte immer selber nachprüfen!


Edit: Verdammt: 1. Fehler korrigiert
Also bei sowas verhaspel ich mich immer. Wenn hier kein Fehler mehr drinn sein sollte könnt ihr mich küssen.

Edit2: Ich hab mal deine Funktionen kurz eingetippt
Delphi-Quellcode:
function addiere4(a,b,c,d:integer):integer;
asm
  add eax,edx
  add eax,ecx
  add eax,[ebp+8]
end;
function addiere4stdcall(a,b,c,d:integer):integer;stdcall;
asm
  mov eax,[ebp+8]
  add eax,[ebp+12]
  add eax,[ebp+16]
  add eax,[ebp+20]
end;
Aber wie gesagt, alles nur für Integer (bzw.Pointer). Ich glaub double landet immer auf dem Stack (egal ob register) und belegt natürlich auch 8 Bytes. Es kann allerdings auch der Stack der FPU sein. Komplizierte Compiler-Welt...

Tormentor32 7. Jan 2007 00:55

Re: Frage zum Stack in Assembler
 
Vielen Dank für die ausführliche Antwort!

Ich hab momentan kein Delphi zur Hand, aber ich werds am Montag ausprobieren, bzw damit rumprobieren, sollten noch weitere Fragen auftauchen, stelle ich sie einfach hier

Schönes Wochenende noch

Richard

sirius 7. Jan 2007 10:59

Re: Frage zum Stack in Assembler
 
Jetzt wollte ich grad noch einen kleinen Fehler korrigieren, aber durfte nicht mehr :cry: .

Also in den beiden Zeilen:
Zitat:

Ansonsten ist ein Stack ganz normal über Pointer adressierbar, man sollte nur wissen, wo man rumschreiben darf [esp+x] (lokale Variablen) und wo man maximal lesen darf [esp-x] (Parameter und alles andere)
..müsste eher [ebp+x] bzw. [ebp-x] stehen, da man ebp eher selten verändert (wenn man es natürlich macht, gilt diese Regel nicht mehr) aber esp ständig durch push und pop und Anlegen lokaler Variablen verändert wird.


Alle Zeitangaben in WEZ +1. Es ist jetzt 14:03 Uhr.

Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz