proceso de llamada de función
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:
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
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().
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,
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
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
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;