He escrito un programa básico en C que define una variable entera x, la establece en cero y devuelve el valor de esa variable:
#include <stdio.h> int main(int argc, char **argv) { int x; x = 0; return x; }Cuando descargo el código objeto usando objdump (compilado en Linux X86-64 con gcc):
0x0000000000400474 <main+0>: push %rbp 0x0000000000400475 <main+1>: mov %rsp,%rbp 0x0000000000400478 <main+4>: mov %edi,-0x14(%rbp) 0x000000000040047b <main+7>: mov %rsi,-0x20(%rbp) 0x000000000040047f <main+11>: movl $0x0,-0x4(%rbp) 0x0000000000400486 <main+18>: mov -0x4(%rbp),%eax 0x0000000000400489 <main+21>: leaveq 0x000000000040048a <main+22>: retq Puedo ver el prólogo de la función, pero antes de establecer x en 0 en la dirección 0x000000000040047f , hay dos instrucciones que mueven %edi y %rsi a la pila. ¿Para qué son estos?
Además, a diferencia de donde establecemos x en 0, la instrucción mov, como se muestra en la sintaxis de GAS, no tiene sufijo.
Si no se especifica el sufijo y no hay operandos de memoria para la instrucción, GAS deduce el tamaño del operando del tamaño del operando del registro de destino.
En este caso, ¿son -0x14(%rsbp) y -0x20(%rbp) operandos de memoria y cuáles son sus tamaños? Como %edi es un registro de 32 bits, ¿se mueven 32 bits a -0x14(%rsbp) mientras que como %rsi es un registro de 64 bits, se mueven 64 bits a %rsi,-0x20(%rbp) ?
En este caso simple, ¿por qué no le preguntas directamente a tu compilador? Para GCC, clang e ICC existe la opción -fverbose-asm .
main: pushq %rbp # movq %rsp, %rbp #, movl %edi, -20(%rbp) # argc, argc movq %rsi, -32(%rbp) # argv, argv movl $0, -4(%rbp) #, x movl -4(%rbp), %eax # x, D.2607 popq %rbp # ret Entonces, sí, guardan argv y argv en la pila usando el método de puntero de marco "antiguo" ya que las nuevas arquitecturas permiten restar/agregar desde/al puntero de pila directamente, omitiendo así el puntero de marco ( -fomit-frame-pointer ).
¿Propósito de los registros ESI y EDI?
Según esto y el contexto, no soy un experto, pero supongo que estos están capturando los parámetros de entrada main() . EDI toma un ancho estándar, que coincidiría con int argc , mientras que RSI toma un largo, que coincidiría con el puntero char **argv .