2012-01-01 13 views
13

Próbuję nauczyć się pisania języka asemblerowego dla 64-bitowego systemu Mac OS. Nie mam problemu z 32-bitowym systemem Mac OS oraz 32- i 64-bitowym systemem Linux.Jak napisać asemblerowy program assemblera dla 64-bitowego systemu Mac OS X przy użyciu printf?

Jednak wersja 64-bitowa Mac OS jest inna i nie mogłem tego rozgryźć. Dlatego jestem tutaj, aby poprosić o pomoc.

Nie mam problemu z wykorzystaniem wywołania systemowego do drukowania. Chciałbym jednak nauczyć się wywoływać funkcje języka C przy użyciu 64-bitowego języka asemblera systemu Mac OS.

Proszę spojrzeć na poniższy kod

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


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

używam $ gcc -arch x86_64 hello.s

montaż i łącza.

Generuje kod binarny. Jednak podczas uruchamiania go wystąpił błąd segmentacji.

Próbowałem dodać "subq 8 USD,% rsp" przed wywołaniem _printf, wciąż taki sam wynik jak poprzednio.

Co zrobiłem źle?

Nawiasem mówiąc, czy to sposób debugowania tego kodu na komputerze Mac? Próbowałem dodać -ggdb lub -gstab lub -gDWARF, i $ gdb ./a.out, i nie widzę kodu i ustawić punkty przerwania.

+1

dobry artykuł na x86_64 konwencji numerem: http://nickdesaulniers.github.io/blog/2014/04/18/lets-write-some-x86-64/ –

+0

eax dla liczby vararg: http://stackoverflow.com/questions/6212665, problemy z wyrównaniem: http://stackoverflow.com/questions/10324333, http://stackoverflow.com/questions/14000351, ogólne pytanie: http://stackoverflow.com/questions/10857273 –

Odpowiedz

8

Nie powiedziałeś dokładnie, na czym polega problem, ale domyślam się, że ulegasz awarii w momencie połączenia z numerem printf. Wynika to z faktu, że OS X (zarówno 32-, jak i 64-bitowy) wymaga, aby wskaźnik stosu miał 16-bajtowe wyrównanie w punkcie każdego zewnętrznego wywołania funkcji.

Wskaźnik stosu był wyrównany do 16 bajtów po wywołaniu _main; to wywołanie przesunął ośmiobajtowy adres powrotny na stos, więc stos nie jest wyrównany do 16 bajtów w punkcie połączenia z _printf. Odejmij osiem od %rsp przed wykonaniem połączenia, aby prawidłowo wyrównać.


Więc poszedł do przodu i debugowania to dla ciebie (nie magia udział, wystarczy użyć gdb, break main, display/5i $pc, stepi, etc). Inny problem masz jest tutaj:

movq _hello(%rip), %rdi 

Ten ładuje pierwsze osiem bajtów Twojego ciąg do %rdi, co nie jest to, co chcesz w ogóle (w szczególności pierwsze osiem bajtów Twojego ciąg są bardzo mało prawdopodobne, aby stanowiło prawidłowy wskaźnik do ciągu formatu, co powoduje awarię w printf). Zamiast tego chcesz załadować adres adresu. Debugowany wersja programu jest:

.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

Dzięki, Stephen. Jestem świadomy problemu wyrównania stosu 16-bajtowego stosu. Dodałem subq 8 USD,% rsp i nadal otrzymuję błąd segmentacji ...Podejrzewam movq _hello (% rip),% rdi, nie jestem pewien, czy to prawda. W systemie Linux mogę po prostu użyć movq _hello,% rdi, ale mac będzie narzekał, że adres bezwzględny nie jest obsługiwany. –

+0

@AlfredZhong: zaktualizowałem moją odpowiedź dla ciebie. –

+0

Jeszcze raz dziękuję, Stephen! To działa! Cóż za magia! Próbowałem używać Leaqa. Jednak nie zdawałem sobie sprawy, nawet nie naciskając żadnego parametru na stosie, stos wciąż musi być wyrównany o 16 bajtów. Co za myląca sytuacja! –