[C Capítulo Final] Creación y Destrucción de Marcos de Pila de Funciones

contenido

1. El propósito de este documento

2. Conocimientos básicos

       1. Registrarse

       2. Caso de código

       3. Descripción general del marco de pila general

       4. Resumen del código de desmontaje requerido

3. Proceso de creación y destrucción de marcos de pila de funciones

       1, función _tmainCRTStartup (llamando a la función principal) creación de marcos de pila

       2. La creación del marco de pila de función principal

       3. Ejecutar código válido (variables) en la función principal

       4. La creación del marco de pila de la función Agregar

       5. Ejecutar código válido en la función Agregar

       6. Destrucción del marco de pila de la función Agregar

       7. Destrucción del marco de la pila de funciones principales

4. Resumen


1. El propósito de este documento

  • 1. ¿Cómo se crean las variables locales?
  • 2. ¿Por qué el valor de la variable local es un valor aleatorio?
  • 3. ¿Cómo pasan parámetros las funciones? ¿Cuál es el orden en que se pasan los parámetros?
  • 4. ¿Cuál es la relación entre los parámetros formales y los parámetros reales?
  • 5. ¿Cómo se realiza la llamada a la función?
  • 6. ¿Cómo regresa la función después del final de la llamada a la función?

Cuando entendemos profundamente la creación y destrucción de marcos de pila de funciones, la respuesta es naturalmente clara. El texto comienza:

2. Conocimientos básicos

1. Registrarse

nombre de registro Introducción
eax "Acumulador" Este es el registro predeterminado para muchas instrucciones de suma y multiplicación.
ebx El registro de "dirección base" almacena la dirección base al direccionar la memoria.
ecx Contador, que es el contador predeterminado para las instrucciones de prefijo de repetición (REP) y las instrucciones de BUCLE.
edx Siempre se usa para poner el resto de la división de enteros.
registro de índice de fuente
registro de índice de destino
reflujo (puntero inferior de la pila) "puntero de dirección base", que almacena la dirección y se utiliza para mantener el marco de la pila de funciones
especialmente (puntero superior de la pila) se usa especialmente como puntero de la pila, que almacena la dirección y se usa para mantener el marco de la pila de funciones

2. Caso de código

  •  El compilador del que depende este artículo: VS2013
#include<stdio.h>
int Add(int x, int y)
{
	int z = 0;
	z = x + y;
	return z;
}
int main()
{
	int a = 10;
	int b = 20;
	int c = 0;
	c = Add(a, b);
	printf("%d\n", c);
	return 0;
}

3. Descripción general del marco de pila general

Cada llamada de función necesita abrir espacio para ella en el área de la pila. Al igual que el código anterior, están la función principal y la función Agregar que son visibles a simple vista. La correspondiente necesidad de abrir espacio para ellas, pero de hecho la función principal también se llama, cuando presionamos F10 , presione nuevamente cuando llegue a 0, y aparecerá la siguiente interfaz

 Se puede ver en la figura que la función principal es llamada por la función __tmainCRTStartup , y __tmainCRTStartup es llamada por mainCRTStartup . Echemos un vistazo al desarrollo general del marco de la pila de funciones:

  • Dos puntos de conocimiento importantes:
  1. Empujar: poner un elemento en la parte superior de la pila
  2. pop: elimina un elemento de la parte superior de la pila

A continuación, se explicará en detalle el desarrollo del marco de pila de funciones: 

4. Resumen del código de desmontaje requerido

int main()
{
00031410  push        ebp  
00031411  mov         ebp,esp  
00031413  sub         esp,0E4h  
00031419  push        ebx  
0003141A  push        esi  
0003141B  push        edi  
0003141C  lea         edi,[ebp+FFFFFF1Ch]  
00031422  mov         ecx,39h  
00031427  mov         eax,0CCCCCCCCh  
0003142C  rep stos    dword ptr es:[edi]  
	int a = 10;
0003142E  mov         dword ptr [ebp-8],0Ah  
	int b = 20;
00031435  mov         dword ptr [ebp-14h],14h  
	int c = 0;
0003143C  mov         dword ptr [ebp-20h],0

    c=Add(a,b);  
00031443  mov         eax,dword ptr [ebp-14h]  
00031446  push        eax  
00031447  mov         ecx,dword ptr [ebp-8]  
0003144A  push        ecx  
0003144B  call        00C210E1  
00031440  add         esp,8  
00031443  mov         dword ptr [ebp-20h],eax  
	printf("%d", c);
00241456  mov         esi,esp  
00241458  mov         eax,dword ptr [ebp-20h]  
0024145B  push        eax  
0024145C  push        245858h  
00241461  call        dword ptr ds:[00249114h]  
00241467  add         esp,8  
0024146A  cmp         esi,esp  
0024146C  call        0024113B  
	return 0;
00241471  xor         eax,eax  
}
00031451  pop         edi  
00031452  pop         esi  
00031453  pop         ebx  
00031454  add         esp,0E4h  
0003145A  cmp         ebp,esp  
0003145C  call        __RTC_CheckEsp (03113Bh)  
00031461  mov         esp,ebp  
00031463  pop         ebp  
00031464  ret  
int Add(int x, int y)
{
000313C0  push        ebp  
000313C1  mov         ebp,esp  
000313C3  sub         esp,0CCh  
000313C9  push        ebx  
000313CA  push        esi  
000313CB  push        edi  
000313CC  lea         edi,[ebp-0CCh]  
000313D2  mov         ecx,33h  
000313D7  mov         eax,0CCCCCCCCh  
000313DC  rep stos    dword ptr es:[edi]  
	int z = 0;
000313DE  mov         dword ptr [ebp-8],0  
	z = x + y;
000313E5  mov         eax,dword ptr [ebp+8]  
000313E8  add         eax,dword ptr [ebp+0ch]  
000313EB  mov         dword ptr [ebp-8],eax  
	return z;
000313EE  mov         eax,dword ptr [ebp-8]  
}
000313F1  pop         edi  
000313F2  pop         esi  
000313F3  pop         ebx  
000313F4  mov         esp,ebp  
000313F6  pop         ebp  
000313F7  ret  

3. Proceso de creación y destrucción de marcos de pila de funciones

1, función _tmainCRTStartup (llamando a la función principal) creación de marcos de pila

De acuerdo con lo anterior, ya sabemos que la función principal es llamada por la función _tmainCRTStartup. Naturalmente, necesitamos abrir un marco de pila para ello. Este espacio debe ser mantenido por los registros ebp y sep, siempre que la dirección inferior sea alta y la dirección superior es baja. Como se muestra en la figura:

 En este punto, ingresas a la función principal. Primero, necesitas empujar para empujar la pila:

 Push ebp es empujar ebp a la parte superior de la pila. En este momento, sep se mueve a la parte superior de la nueva pila en consecuencia, lo que se puede verificar monitoreando:

 El diagrama es el siguiente:

 A continuación, realice la operación mov:

 Esta línea de código significa asignar sep a ebp, por lo que la posición a la que apunta ebp es la posición a la que apunta sep, pero la posición de la dirección de la operación de origen permanece sin cambios, lo que se puede verificar mediante el monitoreo.

 Luego realice la suboperación:

La operación es restarle 0E4h a la esp. En este momento la posición de la esp subirá. Observe monitoreando:

 En este momento, después de que se ejecuta la suboperación, en realidad ha entrado en el desarrollo del marco de pila de función principal a continuación.Hasta ahora, se ha completado el desarrollo del marco de pila de función _tmainCRTStartup. Vea el diagrama a continuación:

2. La creación del marco de pila de función principal

Continuando con lo anterior, el diagrama es el siguiente:

A continuación, realice tres operaciones de inserción: introduzca ebx, sei y edi en la pila en secuencia, y la esp correspondiente también debería subir.

Echa un vistazo a:

El diagrama es el siguiente:

Luego realice los siguientes tres pasos

La operación lea (cargar dirección efectiva) carga la dirección efectiva. Es equivalente a poner [ebp+FFFFFF1Ch] en edi.Después de mostrar el nombre del símbolo, [ebp+FFFFFF1Ch] es [ebp-0E4h].El -0E4h se ha ejecutado antes, y se ejecuta de nuevo y se pone en edi. Luego, mov pone 39h en ecx, y luego mov pone 0CCCCCCCCh en eax. 

 

El propósito de la operación anterior es cambiar todos los dwords (1 palabra2 bytes, 2dpalabra4 bytes) 39h veces desde el edi justo ahora a 0CCCCCCCCch 

Echa un vistazo a:

 El diagrama es el siguiente:

En este punto, se ha completado el desarrollo del marco de la pila principal y el siguiente paso es ejecutar el código oficialmente válido, ver a continuación:

3. Ejecutar código válido (variables) en la función principal

A continuación, haga lo siguiente:

 Primero mov coloque 0Ah (10) en la posición de ebp-8, de manera similar coloque 14h (20) en ebp-14h y coloque 0 en ebp-20h, como se muestra en la figura:

 En este momento, se han creado las tres variables de a, b y c, y luego se llama a la función Agregar: primero pase los parámetros

Primero, mov pone ebp-14h (b=20) en eax. A continuación, empuje, empuje la pila para poner eax (20) en la parte superior de la pila y mueva la esp correspondiente. la pila. como muestra la imagen:

 Luego ejecute la operación de llamada, llame a la función Agregar, presione F10 para ejecutar la llamada, presione F11, luego salte al interior de la función Agregar y empuje la dirección de la siguiente instrucción de la instrucción de llamada a la parte superior de la pila. El propósito de esto es volver fácilmente a la dirección al saltar a la función Agregar para volver, como se muestra en la figura:

Presiona F11 para ingresar oficialmente a la función Agregar y crear un marco de pila para ella. Consulta los detalles a continuación:

4. La creación del marco de pila de la función Agregar

int Add(int x, int y)
{
000313C0  push        ebp  
000313C1  mov         ebp,esp  
000313C3  sub         esp,0CCh  
000313C9  push        ebx  
000313CA  push        esi  
000313CB  push        edi  
000313CC  lea         edi,[ebp-0CCh]  
000313D2  mov         ecx,33h  
000313D7  mov         eax,0CCCCCCCCh  
000313DC  rep stos    dword ptr es:[edi]  

Las operaciones anteriores son las mismas que las operaciones internas de la función principal anterior, que en realidad están preparando nuestro marco de pila para la función Agregar.

Primero, push ebp empuja ebp a la parte superior de la pila, luego mov asigna esp a ebp, luego sub, y esp- va a 0CCh. Este paso es para abrir espacio para la función Agregar, y luego presione tres veces, lo mismo como la función principal. Todavía se inicializa a CCCCCCCC, y el proceso detallado no se repetirá. Es lo mismo que la función principal anterior, como se muestra en la figura:

 En este punto, el desarrollo del marco Add stack se ha completado básicamente, y el siguiente paso es ejecutar el código oficial y válido, ver a continuación:

5. Ejecutar código válido en la función Agregar

Continúa desde arriba:

	int z = 0;
000313DE  mov         dword ptr [ebp-8],0  
	z = x + y;
000313E5  mov         eax,dword ptr [ebp+8]  
000313E8  add         eax,dword ptr [ebp+0ch]  
000313EB  mov         dword ptr [ebp-8],eax  
	return z;
000313EE  mov         eax,dword ptr [ebp-8]  
}

Primero, ponga 0 en la posición de ebp-8, luego mov pone el valor de ebp+8 en eax, y eax es 10 en este momento. Luego agregue ebp+0ch a eax, es decir, agregue 20, en este momento eax es 30, luego de agregar eax (30) a ebp-8, el resultado final (30) a z.

En este punto, se completa la ejecución del código válido dentro de la función Agregar, como se muestra en la figura:

El siguiente paso es volver, es decir, la destrucción del marco de la pila de la función Agregar, ver a continuación: 

6. Destrucción del marco de pila de la función Agregar

	return z;
000313EE  mov         eax,dword ptr [ebp-8]  
}
000313F1  pop         edi  
000313F2  pop         esi  
000313F3  pop         ebx  
000313F4  mov         esp,ebp  
000313F6  pop         ebp  
000313F7  ret 

Lo anterior ya sabe que el valor de ebp-8 (30) se ha puesto en eax en este momento, y luego se ejecutan tres pops.Una vez poped, se agregará esp una vez, como se muestra en la figura:

 Luego, asigne ebp a esp, y luego pop a pop ebp. En este momento, esp también se mueve. En este momento, esp y ebp regresan al mantenimiento anterior del marco de la pila de funciones principal. como muestra la imagen:

 En este punto, el esp apunta a la dirección de la siguiente instrucción de la instrucción de llamada, presione F10 nuevamente y el desensamblaje se verá así:

0003144B  call        00C210E1  
00031440  add         esp,8  
00031443  mov         dword ptr [ebp-20h],eax  
	printf("%d", c);
00241456  mov         esi,esp  
00241458  mov         eax,dword ptr [ebp-20h]  
0024145B  push        eax  
0024145C  push        245858h  
00241461  call        dword ptr ds:[00249114h]  
00241467  add         esp,8  
0024146A  cmp         esi,esp  
0024146C  call        0024113B  
	return 0;
00241471  xor         eax,eax  
}

En este punto, entenderemos que la dirección de la siguiente instrucción que previamente almacenó la instrucción call es por conveniencia de retorno, y la posición del esp cambia después de la ejecución anterior de ret:

En este momento, el marco de la pila de la función Add se destruye realmente y luego se destruye el marco de la pila de la función principal.

7. Destrucción del marco de la pila de funciones principales

0003144B  call        00C210E1  
00031440  add         esp,8  
00031443  mov         dword ptr [ebp-20h],eax  
	printf("%d", c);
00241456  mov         esi,esp  
00241458  mov         eax,dword ptr [ebp-20h]  
0024145B  push        eax  
0024145C  push        245858h  
00241461  call        dword ptr ds:[00249114h]  
00241467  add         esp,8  
0024146A  cmp         esi,esp  
0024146C  call        0024113B  
	return 0;
00241471  xor         eax,eax  
}

A través del código de desensamblado, sabemos que la operación de adición suma 8 a la esp en este momento, y los dos parámetros formales x e y se liberan en este momento, apuntando a la posición como se muestra en la figura:

A continuación, mov pone eax en ebp-20h, y eax es la suma calculada cuando ejecutamos la función Add. En este momento, la suma es recuperada por nosotros. Lo siguiente es la destrucción del marco de pila de la función principal, que es lo mismo como la función Agregar anterior La destrucción del marco de la pila no es muy diferente, por lo que no entraré en detalles aquí.

El código de desmontaje es el siguiente:

00241471  xor         eax,eax  
}
00031451  pop         edi  
00031452  pop         esi  
00031453  pop         ebx  
00031454  add         esp,0E4h  
0003145A  cmp         ebp,esp  
0003145C  call        __RTC_CheckEsp (03113Bh)  
00031461  mov         esp,ebp  
00031463  pop         ebp  
00031464  ret  

4. Resumen

En este punto, la creación y destrucción del marco de la pila de funciones ha terminado oficialmente, y las preguntas (objetivos) al principio de este artículo también se pueden entender claramente:

como sigue:

  • 1. ¿Cómo se crean las variables locales?

Primero, asigne e inicialice el espacio del marco de la pila para la función y luego asigne un poco de espacio para las variables locales en el marco de la pila.

  • 2. ¿Por qué el valor de la variable local es un valor aleatorio?

Debido a que el valor aleatorio se ingresa cuando abrimos el marco de la pila, y cuando inicializamos, el valor aleatorio se sobrescribe.

  • 3. ¿Cómo pasan parámetros las funciones? ¿Cuál es el orden en que se pasan los parámetros?

Antes de llamar a la función, ya presioné y presioné estos dos parámetros en la pila de derecha a izquierda. Cuando ingresamos a la función de parámetro formal, podemos encontrarla en el marco de la pila de la función Agregar a través del desplazamiento de el puntero parámetros formales.

  • 4. ¿Cuál es la relación entre los parámetros formales y los parámetros reales?

El parámetro formal es, de hecho, el espacio que se abre cuando se empuja la pila. El parámetro formal y el parámetro real son solo iguales en valor e independientes en el espacio. El parámetro formal es una copia temporal del parámetro real, y cambiar el parámetro formal no afectará el parámetro real.

  • 5. ¿Cómo regresa la función después del final de la llamada a la función?

Ya hemos insertado la dirección de la siguiente instrucción de la instrucción de llamada antes de la llamada. Cuando la función regresa después de la llamada de función, saltará a la dirección de la siguiente instrucción de la instrucción de llamada, y se traerá el valor de retorno. de vuelta a través del registro.

Supongo que te gusta

Origin blog.csdn.net/bit_zyx/article/details/123321243
Recomendado
Clasificación