Welche Auswirkung haben diese beiden Anweisungen auf den von gcc für x86-Computer generierten Assemblycode?
Push %ebp
movl %esp, %ebp
die Erklärung von unwind ist die wörtliche Wahrheit (ungeachtet eines geringfügigen Richtungsfehlers), erklärt aber nicht warum.
%ebp
ist der "Basiszeiger" für Ihren Stapelrahmen. Dies ist der Zeiger, mit dem die C-Laufzeitumgebung auf lokale Variablen und Parameter im Stack zugreift. Hier ist ein typischer Funktions-Prolog-Code, der von GCC generiert wird (g ++, um genau zu sein). Zuerst die C++ - Quelle.
// junk.c++
int addtwo(int a)
{
int x = 2;
return a + x;
}
Dies erzeugt den folgenden Assembler.
.file "junk.c++"
.text
.globl _Z6addtwoi
.type _Z6addtwoi, @function
_Z6addtwoi:
.LFB2:
pushl %ebp
.LCFI0:
movl %esp, %ebp
.LCFI1:
subl $16, %esp
.LCFI2:
movl $2, -4(%ebp)
movl -4(%ebp), %edx
movl 8(%ebp), %eax
addl %edx, %eax
leave
ret
.LFE2:
.size _Z6addtwoi, .-_Z6addtwoi
.ident "GCC: (Ubuntu 4.3.3-5ubuntu4) 4.3.3"
.section .note.GNU-stack,"",@progbits
Um nun diesen Prologcode (alles vor .LCFI2:
) zu erklären, zuerst:
pushl %ebp
speichert den Stack-Frame der aufrufenden - Funktion auf dem Stack.movl %esp, %ebp
nimmt den aktuellen Stapelzeiger und verwendet ihn als Frame für die aufgerufene - Funktion.subl $16, %esp
lässt Platz für lokale Variablen.Jetzt ist Ihre Funktion betriebsbereit. Alle Verweise mit einem negativen Offset vom %ebp%
-Register sind Ihre lokalen Variablen (in diesem Beispiel x
). Alle Referenzen mit einem positiven Offset aus dem %ebp%
-Register sind Ihre übergebenen Parameter.
Der letzte Punkt von Interesse ist die leave
-Anweisung, eine x86-Assembler-Anweisung, die den Stack-Frame der aufrufenden Funktion wiederherstellt. Dies wird normalerweise in der schnelleren Sequenz move %ebp %esp
und pop %ebp%
in C-Code optimiert. Zu Illustrationszwecken habe ich jedoch keinerlei Optimierungen vorgenommen.
Es ist ein typischer Code, den Sie am Anfang einer Funktion sehen.
Es speichert den Inhalt des EBP-Registers im Stapel und speichert dann den Inhalt des aktuellen Stapelzeigers in EBP.
Der Stack wird während eines Funktionsaufrufs zum Speichern lokaler Argumente verwendet. In der Funktion kann sich der Stapelzeiger jedoch ändern, da Werte im Stapel gespeichert werden.
Wenn Sie den ursprünglichen Wert des Stapels speichern, können Sie über das EBP-Register auf die gespeicherten Argumente verweisen, während Sie den Stapel weiterhin verwenden (Werte hinzufügen) können.
Am Ende der Funktion sehen Sie wahrscheinlich den Befehl
pop %ebp ; restore original value
ret ; return
Push %ebp
Dadurch wird das 32-Bit-Basiszeigerregister (erweitert) auf den Stapel verschoben, d. H. Der Stapelzeiger (% esp) wird von vier subtrahiert, dann wird der Wert von% ebp an die Stelle kopiert, auf die der Stapelzeiger zeigt.
movl %esp, %ebp
Dies kopiert das Stapelzeigerregister in das Basiszeigerregister.
Der Zweck des Kopierens des Stapelzeigers auf den Basiszeiger besteht darin, einen Stapelrahmen zu erzeugen, d. H. Einen Bereich auf dem Stapel, in dem eine Unterroutine lokale Daten speichern kann. Der Code in der Subroutine würde den Basiszeiger verwenden, um auf die Daten zu verweisen.
Es ist Teil der sogenannten Funktion Prolog .
Es speichert den aktuellen Basiszeiger, der beim Beenden der Funktion abgerufen wird, und setzt den neuen Ebp-Wert an den Anfang des neuen Frames.
Ich denke auch, dass es wichtig ist zu beachten, dass die Assembly oft nach Push %ebp
und movl %esp, %ebp
Push %ebx
oder Push %edx
hat. Dies sind Anrufersicherungen der Register %ebx
und %edx
. Am Ende des Routineaufrufs werden die Register mit ihren ursprünglichen Werten wiederhergestellt.
Auch - %ebx, %esi, %edi
sind alle sichersten Speicherregister. Wenn Sie sie also überschreiben möchten, müssen Sie sie zuerst speichern und dann wiederherstellen.
Der Code legt den Stapel für Ihr Programm fest.
In x86-Stack werden Informationen von zwei Registern gehalten.
Base pointer (bp): Holds starting address of the stack
Stack pointer (sp): Holds the address in which next value will be stored
Diese Register haben unterschiedliche Namen in verschiedenen Modi:
Base pointer Stack pointer
16-Bit-Realmodus: bp sp
32-Bit-Schutzmodus: ebp (% ebp) esp (% esp)
64-Bit-Modus: rbp rsp
</code>
Wenn Sie einen Stapel einrichten, erhalten der Stapelzeiger und der Basiszeiger am Anfang dieselbe Adresse.
Nun, um Ihren Code zu erklären,
Push %ebp
Dieser Code schiebt die aktuelle Stapeladresse in den Stapel, damit die Funktion ordnungsgemäß "beenden" oder "zurückkehren" kann.
movl %esp, %ebp
Dieser Code legt den Stack für Ihre Funktion fest.
Weitere Informationen finden Sie in dieser Frage .
Ich hoffe das hilft!