2011-07-05 9 views
5

Więc uczę się montażu nasm x86_64 na moim macu dla zabawy. Po witaniu świata i podstawowej arytmetyki, próbowałem skopiować nieco bardziej zaawansowany program na świecie od this site i modyfikując go na 64-bitową intel, ale nie mogę pozbyć się tego jednego komunikatu o błędzie: hello.s:53: error: Mach-O 64-bit format does not support 32-bit absolute addresses. Oto polecenie, którego używam do złożenia i połączenia: nasm -f macho64 hello.s && ld -macosx_version_min 10.6 hello.o. I tu jest odpowiednia linia:Błąd assemblera: Mach-O 64-bit nie obsługuje adresów bezwzględnych 32-bitowych

cmp rsi, name+8 

RSI jest rejestrem używam mojego indeksu w pętli, a nazwa jest słowem quad zarezerwowane dla danych wprowadzonych przez użytkownika, który jest nazwą, która przez ten punkt ma już został napisany.

Oto część kodu (aby zobaczyć resztę, kliknij link i przejść do sedna, jedyną różnicą jest to, że mogę korzystać z 64 rejestrów bitowych):

loopAgain: 
mov al, [rsi]   ; al is a 1 byte register 
cmp al, 0x0a   ; if al holds an ascii newline... 
je exitLoop    ; then jump to label exitLoop 

; If al does not hold an ascii newline... 
mov rax, 0x2000004  ; System call write = 4 
mov rdi, 1    ; Write to stdout = 1 
mov rdx, 1    ; Size to write 
syscall 

inc rsi 

cmp rsi, name+8   ; LINE THAT CAUSES ERROR 
jl loopAgain 
+0

Jak zdefiniowano "name"? – bdonlan

+0

Jedna z propozycji: spróbuj wpisać ten sam kod w C, skompiluj go za pomocą 'gcc -S' i spójrz na zestaw, aby zobaczyć, jak GCC go obsługuje. – Nemo

+0

@bdonlan: w sekcji .bss, mam 'name: resb 8' – mk12

Odpowiedz

3

Dyspozycja cmp nie obsługuje 64-bitowy operand natychmiastowy. W związku z tym nie można umieścić 64-bitowego bezpośredniego adresu w jednym z jego argumentów - załaduj name+8 do rejestru (używając zwykłego MOV), a następnie porównaj z tym rejestrem.

Możesz zobaczyć, jakie kodowanie instrukcji jest dozwolone w Intel ISA manual (ostrzeżenie: ogromny plik PDF). Jak widać na wejściu dla CMP, istnieją kodowania CMP r/m32, imm32 i CMP r/m64, imm32, które pozwalają na porównanie 32-bitowych natychmiastowych z rejestrami zarówno 32-bitowymi, jak i 64-bitowymi, ale nie CMP r/m64, imm64. Istnieje jednak kodowanie MOV r64, imm64.

Ponieważ nasm się zawiesza, niepowodzenie MOV rcx, name+8 jest po prostu błędem w nasm. Prześlij go do nasm devs (po upewnieniu się, że używasz najnowszej wersji nasm, a także sprawdź, czy this patch nie rozwiązuje problemu). W każdym razie, choć jedno obejście byłoby dodanie symbolu na koniec name:

name: 
    resb 8 
name_end: 

Teraz wystarczy użyć MOV rcx, name_end. Ma to tę zaletę, że nie wymaga aktualizacji referentów, gdy zmienia się rozmiar name. Alternatywnie można użyć innego asemblera, takiego jak clang lub asemblery GNU binutils.

+0

Próbowałem robić 'mov rcx, name + 8', a następnie' cmp rsi, rcx', ale kiedy montuję z nasm, po prostu wypisuję 'Segmentation fault'. Jeśli usunę +8, to się złoży w porządku. Dlaczego to robi? – mk12

+0

Jeśli zrobię "mov rcx, name', a następnie' add rcx, 8' i 'cmp rsi, rcx' to działa dobrze. Ale dlaczego nie mogę po prostu zrobić 'mov rcx, name + 8'? – mk12

+0

Co stanie się, gdy zdemontujesz wynik użycia nazwy + 8? – bdonlan

3

Wierzę, że problem, przed którym stoisz, jest prosty: format Mach-O nakazuje relokowalny kod, co oznacza, że ​​dane muszą być dostępne nie przez adres bezwzględny, ale przez adres względny. Oznacza to, że asembler nie może przekształcić name w stałą, ponieważ jest stałą, dane mogą być pod dowolnym adresem.

Teraz, kiedy już wiesz, że adres danych jest związany z adresem twojego kodu, sprawdź, czy możesz odczytać dane wyjściowe z GCC. Na przykład,

static unsigned global_var; 
unsigned inc(void) 
{ 
    return ++global_var; 
} 

_inc: 
    mflr r0           ; Save old link register 
    bcl 20,31,"L00000000001$pb"      ; Jump 
"L00000000001$pb": 
    mflr r10           ; Get address of jump 
    mtlr r0           ; Restore old link register 
    addis r2,r10,ha16(_global_var-"L00000000001$pb") ; Add offset to address 
    lwz r3,lo16(_global_var-"L00000000001$pb")(r2) ; Load global_var 
    addi r3,r3,1          ; Increment global_var 
    stw r3,lo16(_global_var-"L00000000001$pb")(r2) ; Store global_var 
    blr            ; Return 

Należy pamiętać, że jest to na PowerPC, bo nie wiem, Mach-O ABI dla x86-64. W PowerPC wykonujesz skok, zapisując licznik programu, a następnie wykonujesz arytmetyczną operację na wyniku. Wierzę, że coś zupełnie innego dzieje się na x86-64.

(Uwaga: Jeśli spojrzeć na montażowej GCC, spróbuj patrząc na niego z -O2 nie przeszkadza patrząc na -O0 ponieważ jest zbyt rozwlekły i trudniejsze do zrozumienia.).

Moja rekomendacja? ile piszesz kompilatora (a czasem nawet wtedy), napisać swoje funkcje montażowe na jeden z dwóch sposobów:

  • przekazać wszystkie niezbędne wskaźniki jako argumenty funkcji, lub
  • Napisz zespół jako inline montaż wewnątrz funkcji C.

Zasadniczo będzie to również bardziej przenośne, ponieważ będziesz polegać mniej na niektórych szczegółach ABI. Ale ABI jest nadal ważna! Jeśli nie znasz ABI i nie podążasz za nią, spowodujesz błędy, które są dość trudne do wykrycia. Na przykład wiele lat temu pojawił się błąd w kodzie montującym LibSDL, który spowodował, że libc's memcpy (także zespół) skopiował nieprawidłowe dane w pewnych ściśle określonych okolicznościach.

Powiązane problemy