2012-10-26 75 views
4

我是很新,在Mac上的x64組裝,所以我越來越糊塗在64位移植一些32位代碼。
程序應該簡單地通過C標準庫中的printf函數打印出一條消息。
我已經開始使用此代碼:64 NASM:推動內存地址到堆棧和通話功能

section .data 
    msg db 'This is a test', 10, 0 ; something stupid here 

section .text 
    global _main 
    extern _printf 

_main: 
    push rbp 
    mov  rbp, rsp  

    push msg 
    call _printf 

    mov  rsp, rbp 
    pop  rbp 
    ret 

與NASM這樣編譯它:

$ nasm -f macho64 main.s 

返回以下錯誤:

main.s:12: error: Mach-O 64-bit format does not support 32-bit absolute addresses 

我試圖解決這個問題字節更改代碼這樣:

section .data 
    msg db 'This is a test', 10, 0 ; something stupid here 

section .text 
    global _main 
    extern _printf 

_main: 
    push rbp 
    mov  rbp, rsp  

    mov  rax, msg ; shouldn't rax now contain the address of msg? 
    push rax   ; push the address 
    call _printf 

    mov  rsp, rbp 
    pop  rbp 
    ret 

它編譯罰款與上述nasm命令,但現在有一個警告,而與gcc編譯對象文件到實際的程序:

$ gcc main.o 
ld: warning: PIE disabled. Absolute addressing (perhaps -mdynamic-no-pic) not 
allowed in code signed PIE, but used in _main from main.o. To fix this warning, 
don't compile with -mdynamic-no-pic or link with -Wl,-no_pie 

因爲它是一個警告不是我執行的a.out文件的錯誤:

$ ./a.out 
Segmentation fault: 11 

希望有人知道我做錯了什麼。

回答

7

64位OS X ABI符合在大到System V ABI - AMD64 Architecture Processor Supplement。其代碼模型與小型位置獨立代碼模型(PIC)非常相似,差別在於here。在該代碼模型中,所有本地和小型數據都是使用RIP相對尋址直接訪問的。正如Z boson在評論中指出的那樣,64位Mach-O可執行文件的映像庫超出了虛擬地址空間的前4個GiB,因此push msg不僅是將msg地址放在堆棧上的無效方式,但它也是不可能的,因爲PUSH不支持64位立即值。該代碼應該看起來類似於:

lea rax, [rel msg] ; RIP-relative addressing 
push rax 

但在這種特殊情況下,根本不需要推入棧上的值。64位調用約定要求第一個6整數/指針參數在RDI,RSI,RDX,RCX,R8R9的正確順序中傳遞。前8個浮點或向量參數進入XMM0XMM1,...,XMM7。只有在使用了所有可用的寄存器之後,或者存在不能適用於這些寄存器的參數(例如80位的值爲long double)時,才使用堆棧。使用MOVQWORD變體)而不是PUSH執行64位立即推送。簡單的返回值將返回到RAX寄存器中。調用者還必須爲被調用者提供堆棧空間來保存一些寄存器。

printf是一個特殊的函數,因爲它需要可變數量的參數。當調用這些函數RAX時,應將其設置爲向量寄存器中傳遞的浮點參數的數量。另請注意,RIP-對於位於代碼2吉比內的數據,首選相對尋址。

下面是如何gcc在OS X轉化printf("This is a test\n");成彙編:

xorb %al, %al    (1) 
    leaq L_.str(%rip), %rdi  (2) 
    callq _printf    (3) 

L_.str: 
    .asciz "This is a test\n" 

(這是AT & T型組件,源被左,目的地是正確的,註冊名稱的前綴爲%,數據寬度是編碼爲指令名稱的後綴)

(1)由於沒有傳遞任何浮點參數,因此將零置入RAX。在(2)處,該字符串的地址被加載到RDI中。請注意,該值實際上是與當前值RIP的偏移量。由於彙編器不知道這個值是什麼,它會在目標文件中放置一個重定位請求。鏈接器會看到重定位並在鏈接時提供正確的值。

我不是NASM大師,但我認爲下面的代碼應該這樣做:

section .data 
    msg db 'This is a test', 10, 0 ; something stupid here 

section .text 
    global _main 
    extern _printf 

_main: 
    push rbp 
    mov  rbp, rsp  

    xor  al, al 
    lea  rdi, [rel msg] 
    call _printf 

    mov  rsp, rbp 
    pop  rbp 
    ret 
+0

該函數是否應該從堆棧中刪除參數或者調用者是否應該刪除它們? (如果堆棧中有任何參數) – qwertz

+0

調用者應在調用後清理堆棧。 –

+0

你的回答並不能解釋爲什麼NA​​SM不會創建目標文件。原因是Mac OS X的限制。OS X上的圖像基大於2^32。OP的原始代碼與elf64很好地組裝在一起。 [馬赫 - 鄰 - 64位格式此結果不支持的32位絕對地址-NASM](https://stackoverflow.com/questions/26394359/mach-o-64-bit-format- do-not-support-32-bit-absolute-addresses-nasm/26402647#26402647) –

2

根據用於x86 64位指令中的文檔集http://download.intel.com/products/processor/manual/325383.pdf

PUSH只接受8,16和32位的立即值(64位寄存器和寄存器尋址的存儲器塊被允許雖然)。

PUSH msg 

其中msg是64位立即地址將不會編譯,因爲你發現了。


什麼調用約定是_printf在您的64位庫中定義的?

是期待它的堆棧上的參數或使用快速調用規範,其中在寄存器中的參數?因爲x86-64使更多通用寄存器可用,所以更快地使用快速調用約定。

+0

'msg'是一個32位地址,所以問題不是'PUSH'。問題是Mach-O不允許32位絕對尋址。 –

3

沒有答案了已經解釋了爲什麼NA​​SM報告

Mach-O 64-bit format does not support 32-bit absolute addresses 

原因NASM不會做這在Agner Fog's Optimizing Assembly手冊中有解釋3.3尋址模式根據小節標題爲32位絕對尋址在64位模式下他寫道

32-bit absolute addresses cannot be used in Mac OS X, where addresses are above 2^32 by default.

這在Linux或Windows上不是問題。事實上,我已經在static-linkage-with-glibc-without-calling-main上展示過這個作品。 hello world代碼在elf64上使用32位絕對尋址,運行良好。

@HristoIliev建議使用rip相對尋址,但沒有解釋在Linux中的32位絕對尋址也可以。如果您更改lea rdi, [rel msg]lea rdi, [msg]它組裝和運行良好與nasm -efl64但失敗nasm -macho64

像這樣一個事實:

section .data 
    msg db 'This is a test', 10, 0 ; something stupid here 

section .text 
    global _main 
    extern _printf 

_main: 
    push rbp 
    mov  rbp, rsp  

    xor  al, al 
    lea  rdi, [msg] 
    call _printf 

    mov  rsp, rbp 
    pop  rbp 
    ret 

您可以檢查,這是一個絕對的32位地址,而不是撕裂相對與objdump。但是,需要指出的是,首選的方法仍然是rip相對尋址。 Agner在同一個手冊中寫道:

There is absolutely no reason to use absolute addresses for simple memory operands. Rip- relative addresses make instructions shorter, they eliminate the need for relocation at load time, and they are safe to use in all systems.

那麼什麼時候會在64位模式下使用32位絕對地址呢?靜態數組是一個很好的選擇。請參閱以下小節以64位模式尋址靜態數組。簡單的情況是e.g:

mov eax, [A+rcx*4] 

其中A是靜態數組的絕對值的32位地址。這對Linux很好,但再次使用Mac OS X時無法執行此操作,因爲默認情況下,映像基本大於2^32。在Mac OS X上,請參閱Agner手冊中的示例3.11c和3.11d。在例如3.11c,你可以做

mov eax, [(imagerel A) + rbx + rcx*4] 

如果您使用來自馬赫Ø__mh_execute_header的外部引用來獲取圖像的基礎。在例3.11c中你使用了rip相對尋址並加載這樣的地址

lea rbx, [rel A]; rel tells nasm to do [rip + A] 
mov eax, [rbx + 4*rcx] ; A[i] 
+0

我簡直無法找到一個可靠的信息,爲什麼64位Mach-O二進制文件的圖像基礎如此之高,但似乎在x86-64上'__PAGEZERO'段佔用了VM的整個前4 GiB,因此任何32位指針無效('__PAGEZERO'既不可讀也不可寫或不可執行)。這可能是一種廉價的方式,通過簡單地崩潰程序來檢測假設'sizeof(int)'等於sizeof(void *)'的舊程序代碼。 –

+0

[This answer](http://stackoverflow.com/a/23188634/1374437)及其評論表明我的假設可能是正確的。 –

+0

@HristoIliev,它看起來像你找出你。我想知道這有什麼其他影響。這意味着靜態數組的尋址模式[絕對32位地址+索引]在OS X中是不可能的。Evgeny Kluev對我的問題的建議https://stackoverflow.com/questions/25899395/obtaining-peak-bandwidth-on在OS X中,只有cache-only-getting-62纔是不可能的。一些指令/端口(例如Haswell上的端口7)只能用於這種簡單的尋址模式。此外,微操作融合可能會受到影響http://stackoverflow.com/questions/26046634/microfusion-and-addressing-modes –