Proceso de llamada de funciones (creación y destrucción de marcos de pila)

Cuando se llama a una función en lenguaje C, saltará para ejecutar la función hasta que se complete y luego ejecutará la siguiente instrucción.
Esto se logra formando un marco de pila durante la ejecución de la función de llamada. Un marco de pila es una estructura de datos utilizada por el compilador para implementar el proceso de llamada de función.

Tome la función Add() como ejemplo para analizar el proceso de llamada:

#include<stdio.h>
#include<stdlib.h>
int Add(int x, int y){
    
    
    int sum = 0;
    sum = x + y;
    return sum;
}
int main(){
    
    
    int a = 10;
    int b = 20;
    int ret = 0;
    ret = Add(a, b);
    return 0;
}
  • El marco de la pila requiere dos registros, ebp y esp. Durante la llamada a la función, estos dos registros almacenan los punteros inferior y superior de la pila que mantienen la pila.
    Nota: ebp apunta a la parte inferior del marco de la pila actualmente ubicado en la parte superior de la pila del sistema , no a la parte inferior de la pila del sistema. . Estrictamente hablando, "parte inferior del marco de la pila" y "parte inferior de la pila" son conceptos diferentes: la parte superior del marco de la pila al que se refiere ESP y la parte superior de la pila del sistema están en la misma posición .

esp: registro de puntero de pila (puntero de pila extendido), que almacena un puntero que siempre apunta a la parte superior del marco de la pila en la parte superior de la pila del sistema; en una plataforma de 32 bits, ESP disminuye en 4 bytes cada vez que ebp: base
dirección El registro de puntero (puntero base extendido) almacena un puntero, que siempre apunta a la parte inferior del marco de pila superior de la pila del sistema.
eax es el "acumulador", que es el registro predeterminado para muchas instrucciones de suma y multiplicación.
ebx es El registro de "dirección base" (base) almacena la dirección base cuando se direcciona la memoria.
ecx es un contador, que es el contador predeterminado de la instrucción de prefijo de repetición (REP) y la instrucción LOOP.
edx siempre se usa para almacenar el resto generado por división entera.
esi/edi se denominan "índice de origen/destino" respectivamente, porque en muchas instrucciones de operación de cadenas, DS:ESI apunta a la cadena de origen y ES:EDI apunta a la cadena de destino. Instrucción de ensamblaje: mov: instrucción de transferencia de
datos
, es también la instrucción de programación más básica, utilizada para transferir datos desde la dirección de origen a la dirección de destino (la transferencia de datos entre registros es esencialmente la misma) sub: instrucción de resta lea: tomar la dirección de desplazamiento push: la instrucción
para
implementar
la operación push Es la instrucción PUSH
pop: la instrucción que implementa la operación pop
llamada: se utiliza para guardar la siguiente instrucción de la instrucción actual y saltar a la función de destino

Distribución del espacio de direcciones de memoria:
Insertar descripción de la imagen aquí
el espacio de la pila crece hacia direcciones más bajas y se utiliza principalmente para guardar marcos de pila de funciones. El tamaño del espacio de la pila es muy limitado, solo unos pocos MB (por lo que la dirección alta en la imagen a continuación está en la parte inferior)

Implementación del código ensamblador:

int main ()
{
    
    
011B26E0  push        ebp  
011B26E1  mov         ebp,esp 
011B26E3  sub         esp,0E4h 
011B26E9  push        ebx  
011B26EA  push        esi  
011B26EB  push        edi  
011B26EC  lea         edi,[ebp-0E4h] 
011B26F2  mov         ecx,39h 
011B26F7  mov         eax,0CCCCCCCCh 
011B26FC  rep stos    dword ptr es:[edi] 
    int a = 10;
011B26FE  mov         dword ptr [a],0Ah 
    int b = 20;
011B2705  mov         dword ptr [b],0Ch 
    int ret = 0;
011B270C  mov         dword ptr [ret],0 
    ret = Add(a,b);
011B2713  mov         eax,dword ptr [b] 
011B2716  push        eax  
011B2717  mov         ecx,dword ptr [a] 
011B271A  push        ecx  
011B271B  call        @ILT+640(_Add) (11B1285h) 
011B2720  add         esp,8 
011B2723  mov         dword ptr [ret],eax 
    return 0;
011B2726  xor         eax,eax 
}
011B2728  pop         edi  
011B2729  pop         esi  
011B272A  pop         ebx  
011B272B  add         esp,0E4h 
011B2731  cmp         ebp,esp 
011B2733  call        @ILT+450(__RTC_CheckEsp) (11B11C7h) 
011B2738  mov         esp,ebp 
011B273A  pop         ebp  
011B273B  ret              
int Add(int x,int y)
{
    
    
011B26A0  push        ebp  
011B26A1  mov         ebp,esp 
011B26A3  sub         esp,0CCh 
011B26A9  push        ebx  
011B26AA  push        esi  
011B26AB  push        edi  
011B26AC  lea         edi,[ebp-0CCh] 
011B26B2  mov         ecx,33h 
011B26B7  mov         eax,0CCCCCCCCh 
011B26BC  rep stos    dword ptr es:[edi] 
    int sum = 0;
011B26BE  mov         dword ptr [sum],0 
    sum = x + y;
011B26C5  mov         eax,dword ptr [x] 
011B26C8  add         eax,dword ptr [y] 
011B26CB  mov         dword ptr [sum],eax 
    return sum;
011B26CE  mov         eax,dword ptr [sum] 
}
011B26D1  pop         edi  
011B26D2  pop         esi  
011B26D3  pop         ebx  
011B26D4  mov         esp,ebp 
011B26D6  pop         ebp  
011B26D7  ret              

1. Llame a la función principal():

1. empuje la pila, coloque ebp en la parte superior de la pila y esp siempre apunta a la parte superior de la pila
2. mov , pase el valor de esp a ebp, es decir, mueva esp y ebp juntos
3. sub (es decir para disminuir), es decir, esp -0E4h se asigna a esp, y la asignación de llamada de función aumenta de dirección alta a dirección baja , por lo que esp se mueve hacia arriba, lo que abre un nuevo espacio, es decir, abre espacio para el principal función
4. Los siguientes tres empujes serán ebx, esi y edi respectivamente. Empuje la parte superior de la pila en orden, y esp también apuntará a la parte superior de la pila
5.lea la instrucción para cargar la dirección efectiva; coloque la dirección de ebp-0E4h en edi, es decir, edi apunta a ebp-0E4h (1. Coloque 39h en ecx; 2. Coloque 0ccccccccch en eax; 3. Comience a copiar desde la dirección señalada por edi a la dirección alta. El número de copias es el contenido ecx y el contenido copiado está dentro de eax) 6. Cree las variables a y b e inicialice
10 y 20
Insertar descripción de la imagen aquí

2. Llamada de la función Add()

Coloque b en eax y luego presione eax en la pila (parámetro formal a).
Coloque a en eax y luego presione eax en la pila (parámetro formal b).
Llamada: inserte la dirección de la siguiente instrucción en la pila y luego ingrese la función add().
Insertar descripción de la imagen aquí

Nota: La declaración de llamada envía la dirección de la siguiente instrucción. Para saber dónde continuar la ejecución cuando la función regrese,
ingrese la función add():

A. Primero inserte la función principal ebp en la pila y guarde la dirección del ebp que apunta a la parte inferior del marco de la pila de la función principal (). El propósito es encontrar la parte inferior de la pila de funciones principal al regresar. En este momento, esp apunta a la nueva parte superior de la pila y cambia el ebp de la función principal
. Empujar la pila también es encontrar la parte inferior de la pila de funciones principales al regresar.
B. Asigne el valor de esp a ebp para generar un nuevo ebp, es decir, el ebp del marco de pila de la función Add();
C. Restar un número hexadecimal 0CCh de esp (espacio abierto previamente para la función Add());
D. push ebx, esi, edi;
E. instrucción lea, cargar la dirección efectiva,
F. Inicializar el espacio previamente abierto a 0xcccccccc,
Insertar descripción de la imagen aquí
G. Crear la variable z y asignarle un valor
H. Poner Poner el parámetro formal a en eax, es decir, poner 10 en eax, agregar el parámetro formal b en eax, es decir, sumar 20 a eax, y luego poner eax en la posición de z, es decir, poner la suma de los dos números en z I. Poner el valor de z
en el registro eax y regresar. Porque z es un espacio variable abierto temporalmente por la función y se destruirá después de que se ejecute la función, así que colóquelo en el registro y devuelva K. Luego, realice la
operación pop desde la pila y edi esi ebx en secuencia de arriba a abajo Al hacer estallar la pila, esp se moverá hacia abajo. Las características de la pila son: primero en entrar, último en salir, último en entrar, primero en salir
L. Asigne el valor de ebp a esp, es decir, esp se mueve hacia abajo para señalar la posición de ebp. En este momento, el espacio de la pila abierto por add ha sido destruido
M .pop coloca el elemento en la parte superior de la pila en ebp, es decir, coloca el ebp de la función principal en ebp, es decir, ebp ahora apunta a la función principal ebp
Insertar descripción de la imagen aquí
N. Después de ejecutar ret, la dirección de la inserción anterior aparecerá, por lo que volverá a la función principal, razón por la cual esta dirección se presionó antes, para que se complete la instrucción de llamada. A continuación, continúe ejecutando O desde allí
. llamar a la instrucción
Insertar descripción de la imagen aquí
Mueva esp + 8, es decir, mueva esp hacia abajo y destruya los parámetros formales.

P. Finalmente, el marco de pila de la función main() se destruye, el método es el mismo que el anterior

Resumen: La pila es un espacio necesario para registrar la ruta de llamada y los parámetros cuando se ejecuta un programa en lenguaje C:
marco de llamada de función,
paso de parámetros,
guardar dirección de retorno,
proporcionar espacio de variable local;

Supongo que te gusta

Origin blog.csdn.net/weixin_44280688/article/details/105468514
Recomendado
Clasificación