2012-01-01 21 views
13

Estoy tratando de aprender a escribir lenguaje ensamblador para Mac OS de 64 bits. No tengo ningún problema con Mac OS de 32 bits y Linux de 32 bits y 64 bits.Cómo escribir el lenguaje ensamblador hello world program para 64 bit Mac OS X usando printf?

Sin embargo, Mac OS 64 bit es diferente y no pude entenderlo. Por lo tanto, estoy aquí para pedir ayuda.

No he tenido problemas al utilizar la llamada al sistema para imprimir. Sin embargo, me gustaría aprender cómo llamar a las funciones C utilizando el lenguaje ensamblador de 64 bits de Mac OS.

Por favor mirar el siguiente código

.data 
_hello: 
    .asciz "Hello, world\n" 


.text 
.globl _main 
_main: 
    movq $0, %rax 
    movq _hello(%rip), %rdi 
    call _printf 

utilizo $ gcc -arch x86_64 hello.s

de montar y de enlace.

Genera código binario. Sin embargo, recibí un error de segmentación cuando lo ejecutaba.

Intenté agregar "subq $ 8,% rsp" antes de llamar a _printf, sigue teniendo el mismo resultado que antes.

¿Qué hice mal?

Por cierto, ¿hay alguna forma de depurar este código en Mac? Intenté agregar -ggdb o -gstab o -gDWARF, y $ gdb ./a.out, y no puedo ver el código y establecer puntos de interrupción.

+1

buen artículo sobre convenciones de llamada x86_64: http://nickdesaulniers.github.io/blog/2014/04/18/lets-write-some-x86-64/ –

+0

eax for vararg count: http://stackoverflow.com/questions/6212665, problemas de alineación: http://stackoverflow.com/questions/10324333, http://stackoverflow.com/questions/14000351, pregunta genérica: http://stackoverflow.com/questions/10857273 –

Respuesta

8

No dijiste exactamente cuál es el problema que estás viendo, pero supongo que estás estrellándote en el momento de llamar a printf. Esto se debe a que OS X (32 y 64 bits) requiere que el puntero de la pila tenga una alineación de 16 bytes en el punto de cualquier llamada de función externa.

El puntero de la pila estaba alineado en 16 bytes cuando se llamó a _main; esa llamada empujó una dirección de retorno de ocho bytes en la pila, por lo que la pila no está alineada en 16 bytes en el punto de la llamada al _printf. Reste ocho de %rsp antes de hacer la llamada para alinearla correctamente.


Así que siguió adelante y depurado esto para usted (nada mágico, sólo tiene que utilizar el BGF, break main, display/5i $pc, stepi, etc). El otro problema que tienes es aquí:

movq _hello(%rip), %rdi 

Esto carga los primeros ocho bytes de su cadena en %rdi, que no es lo que desea en absoluto (en particular, los ocho primeros bytes de la cadena son es muy poco probable que constituya un puntero válido a una cadena de formato, lo que da como resultado un bloqueo en printf). En su lugar, desea cargar la dirección de la cadena. Una versión depurada de su programa es:

.cstring 
_hello: .asciz "Hello, world\n" 

.text 
.globl _main 
_main: 
    sub $8, %rsp   // align rsp to 16B boundary 
    mov $0, %rax 
    lea _hello(%rip), %rdi // load address of format string 
    call _printf   // call printf 
    add $8, %rsp   // restore rsp 
    ret 
+0

Gracias, Stephen. Soy consciente del problema de alineación de la pila de 16 bytes. Añadí subq $ 8,% rsp y todavía recibo un error de segmentación ...Sospecho que movq _hello (% rip),% rdi, no estoy seguro de que esto sea correcto. En Linux, puedo usar movq _hello,% rdi, pero mac se quejará de que la dirección absoluta no es compatible. –

+0

@AlfredZhong: actualicé mi respuesta por usted. –

+0

¡Gracias de nuevo, Stephen! ¡Funciona! ¡Qué magia! De hecho, traté de usar leaq. Sin embargo, no me di cuenta de que, incluso sin presionar ningún parámetro en la pila, la pila todavía tiene que estar alineada en 16 bytes. ¡Qué situación tan confusa! –