2009-09-08 7 views
31

tengo el siguiente código asm:ret, RETN, retf - cómo los utilizan

; int __stdcall wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nShowCmd) 
[email protected] proc near 

var_8= dword ptr -8 
var_4= dword ptr -4 
hInstance= dword ptr 8 
hPrevInstance= dword ptr 0Ch 
lpCmdLine= dword ptr 10h 
nShowCmd= dword ptr 14h 

push ebp 
mov  ebp, esp 
sub  esp, 8 
mov  [ebp+var_4], 5 
mov  eax, [ebp+var_4] 
add  eax, 1 
mov  [ebp+var_8], eax 
xor  eax, eax 
mov  esp, ebp 
pop  ebp 
retn 10h 

Por lo que leo, tiene 3 tipos de instrucción de retorno: ret, RETN y retf, es decir, vuelta cerca y regresa lejos. Permiten un argumento opcional nBytes, que supongo que es el número de bytes que se mostrarán a partir de las variables definidas. ¿Cuándo debo usar retn o retf en lugar de ret? ¿Cómo puedo calcular el parámetro opcional, nBytes?

Respuesta

23

En la retne mnemónica, N es el tamaño de los parámetros en la pila. En este caso, es 4 * 4 = 16 (10 h) para 4 DWORD.
Pero esto solo se aplica a las convenciones de llamadas cuando el destinatario es responsable de la limpieza de la pila. En el caso de la convención cdecl, el ret debe estar sin ningún número, ya que el que llama es responsable de la limpieza de la pila.

+0

Ah, entonces la N in ret N se refiere a la cantidad de argumentos enviados por la persona que llama, y ​​no a las variables locales, como yo pensaba. ¿Es asi? –

+1

Sí. Es el número de argumentos empujados. Los locales están influenciados por mov esp, ebp comando al final – Max

50

En realidad, solo hay dos devoluciones diferentes, retn (near return) y retf (far return). Cuando solo usa ret, el ensamblador o compilador es lo suficientemente inteligente como para elegir cuál es necesario. Un retorno cercano es un salto dentro del segmento de código existente, un retorno lejano es un salto a un segmento de código diferente. En Windows, solo tiene un segmento de código único, y por lo tanto, ret debe ser solo una referencia para retn. Las instrucciones separadas retn y retf son un retroceso a los viejos tiempos cuando los modelos de memoria segmentados eran comunes. Casi todos los sistemas x86 de 32 bits que se ejecutan en la actualidad utilizan un modelo de memoria plano, no segmentado.

Ret sin argumento saca la dirección de retorno de la pila y salta hacia ella. Algunas convenciones de llamadas (como __stdcall) especifican que la función de llamada limpia la pila. En este caso, llaman a ret con número de bytes para sacar esos parámetros de la pila. Los 16 bytes son los parámetros de la función winmain.

+0

Lo siento, no recibí esta última parte. Cuando uso retn debería aclarar qué? Esas 2 variables + el ebp (4 bytes)? Como dices, eso hace 8 + 4 = 12, pero el código muestra 16. ¿Qué son esos 4 bytes restantes? –

+0

Lo sentimos, olvidemos que la convención de llamadas era __stdcall. Actualizaré mi respuesta. – Michael

+0

Si sustituye la función epilog con ret 0ch, el contenido de ebp será incorrecto en la función de llamada. – Max

20

Es en realidad dos tipos: retn y retf. El tercero ret está codificado por el ensamblador en uno de los dos primeros.

La diferencia es que retn (volver cerca) mostrará el puntero de instrucción (IP) solamente. Mientras que el retf (retorno lejos) mostrará el puntero de instrucción (IP) y el segmento de código (CS).

Cuestiones relacionadas