Qu'est-ce que le pointeur de base et le pointeur de pile? De quoi ont-ils point?

utilisant cet exemple provenant de wikipedia, dans lequel DrawSquare () appelle DrawLine (), " 15198090920"

alt text

(notez que ce diagramme a des adresses hautes en bas et des adresses basses en haut.)

est-ce que quelqu'un pourrait m'expliquer ce que ebp et esp sont dans ce contexte?

D'après ce que je vois, je dirais que le pointeur de pile pointe toujours vers le haut de la pile, et le pointeur de la base au début de la fonction actuelle? Ou quoi?


edit: je veux dire dans le contexte de programmes de windows

edit2: et comment eip fonctionne, aussi?

Edit 3: J'ai le code suivant de MSVC++:

var_C= dword ptr -0Ch
var_8= dword ptr -8
var_4= dword ptr -4
hInstance= dword ptr  8
hPrevInstance= dword ptr  0Ch
lpCmdLine= dword ptr  10h
nShowCmd= dword ptr  14h

tous semblent être des dwords, prenant donc 4 octets chacun. Donc je peux voir qu'il y a un écart entre l'antériorité et var_4 de 4 octets. Quels sont-ils? Je suppose que c/" class="blnk">c'est l'adresse de retour, comme on peut le voir sur la photo de wikipedia?


(note de L'éditeur: supprimé une longue citation de la réponse de Michael, qui n'appartient pas à la question, mais une question de suivi a été édité en):

c'est parce que le flux de l'appel de fonction est:

* Push parameters (hInstance, etc.)
* Call function, which pushes return address
* Push ebp
* Allocate space for locals

ma question (dernière, j'ai de l'espoir!) maintenant est, ce qui est exactement ce qui se passe à partir de l'instant je pop les arguments de la fonction que je veux appeler jusqu'à la fin du prolog? Je veux savoir comment l'ebp, esp évoluent pendant ces moments (j'ai déjà compris comment fonctionne prolog, je veux juste savoir ce qui se passe après avoir poussé les arguments sur la pile et avant le prolog).

193
demandé sur Community 2009-09-08 22:37:01

8 réponses

esp est comme vous le dites, le haut de la pile.

ebp est habituellement défini à esp au début de la fonction. Les paramètres de fonction et les variables locales sont accessibles en ajoutant et en soustrayant, respectivement, un décalage constant de ebp . Toutes les conventions d'appel x86 définissent ebp comme étant préservée à travers les appels de fonction. ebp lui-même pointe en fait sur le pointeur de base du cadre précédent, ce qui permet à stack marcher dans un débogueur et regarder d'autres cadres variables locales pour fonctionner.

la plupart des prologs de fonction ressemblent à quelque chose comme:

push ebp      ; Preserve current frame pointer
mov ebp, esp  ; Create new frame pointer pointing to current stack top
sub esp, 20   ; allocate 20 bytes worth of locals on stack.

puis plus tard dans la fonction vous pouvez avoir le code comme (présumant que les deux variables locales sont 4 octets)

mov [ebp-4], eax    ; Store eax in first local
mov ebx, [ebp - 8]  ; Load ebx from second local

FPO ou omission de pointeur de trame l'optimisation que vous pouvez activer va en fait éliminer cela et utiliser ebp comme un autre registre et accédez aux sections locales directement à partir de esp , mais cela rend le débogage un peu plus difficile car le débogueur ne peut plus accéder directement aux cadres de la pile des appels de fonction précédents.

EDIT:

pour votre question mise à jour, les deux entrées manquantes dans la pile sont:

var_C = dword ptr -0Ch
var_8 = dword ptr -8
var_4 = dword ptr -4
*savedFramePointer = dword ptr 0*
*return address = dword ptr 4*
hInstance = dword ptr  8h
PrevInstance = dword ptr  0C
hlpCmdLine = dword ptr  10h
nShowCmd = dword ptr  14h

c'est parce que le flux de l'appel de fonction est:

  • Push paramètres ( hInstance , etc.)
  • fonction D'appel, qui pousse l'adresse de retour
  • Push ebp
  • allouer de l'espace pour les locaux
203
répondu Michael 2017-08-10 05:58:22

ESP est le pointeur de pile courant, qui changera chaque fois qu'un mot ou une adresse est poussé ou poppé sur/hors de la pile. EBP est un moyen plus pratique pour le compilateur de garder la trace des paramètres d'une fonction et des variables locales que D'utiliser L'ESP directement.

généralement (et cela peut varier d'un compilateur à l'autre), tous les arguments à une fonction appelée sont poussés sur la pile (habituellement dans l'ordre inverse qu'ils sont déclarés dans la fonction prototype, mais cela varie). Alors la fonction est appelée, ce qui pousse l'adresse de retour (EIP) sur la pile.

à l'entrée de la fonction, l'ancienne valeur EBP est poussée sur la pile et EBP est réglée à la valeur de ESP. Puis L'ESP est décrémenté (parce que la pile pousse vers le bas dans la mémoire) pour allouer l'espace pour les variables locales et les temporairesde la fonction. À partir de ce moment, pendant l'exécution de la fonction, les arguments de la fonction sont situés sur stack at positive offsets from EBP (parce qu'ils ont été poussés avant l'appel de fonction), et les variables locales sont localisées à des offsets négatifs de EBP (parce qu'ils ont été alloués sur la pile après l'entrée de fonction). C'est pourquoi L'EBP est appelé le pointeur de cadre , parce qu'il pointe vers le centre du cadre d'appel de fonction .

à la sortie, tout ce que la fonction doit faire est de mettre ESP à la valeur de EBP (qui désallace les variables locales de la pile, et expose L'EBP entrée sur le dessus de la pile), puis pop L'ancienne valeur EBP de la pile, et ensuite la fonction retourne (popping l'adresse de retour dans EIP).

67
répondu David R Tribble 2016-11-28 22:33:19

vous avez raison. Le pointeur de pile pointe vers l'élément supérieur de la pile et le pointeur de base pointe vers le "précédent" haut de la pile avant que la fonction a été appelée.

Lorsque vous appelez une fonction, toute variable locale sera stockée sur la pile et le pointeur de pile sera incrémenté. Lorsque vous revenez de la fonction, Toutes les variables locales de la pile sortent de la portée. Vous faites cela en plaçant le pointeur de pile de nouveau à la base pointeur (qui était le "précédent" avant l'appel de fonction).

faire l'allocation de mémoire de cette façon est très , très rapide et efficace.

12
répondu Robert Cartaino 2009-09-08 18:48:39

EDIT: Pour une meilleure description, voir la section x86/Démontage des Fonctions et une Pile d'Images dans un WikiBook sur x86 assemblée. J'essaie d'ajouter quelques informations que vous pourriez être intéressé à utiliser Visual Studio.

stockant L'EBP de l'appelant comme première variable locale est appelé un cadre de pile standard, et cela peut être utilisé pour presque toutes les conventions d'appel sur Windows. Il existe des différences selon que l'appelant ou l'appelant désalloue la paramètres passés, et quels paramètres sont passés dans les registres, mais ceux-ci sont orthogonaux au problème Standard stack frame.

en parlant des programmes Windows, vous pourriez probablement utiliser Visual Studio pour compiler votre code C++. Soyez conscient que Microsoft utilise une optimisation appelée Omission de pointeur de trame, qui rend presque impossible de faire marcher la pile sans utiliser la bibliothèque dbghlp et le fichier PDB pour l'exécutable.

Ce Pointeur De Cadre Omission signifie que le compilateur ne stocke pas l'ancien EBP sur un endroit standard et utilise le registre EBP pour autre chose, donc vous avez du mal à trouver l'EIP appelant sans savoir combien d'espace les variables locales ont besoin pour une fonction donnée. Bien sûr Microsoft fournit une API qui vous permet de faire des cheminements même dans ce cas, mais chercher la base de données de table de symboles dans les fichiers PDB prend trop de temps pour certains cas d'utilisation.

pour éviter FPO dans votre compilation unités, vous devez éviter d'utiliser /O2 ou besoin d'ajouter explicitement /Oy-aux drapeaux de compilation C++ dans vos projets. Vous avez probablement un lien avec L'exécution C ou C++, qui utilise FPO dans la configuration de sortie, Donc vous aurez du mal à faire des cheminements sans le dbghlp.DLL.

6
répondu wigy 2009-09-09 09:09:02

tout d'abord, le pointeur de la pile pointe vers le bas de la pile car les piles x86 construisent à partir de valeurs d'adresse élevées vers des valeurs d'adresse plus basses. Le pointeur de pile est le point où le prochain appel à push (ou à call) placera la valeur suivante. Son fonctionnement est équivalent à L'instruction c / c++:

 // push eax
 --*esp = eax
 // pop eax
 eax = *esp++;

 // a function call, in this case, the caller must clean up the function parameters
 move eax,some value
 push eax
 call some address  // this pushes the next value of the instruction pointer onto the
                    // stack and changes the instruction pointer to "some address"
 add esp,4 // remove eax from the stack

 // a function
 push ebp // save the old stack frame
 move ebp, esp
 ... // do stuff
 pop ebp  // restore the old stack frame
 ret

le pointeur de base est en haut du cadre actuel. ebp indique généralement votre adresse de retour. ebp+4 points au premier paramètre de votre fonction (ou le cette valeur d'une méthode de classe). ebp-4 points à la première variable locale de votre fonction, habituellement l'ancienne valeur de ebp de sorte que vous pouvez restaurer le pointeur de cadre précédent.

5
répondu jmucchiello 2009-09-08 18:59:05

longtemps depuis que j'ai fait la programmation D'assemblage, mais ce lien pourrait être utile...

Le processeur dispose d'une collection de registres qui sont utilisés pour stocker des données. Certaines de ces valeurs sont directes alors que d'autres pointent vers une zone à L'intérieur de la mémoire vive. Les registres ont tendance à être utilisé pour certaines actions spécifiques et chaque opérande dans l'assemblée besoin d'une certaine quantité de données dans certains registres.

le pointeur de pile est surtout utilisé lorsque vous appelez d'autres procédures. Avec les compilateurs modernes, un tas de données sera jeté en premier sur la pile, suivi de l'adresse de retour de sorte que le système saura où retourner une fois qu'on lui aura dit de revenir. Le pointeur de pile pointera à l'endroit suivant où les nouvelles données peuvent être poussées à la pile, où elle restera jusqu'à ce qu'elle soit réapparue.

les registres de Base ou les registres de segments indiquent simplement l'espace d'adresse d'une grande quantité de données. Combiné avec un second registre, le pointeur de Base divisera la mémoire en blocs énormes tandis que le second registre pointera vers un élément dans ce bloc. Les pointeurs de Base pointent vers la base des blocs de données.

n'oubliez pas que L'assemblage est très spécifique au CPU. La page à laquelle j'ai fait référence fournit des informations sur les différents types de CPU.

1
répondu Wim ten Brink 2009-09-08 18:50:18

Edit Oui, c'est plutôt mal. Il décrit quelque chose d'entièrement différent dans le cas où n'importe qui est intéressé :)

Oui, le pointeur de pile pointe vers le haut de la pile (que ce soit le premier emplacement de pile vide ou le dernier emplacement complet dont je ne suis pas sûr). Le pointeur de base indique l'emplacement de mémoire de l'instruction qui est exécutée. C'est au niveau des opcodes - l'instruction la plus élémentaire que vous pouvez obtenir sur un ordinateur. Chacun opcode et ses paramètres sont stockés en mémoire. Une ligne C ou C++ ou C# pourrait être traduite en un opcode, ou une séquence de deux ou plus selon sa complexité. Ceux-ci sont écrits en mémoire de programme séquentiellement et exécutés. Dans des circonstances normales, le pointeur de base est incrémenté d'une instruction. Pour le contrôle de programme (GOTO, IF, etc) il peut être incrémenté plusieurs fois ou simplement remplacé par l'adresse mémoire suivante.

dans ce contexte, le les fonctions sont stockées dans la mémoire programme à une adresse donnée. Lorsque la fonction est appelée, certaines informations sont poussées sur la pile qui permet au programme de retrouver son was à l'endroit où la fonction a été appelée ainsi que les paramètres de la fonction, puis l'adresse de la fonction dans la mémoire du programme est poussée dans le pointeur de base. Lors du prochain cycle d'horloge, l'ordinateur commence à exécuter les instructions à partir de cette adresse mémoire. Puis, à un moment donné, il reviendra à l'endroit de la mémoire après l'instruction qui a appelé la fonction et continuer à partir de là.

-3
répondu Stephen Friederichs 2009-09-08 19:11:23

esp signifie "pointeur de pile étendu".....ebp pour "Something Base Pointer"....et eip pour "Something Instruction Pointer"...... Le pointeur de pile pointe vers l'adresse offset du segment de pile. Le pointeur de Base pointe vers l'adresse offset du segment extra. Le pointeur D'Instruction pointe vers l'adresse offset du segment de code. Maintenant, à propos des segments...ce sont de petites divisions 64KB de la zone mémoire des processeurs.....Ce processus est connu sous le nom de Segmentation de la mémoire. J'espère ce post vous a été utile.

-3
répondu Adarsha Kharel 2011-09-14 18:54:40