contenido
1. El propósito de este documento
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
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. |
sí | registro de índice de fuente |
sé | 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:
- Empujar: poner un elemento en la parte superior de la pila
- 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.