Übungsblatt Nr 7

Disclaimer: Dieser Thread wurde aus dem alten Forum importiert. Daher werden eventuell nicht alle Formatierungen richtig angezeigt. Der ursprüngliche Thread beginnt im zweiten Post dieses Threads.

Übungsblatt Nr 7
Hi

Ich verstehe die Aufgabe 7.1 nicht wirklich und auch aus der Lösung werde ich nicht wirklich schlau.Vielleicht kann mir jemand erklären was genau dort gemacht wird(vielleicht jeden Schritt)…auch aus dem mir vorliegen Skript werde ich nicht schlau…und woher weiß ich wieviel Speicher ich reservieren muss für meinen Stack? :anx:

Aufgabe:

Wie sieht die Funktion
int foo(int a,int b){return bar(a+b,a-b);}
mit und ohne Frame-Pointer in Assembler aus?

Lösung

ohne FP:
movl 4(%esp),%eax
subl 8(%esp),%eax
pushl %eax
movl 8(%esp),%eax
addl 12(%esp),%eax
pushl %eax
call bar
addl $8,%esp
ret

mit FP:
pushl %ebp
movl %esp,%ebp
movl 8(%ebp),%eax
subl 12(%ebp),%eax
pushl %eax
movl 8(%ebp),%eax
addl 12(%ebp),%eax
pushl %eax
call bar
movl %ebp,%esp
popl %ebp
ret


hi,

also die framepointer sache ist eigentlich ganz einfach.

hilfreich ist sich mal den stack anzuschauen.

bei version 1:
zum programmstart
|b | #als erstes parameter von foo
|a |
|ret |<-esp #ruecksprung adresse vom uebergeordneten programm

movl 4(%esp),%eax #lade a vom stack in eax
subl 8(%esp),%eax #ziehe b von a ab
pushl %eax speichere ergebnis auf stack um es spaeter an bar() zu uebergeben

stack sieht jetzt so aus:
|b |
|a |
|ret |
|a-b|<-esp

movl 8(%esp),%eax #a in eax, da wir vorher gepusht haben muessen wir jetzt um 8 statt 4 springen!

addl 12(%esp),%eax #a+b, hier das gleiche beim springen
pushl %eax #speichere ergebnis auf stack fuer bar

stack sieht jetzt so aus:
|b |
|a |
|ret |
|a-b |
|a+b|<-esp

call bar #rufe bar auf
addl $8,%esp #nachdem bar fertig ist, muessen wir speicher auf stack wieder aufrauemen, dh esp auf ret zurucksaetzen

stack:
|b |
|a |
|ret |<-esp

ret


verdammt jetzt ist mir doch am ende des zweiten teils tatsaechlich firefox abgeschmiert!!! :#:

also nochmal:

version 2

pushl %ebp #framepointer sichern falls vorgehendes programm auch fp verwendet

stack
|b |
|a |
|ret |
|ebp| <-esp

movl %esp,%ebp #framepointer auf akutelle esp-position setzen

stack
|b |
|a |
|ret |
|ebp| <-esp <-ebp

#wie bei version 1
movl 8(%ebp),%eax
subl 12(%ebp),%eax
pushl %eax

stack
|b |
|a |
|ret |
|ebp|<-ebp
|a-b|<-esp

#wie bei version2
movl 8(%ebp),%eax #fp position hat sich nicht veraendert!!!
addl 12(%ebp),%eax
pushl %eax

stack
|b |
|a |
|ret |
|ebp|<-ebp
|a-b|
|a+b|<-esp

call bar
movl %ebp,%esp #esp auf urspruengliche position zureucksetzen (also ret)
popl %ebp #fp restaurieren
ret


vorteile mit fp:

  • immer feste position auf stack, dh man braucht sich nicht ueberlegen wie viel man schon auf den stack gepusht hat
  • uebersichtlicher
  • leichter um programm von hinten nach vorne zu debuggen

nachteile:

  • ein register mehr wird belegt
  • bisschen mehr code

hi hätte noch eine verständnisfrage dazu :wink:

also der fp bleibt immer an der selben position springt für die ausführung irgendwo hin holt sich den kram und springt dann aber sofort wieder zurück…soweit richtig oder?

allerdings verstehe ich eine Anweisung noch nicht ganz…

bei der zweiten lösung

movl %ebp,%esp

ich schreibe in den stack den wert von dem framepointer???wieso denn das damit wird doch der Stack überschrieben und meine Returnadresse geht verloren oder nicht…dachte im Stack bleibt die ret addy erhalten und der fp führt alles aus und am ende löscht man ihn ohne das man movl %ebp,%esp
macht verstehe den Sinn davon nicht…


Naja er „springt“ eigentlich nicht wirklich, sondern die Adressen werden berechnet.
Im Register %ebp wird die Speicheradresse von Register %esp kopiert.

Danach werden die Adressen der benötigten Variablen anhand der Adresse im %ebp berechnet.
also zB
movl 8(%ebp),%eax
angenommen in %ebp steht Adresse 0x1000
dann werden 4byte an der adresse 0x1000 + 8 nach %eax kopiert.

Achtung! Der Stack steht nicht in einem Register, sondern im Register %esp steht eine Adresse die immer auf das Ende des Stacks zeigt.
Der Stack wird also nicht einfach überschrieben.

mit movl %ebp,%esp wird die Adresse in %ebp nach %esp kopiert,
der Stackpointer zeigt danach also auf die Adresse, die in %ebp steht.

stack
|b |
|a |
|ret |
|ebp|<-ebp
|a-b|
|a+b|<-esp

wird zu

stack
|b |
|a |
|ret |
|ebp|<-ebp<-esp
|a-b|
|a+b|

Nachdem der Stackpointer immer an das Ende des Stacks zeigt haben wir a-b und a+b sozusagen „gelöscht“.

Jetzt alles klar?
:wink:


also zusammengefasst:

am anfang steht im esp die ret adresse die wird nach ebp kopiert…danach mittels ebp adressen berechnet um werte auszulesen und der esp geht immer an die nächste freie Speicherposition später wird die ret adresse wieder in den esp gespeichert um aus dem unterprogramm zu kommen korrekt?


Nein!
Am %esp steht nicht die return-Adresse, die liegt auf dem Stack! %esp ist ein Pointer!
Er zeigt auf Speicheradressen an der jeweils Daten gespeichert sind.
Am Anfang zeigt er auf die Adresse an der die Return-Adresse liegt.

Angenommen die ret-Adresse ist 0x4112 und liegt auf dem Stack an Adresse 0x1000.
Dann steht in %esp 0x1000 und nicht 0x4112.


jo ok danke :slight_smile: