¿Por qué se mueve 0 a la pila cuando se usa el valor de retorno?
⏰ 5 minutos de lectura
Estoy experimentando desmontando clang binarios de programas simples en C (compilados con -O0), y estoy confundido acerca de cierta instrucción que se genera.
Aquí hay dos vacíos main funciones con argumentos estándar, uno de los cuales devuelve valor y otro no:
Ahora, cuando desarmo sus ensamblajes, se ven razonablemente diferentes, pero hay una línea que no entiendo:
return_void.bin:
(__TEXT,__text) section
_main:
0000000000000000 pushq %rbp
0000000000000001 movq %rsp, %rbp
0000000000000004 movl %edi, -0x4(%rbp)
0000000000000007 movq %rsi, -0x10(%rbp)
000000000000000b popq %rbp
000000000000000c retq
return_0.bin:
(__TEXT,__text) section
_main:
0000000100000f80 pushq %rbp
0000000100000f81 movq %rsp, %rbp
0000000100000f84 xorl %eax, %eax # We return with EAX, so we clean it to return 0
0000000100000f86 movl $0x0, -0x4(%rbp) # What does this mean?
0000000100000f8d movl %edi, -0x8(%rbp)
0000000100000f90 movq %rsi, -0x10(%rbp)
0000000100000f94 popq %rbp
0000000100000f95 retq
Solo se genera cuando uso la función no es nula, así que pensé que podría ser otra forma de devolver 0, pero cuando cambié la constante devuelta, esta línea no cambió en absoluto:
// return_1.c
int main(int argc, char** argv)
{
return 1;
}
empty_return_1.bin:
(__TEXT,__text) section
_main:
0000000100000f80 pushq %rbp
0000000100000f81 movq %rsp, %rbp
0000000100000f84 movl $0x1, %eax # Return value modified
0000000100000f89 movl $0x0, -0x4(%rbp) # This value is not modified
0000000100000f90 movl %edi, -0x8(%rbp)
0000000100000f93 movq %rsi, -0x10(%rbp)
0000000100000f97 popq %rbp
0000000100000f98 retq
¿Por qué se genera esta línea y cuál es su propósito?
¿Ha probado esto con funciones normales, es decir, funciones que no se llaman principales?
– Negocio serio
30 de junio de 2015 a las 22:55
main no tener un int el tipo de retorno va en contra del estándar para entornos alojados. No espere ningún comportamiento específico.
– demasiado honesto para este sitio
30 de junio de 2015 a las 23:33
Prohibiste a clang eliminar código inútil (con -O0). ¿Por qué preguntas sobre un código extraño en el ensamblado?
– fuz
30 de junio de 2015 a las 23:41
Hormiga
El propósito de esa área se revela mediante el siguiente código
int main(int argc, char** argv)
{
if (rand() == 42)
return 1;
printf("Helo World!\n");
return 0;
}
Al principio lo hace
movl $0, -4(%rbp)
entonces el regreso anticipado queda de la siguiente manera
Por lo tanto, esta área está reservada para almacenar el valor de retorno de la función. No parece ser muy necesario y no se usa en código optimizado, pero en -O0 modo que es la forma en que funciona.
clang está haciendo espacio en la pila para los argumentos (registros edi y rsi) y también pone el valor 0 en la pila, por alguna razón. Supongo que clang compila su código en una representación SSA como esta:
int main(int argc, char** argv)
{
int a;
a = 0;
return a;
}
Esto explicaría por qué se asigna una ranura de pila. Si clang también se propaga constantemente, esto explicaría por qué eax se pone a cero en lugar de cargarse desde -4(%rbp). En general, no piense demasiado en construcciones dudosas en no optimizado montaje. Después de todo, le prohibiste al compilador eliminar código inútil.
Giuseppe Pes
movl $0x0,-0x4(%rbp)
Esta instrucción almacena 0 en %rbp - 4. Parece que clang asigna una variable local oculta para un valor de retorno implícito de main.
De la lista de correo clang:
Si. Asignamos una variable local implícita para contener el valor de retorno; declaraciones de retorno luego simplemente inicialice la ranura de retorno y salte al epílogo, donde se carga y devuelve la ranura. No usamos un phi porque el flujo de control para llegar al epílogo no es necesariamente tan simple como una rama simple, debido a las limpiezas en los ámbitos locales (como los destructores de C++).
Los valores de retorno implícitos como los de main se manejan con un almacén implícito en el prólogo.
eaxlo hace tenga el valor de retorno, no estoy seguro de dónde obtuvo esta idea de almacenarlo en la pila, especialmente en el marco de la persona que llama que se destruirá cuando la persona que llama obtenga el control para examinar el valor de retorno. Parece que eres tú quien lo confunde con el regreso. habla a.
– bufón
30 de junio de 2015 a las 23:08
Además del hecho de que este es un ABI de 32 bits para cualquier sistema, mientras que el OP claramente usa un sistema de 64 bits, el valor de retorno es guardado en EAX/RAX. Estás confundiendo :), tal vez por el nombre Regreso habla ala pregunta OP es realmente buena.
– usuario781847
30 de junio de 2015 a las 23:08
Ambos tienen razón, no lo siento, me equivoqué, estoy actualizando la respuesta. Gracias
– Giuseppe Pes
30 de junio de 2015 a las 23:11
@Giuseppe Pes: en la plataforma x86, el puntero de la pila se mueve hacia abajo (a direcciones de memoria más bajas) a medida que crece la pila. Esto significa que la dirección de retorno siempre se almacena en positivo compensado desde el rsp valor al inicio de la función. -0x4(%rbp) no puede ser la dirección del remitente en este caso.
– Ant
30 de junio de 2015 a las 23:31
Es bueno que esté tratando de mejorar su respuesta, pero el hecho es que no es relevante. El OP sabe claramente cómo funcionan las llamadas de pila y función, solo se pregunta (como yo) por qué hay un movimiento con 0 en el cuerpo. Creo que es mejor borrarlo y escribir uno nuevo si sabes la respuesta 🙂
– usuario781847
30 de junio de 2015 a las 23:32
Según el estándar (para entornos alojados), 5.1.2.2.1, main está obligado a devolver un int resultado. Por lo tanto, no espere un comportamiento definido si viola esto.
Es más, main en realidad _no se requiere que devuelva explícitamente 0; esto se devuelve implícitamente si llega al final de la función. (Tenga en cuenta que esto es sólo para mainque tampoco tiene un prototipo.
¿Ha sido útil esta solución?
Tu feedback nos ayuda a saber si la solución es correcta y está funcionando. De esta manera podemos revisar y corregir el contenido.
Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos.
Configurar y más información
¿Ha probado esto con funciones normales, es decir, funciones que no se llaman principales?
– Negocio serio
30 de junio de 2015 a las 22:55
main
no tener unint
el tipo de retorno va en contra del estándar para entornos alojados. No espere ningún comportamiento específico.– demasiado honesto para este sitio
30 de junio de 2015 a las 23:33
Prohibiste a clang eliminar código inútil (con
-O0
). ¿Por qué preguntas sobre un código extraño en el ensamblado?– fuz
30 de junio de 2015 a las 23:41