¿Por qué se necesita un marco de pila o una pila de sombreado en un procedimiento?
Eres un traductor de español que traduce documentos de TI. Traduce lo siguiente al español. No traduzcas el código y la salida en formato de markdown.
Disculpa por mi mal inglés, porque soy de Corea del Sur.
Intenté este código
lea rcx, QWORD PTR [message]
call [print_message] ; cubre la dirección de retorno con una dirección errónea
xor rcx, rcx
ret
y se estrelló… después de eso, intenté otra forma
`sub rsp, 8 ;shadow stack
lea rcx, QWORD PTR [message]
call [print_message]
add rsp, 8
ret`
“`
; marco de la pila
push rbp
mov rbp, rsp
lea rcx, QWORD PTR [message]
call [print_message]
mov rsp, rbp
pop rbp
ret
Estos 2 códigos funcionan, pero el problema es … ¿por qué el procedimiento necesita estas cosas? Esto me hace curioso.
Código real donde se produjo el problema
extern <em>_imp</em>MessageBoxA: QWORD
.data
message db "1234", 0
.code
entry_point proc
sub rsp, 8
xor ecx, ecx
lea rdx, QWORD PTR [message]
lea r8, QWORD PTR [message]
mov r9, 0
call [<em>_imp</em>MessageBoxA] ;stdcall
add rsp, 8
ret
entry_point endp
end
davy.ai
El problema con el código original es que sobrescribió la dirección de retorno en la pila con la dirección de la función “print_message”, lo que causó que el programa se bloqueara cuando intentó retornar de esa función.
Para solucionar este problema, el segundo ejemplo de código asigna espacio en la pila para una llamada “pila sombra” y almacena la dirección de retorno original ahí antes de realizar la llamada a la función. Esto asegura que la dirección de retorno no se sobrescriba y se pueda recuperar de manera segura cuando la llamada a la función retorna.
El tercer ejemplo sigue la configuración típica del marco de pila donde el puntero base actual (rbp) se empuja a la pila y luego se establece en el puntero de pila actual (rsp) antes de asignar espacio para variables locales. En este caso, no hay variables locales, por lo que el código simplemente procede a llamar a la función MessageBoxA con los argumentos necesarios. Finalmente, el marco de pila se restablece al valor guardado de rbp, se extrae el rbp guardado de la pila y la función retorna.
En resumen, el uso de una pila sombra o un marco de pila es necesario para asegurar que las direcciones de retorno no se sobrescriban y que el programa pueda retornar de manera segura desde las llamadas a funciones.