2012-01-25 30 views
10

que desmontar un archivo objeto (lo más probable es generada usando el compilador de C++ Visual) usando DumpBin y vio el siguiente fragmento de código:¿Por qué el compilador genera este código?

...   ... 
mov   dword ptr [ebp-4],eax  // Why save EAX? 
push  dword ptr [ebp+14h] 
push  dword ptr [ebp+10h] 
push  dword ptr [ebp+0Ch] 
push  dword ptr [ebp+8] 
mov   eax,dword ptr [ebp-4]  // Why restore EAX? Did it change at all? 
call  <function> 
...   ... 

Podría alguien explicar por qué el registro EAX está siendo salvado y restaurado a través de estos 4 push instrucciones?

+2

He visto compiladores haciendo cosas más tontas que esta ... – Mysticial

+0

@Mysticial: Oh lol ... es la primera vez que noté algo como esto. :) Bueno saber. – Mehrdad

+7

Quizás haya una rama en el primer empujón. –

Respuesta

9

Además, tal vez está compilado en modo de lanzamiento, pero esa variable se ha marcado como volatile, que le dice al compilador que tal variable puede cambiar sin que lo sepa, por lo que se lo fuerza a escribir/restaurar continuamente en/desde la pila

+0

¡Ahhhh esto es muy posible! +1 gracias. – Mehrdad

+0

No estoy seguro de que 'volátil 'marque la diferencia aquí. 'volátil' pertenece a una ubicación de memoria, pero EAX es un registro; no puede marcar un registro como volátil. Así que 'volátil 'explicaría que [ebp-4] se vuelva a cargar * en * eax inmediatamente antes de cada operación, pero no se guardará eax. – Crashworks

+0

Para ciertas aritméticas, el compilador tiene que cargar una ubicación de memoria marcada como volátil en un registro, porque la operación no es posible como instrucción de lectura-modificación-escritura. Después de este segmento de código, una tienda puede seguir, y antes de esto puede preceder una carga, por lo que de la información en cuestión puede ser posible. Pero postly solo es una optimización perdida por VC. – hirschhornsalz

6

¿Estuvo integrado en el modo de depuración? Si es así, el compilador almacena cada variable local en la pila para que el depurador pueda encontrarlas de manera consistente.

La elisión de tales tiendas innecesarias y recargas es una de las optimizaciones que constituye el modo de "liberación".

+0

Creo que * se supone * que es código de modo de liberación (no hay ninguna versión de "depuración" de nada relacionado que pueda ver en ningún lado ...), pero no estoy seguro ... no tengo el código fuente ya sea. Pero +1 es una suposición razonable, gracias. – Mehrdad

2

volatile o no, la única técnica razón por la cual habría EAXtenga que ser inicializado directamente antes de hacer una llamada a la función en Windows eran si eso function se declara __syscall, es decir, utilizando la convención de llamada de Windows CS_SYSCALL. Conceptualmente, esto es un poco similar a la convención UN * X x86_64 donde %al contiene la cantidad de args de tipo de punto flotante pasados ​​en los registros %xmm.

La convención de llamadas syscall en Windows es idéntica a __cdecl, es decir, funciona args en la pila en orden inverso, pero con la adición de que AL contiene un recuento de la cantidad de argumentos; esto se hace para que el código del kernel, que generalmente se encuentra al final de este, sepa cuántos datos leer del usuario se apilan en la pila del kernel para recuperar los args.

EAX es un registro de arañazos para todas las convenciones de llamadas en Windows de 32 bits, su valor nunca se preserva con las llamadas a funciones, inicializándolo directamente antes de hacer una llamada es redundante. Incluso si la variable que contiene es volatile, porque una simple recarga no es una barrera de memoria y no "confirma" una tienda anterior. Además, la ubicación [EBP - 4] está dentro de la pila , por lo que la variable es local (y un calificador volatile tiene poco sentido).

Si no es una optimización perdido, entonces podría ser una invocación de un __syscall function(...) con diferente número de argumentos, como, hipotéticamente,

__syscall printf_syscall_conv(char *fmt, ...); 

void possibly_print_three_vals(char *fmt, int val1, int val2, int val3) 
{ 
    if (*strchr('%', fmt) == '\0') // if no "%" in fmt, pass no args 
     printf_syscall_conv(fmt); 
    else 
     printf_syscall_conv(fmt, val1, val2, val3); 
} 

Esto podría crear concebible salida de montaje como la suya.

Cuestiones relacionadas