2010-09-10 10 views
5

Esta descripción es válida para Linux 32 bit: Cuando comienza un programa Linux, todos los punteros a los argumentos de la línea de comandos se almacenan en la pila. El número de argumentos se almacena en 0 (% ebp), el nombre del programa se almacena en 4 (% ebp) y los argumentos se almacenan en 8 (% ebp).Linux 64 parámetros de línea de comando en el ensamblaje

Necesito la misma información para 64 bit.

Editar: He trabajando ejemplo de código que muestra cómo utilizar argc, argv [0] y argv [1]: http://cubbi.com/fibonacci/asm.html

 
.globl _start 
_start: 
    popq %rcx  # this is argc, must be 2 for one argument 
    cmpq $2,%rcx 
    jne  usage_exit 
    addq $8,%rsp  # skip argv[0] 
    popq %rsi  # get argv[1] 
    call ... 
... 
} 

Parece que los parámetros están en la pila. Como este código no está claro, hago esta pregunta. Supongo que puedo mantener rsp en rbp, y luego acceder a estos parámetros usando 0 (% rbp), 8 (% rbp), 16 (% rbp) etc. ¿Es esto correcto?

Respuesta

9

Parece que la sección 3.4 Inicialización de proceso, y específicamente figura 3.9, en el ya mencionado System V AMD64 ABI describe precisamente lo que desea saber.

+2

Las respuestas del enlace solo son frágiles. Esa página no funciona para mí. * www.x86-64.org no envió ningún dato. ERR_EMPTY_RESPONSE * – doug65536

1

Creo que lo que debes hacer es consultar x86-64 ABI. Específicamente, creo que debe consultar la sección 3.2.3 Pasar parámetros.

+2

Esto describe la convención de llamadas dentro del programa. Los parámetros de la línea de comando de proceso pueden tener sus propias reglas. Necesito esta información específica para Linux 64. –

8

A pesar de que la respuesta aceptada es más que suficiente, me gustaría dar una respuesta explícita, ya que hay algunas otras respuestas que pueden confundir.

más importantes (para más información ver ejemplos más abajo): en x86-64 los argumentos de línea de comandos se pasan a través de la pila:

(%rsp) -> number of arguments 
8(%rsp) -> address of the name of the executable 
16(%rsp) -> address of the first command line argument (if exists) 
... so on ... 

Es diferente de parámetro de la función que pasa en x86-64, que utiliza %rdi, %rsi y así sucesivamente.

Una cosa más: uno no debe deducir el comportamiento de la ingeniería inversa de la C main -función. C runtime proporciona el punto de entrada _start, ajusta los argumentos de la línea de comandos y llama al main como una función común. Para verlo, consideremos el siguiente ejemplo.

Sin tiempo de ejecución C/GCC con -nostdlib

vamos a ver este sencillo programa x86-64 ensamblador, que no hacen más que devuelve 42:

.section .text 
.globl _start 
_start: 
    movq $60, %rax #60 -> exit 
    movq $42, %rdi #return 42 
    syscall #run kernel 

Construimos con:

as --64 exit64.s -o exit64.o 
ld -m elf_x86_64 exit64.o -o exit64 

o con

gcc -nostdlib exit64.s -o exit64 

carreras en GDB con

./exit64 first second third 

y se detiene en el punto de ruptura en _start. Vamos a comprobar los registros:

(gdb) info registers 
... 
rsi   0x0 0 
rdi   0x0 0 
... 

No había nada. ¿Qué hay de la pila?

(gdb) x/5g $sp 
0x7fffffffde40: 4 140737488347650 
0x7fffffffde50: 140737488347711 140737488347717 
0x7fffffffde60: 140737488347724 

Así que el primer elemento en la pila es 4 - argc la esperada. Los siguientes 4 valores se parecen mucho a los punteros. Miremos el segundo puntero:

(gdb) print (char[5])*(140737488347711) 
$1 = "first" 

Como era de esperar, es el primer argumento de línea de comando.

Así que hay evidencia experimental de que los argumentos de la línea de comando se pasan a través de la pila en x86-64. Sin embargo, solo leyendo el ABI (como sugirió la respuesta aceptada) podemos estar seguros de que este es realmente el caso.

Con tiempo de ejecución C

Tenemos que cambiar ligeramente el programa, el cambio de nombre _start en main, ya que el punto _start entrada es proporcionada por el tiempo de ejecución C.

.section .text 
.globl main 
main: 
    movq $60, %rax #60 -> exit 
    movq $42, %rdi #return 42 
    syscall #run kernel 

lo construimos con (C tiempo de ejecución se utiliza por defecto):

gcc exit64gcc.s -o exit64gcc 

carreras en GDB con

./exit64gcc first second third 

y parar en el punto de ruptura en main. ¿Qué hay en la pila?

(gdb) x/5g $sp 
0x7fffffffdd58: 0x00007ffff7a36f45 0x0000000000000000 
0x7fffffffdd68: 0x00007fffffffde38 0x0000000400000000 
0x7fffffffdd78: 0x00000000004004ed 

No le resulta familiar. Y registros?

(gdb) info registers 
... 
rsi   0x7fffffffde38 140737488346680 
rdi   0x4 4 
... 

Podemos ver que rdi contiene el valor argc. Pero si ahora inspeccionamos el puntero en rsi pueden suceder cosas extrañas:

(gdb) print (char[5])*($rsi) 
$1 = "\211\307???" 

Pero espera, el segundo argumento de la función main en C no es char *, pero char ** también:

(gdb) print (unsigned long long [4])*($rsi) 
$8 = {140737488347644, 140737488347708, 140737488347714, 140737488347721} 
(gdb) print (char[5])*(140737488347708) 
$9 = "first" 

Y ahora Encontramos nuestros argumentos, que se pasan a través de registros como lo sería para una función normal en x86-64.

Conclusión: Como podemos ver, el es una diferencia respecto a paso de los argumentos de línea de comandos entre el código de tiempo de ejecución utilizando C y código que no lo hace.

+1

Puedes construir un programa que defina '_start' [usando' gcc -nostdlib'] (http://stackoverflow.com/questions/36861903/assembling-32-bit-binaries-on-a-64-bit- system-gnu-toolchain/36901649 # 36901649). Si solo defines 'main', not' _start', tienes que usar gcc, pero definir '_start' no te impide usar gcc. –

+0

Ver registros/memoria es una buena forma de averiguar cómo algo * podría * funcionar, es decir, para buscar algo para buscar documentos oficiales.No debes simplemente asumir que todo lo que encuentres está garantizado para estar allí, sin embargo. No quiere depender de algo que en realidad es solo una copia de cero remanente, y podría no estar en una versión futura. Sus conclusiones son correctas, pero sería mucho mejor hacer referencia a cómo SysV ABI garantiza que esos valores estarán allí. –

+0

Tampoco mencionas el código CRT glibc que proporciona '_start' y llama a tu' main'. No hay diferencia mágica entre definir 'main' o' _start'; no es difícil entender lo que hace el código repetitivo de arranque para recolectar el estado de inicio del proceso y llamar 'main' con esos argumentos de función. –

Cuestiones relacionadas