004
04.01.2006, 08:04 Uhr
virtual
Sexiest Bit alive (Operator)
|
Der Instructionpointer (IP) ist ein Register, welches immer auf den als nächstes auszuführenden Befehl zeigt. Wenn Du einen Befehl ausführst, wird der IP automatisch vom Prozessor hochgezählt. Eigentlich gibt es nur vier möglichkeiten, den IP zu verändern: 1. Wie angesprochen durch die Ausführung eines Befehls 2. Durch ausführung eines Sprungbefehls. Die Zieladresse des Sprungbefehls beeinflußt den IP 3. Aufruf einer Unterroutine 4. Modifikation einer auf dem Stack abgelegten Rücksprungadresse (wird eigentlich nur dann genutzt, wenn man das Programm was anderes tun lassen will, als es soll insbes. beim Cracken/Hacken von Programmen)
Analog zeigt der Stackpointer (SP) auf den ersten freien SPeicherplatz auf dem Stack., In welche richtung der Stack wächst, hängt vom System ab. Hier gibt es ebenfalls vier Möglichkeiten, den SP zu verändern: 1. Durch pushen oder poppen von Werten auf den Stack 2. Aufrug einer Unterroutine 3. Explizites addieren/subrahieren eines Wertes von SP 4. Explizites Setzen eines neuen Wertes für SP. Diese letzte Möglichkeit finde man eher selten (zB in Bootloadern oder im Systemkern, wenn ein Prozess gestartet wird)
Der Basepointer dient im wesentlichen zum Referenzieren von lokalen Variablen von Funktionen. Im Unterschied zum IP und SP gibt es hier allerdings keinen Automatismus, welche den BP neu setzt, wenn eine Unterroutine aufgerufen wird, dies geschieht meist durch ein explizites kopieren von SP nach BP und aufaddieren eines Konstante, die angibt, wie viel Platz die Lokalen Variablen benötigen. Die lokalen Variablen werden relativ zum BP adressiert.
Beim Aufruf der Fkt. test passiert also folgendes: 1. Zunächst werden die Parameter auf den Stack abgelegt. Da es in C /C++ möglich ist, variable Parameterlisten zu haben ist - anders als etwa Pascal - der Aufrufer dafür verantwortlich, nach Funktionsaufruf die Parameter wieder vom Stack zu entfernen. Ausserdem werden in C die Parameter in umgekehrter Reihenfolge auf dem Stack abgelegt, eben aus dem Grund, weil die Parameterliste variabel sein kann (denke an printf) und daher die Aufgerufene Routine vor allem wissen muß, wo die ersten Parameter liegen. Das "Parameter auf den Stack legen" verändert eigentlich nur den SP.
2. Jetzt wird die Funktion Aufgerufen. Dazu wird ein Assemblerbefehl namens "call" o.ä. ausgeführt. Dieser macht zweierlei: zunächst pusht er auf den Stack, wo denn nach dem Abarbeiten der Funktion in main weiter gemacht werden soll. Dh er schreibt den zukünftigen wert vom IP auf den Stack. Anschließend schreibt er nach IP die Anfangsadresse der Routine. Somit werden sowohl IP alsauch SP verändert.
3. Beim Eintritt in die Funktion liegn also erstmal die Rücksprungadresse und die Parameter auf dem Stack. Um die Parameter aber auch noch anzulegende lokale Variablen ansprechen zukönnen, muß man also Element auf dem Stack ansprechen. Die Routine geht also hin und kiopiert in den BP den Wert vom SP. Dies wird genau einmal am Anfang gemacht, so daß man im folgenden über den BP die Variablen/Parameter referenziert und frei ist, weitere Routinen aufzurufen, welche ja den SP verändern könnte. Da das aber alle so machen, ist es notwendig, den alten Wert vom BP zuvor zu speichern. Er wird daher auf dem stack abgelegt. Danach wird BP = SP gesetzt.
4. Nun werden die lokalen Variablen angelegt. Dies geschied einfach dadurch, indem man zu SP den Platzbedarf für die Lokalen Variablen addiert/subtrahiert. In unserem Ursprungsbepiel war SP=0xbffff42c: Da buffer 16 Byte benötigt, wurden 16 Byte abgezogen und man erhält 0xbffff41c
5. Die Routine wird nun abgearbeitet. Für in der Routine abgearbeitete Funktionen wiederholen sich die hier dargestellten Schritte.
6. Am Ende wird im Wesentlichen BP zurück nach SP kopiert, was die lokalen Variablen löscht. Anschließend wird der alte Wert von BP restauiert, indem man ihn vom Stack popt. Dann wird ein ret -Assembler befehl aufgerufen, welcher den im 2. Schritt auf den Stack gespeicherten IP popt und setzt. dadurch ist der Fkt.aufruf fast beendet:
7. Es werden die in 1 auf den Stack plazierten Elemente vom Stack genommen.
Je nach Betriebssystem und Rechnerarchitektur gibt es hier auch noch abweichungen, aber das Grundmodell ist in etwa so wie hier beschrieben. -- Gruß, virtual Quote of the Month Ich eß' nur was ein Gesicht hat (Creme 21) |