wake-up-neo.com

Was ist die Verwendung von "Push% ebp; movl% esp,% ebp", das von GCC für x86 generiert wird?

Welche Auswirkung haben diese beiden Anweisungen auf den von gcc für x86-Computer generierten Assemblycode?

Push %ebp
movl %esp, %ebp
34
mahesh

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:

  1. pushl %ebp speichert den Stack-Frame der aufrufenden - Funktion auf dem Stack.
  2. movl %esp, %ebp nimmt den aktuellen Stapelzeiger und verwendet ihn als Frame für die aufgerufene - Funktion.
  3. 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 
9
Roalt
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.

6
Guffa

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.

2
mgv

Ich denke auch, dass es wichtig ist zu beachten, dass die Assembly oft nach Push %ebp und movl %esp, %ebpPush %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.

1
Alex Spencer

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!

0