2013-05-14 188 views
0

我想在這裏學習一些程序集,我需要來自專業人士的一點幫助!Noob ASM問題

test.s:

.data 
helloworld: 
    .asciz "printf test! %i\n" 
.text 
.globl main 
main: 
    push $0x40 
    push $helloworld 
    call printf 
    mov $0, %eax 
    ret 

test.working.s:

.data 
helloworld: 
    .asciz "printf test! %i\n" 
.text 
.globl main 
main: 
    mov $0x40, %esi 
    mov $printf_test, %edi 
    mov $0x0, %eax 
    call printf 
    mov $0, %eax 
    ret 

compile.sh:

rm test 
gcc test.s -o test -lc 
chmod 777 test 
./test 

test.s立即出現segfaults。我通過使用eclipse中的反彙編窗口來製作test.working.s,並且只是編寫一個小的C程序來使用printf打印一些東西。

所以,問題!

  1. 爲什麼test.s不行

  2. 在我的C程序,主要是指主(INT ARGC,字符** argv的),是不是?因此,如果我不需要這些參數,我不應該在開始時兩次需要pop

  3. 在x86-64中,我讀到%rax是64位寄存器,%eax是32位寄存器,%ax是16位寄存器。所以寄存器看起來是這樣的: XX XX EE EE RR RR RR RR(R = 4位RAX,E = 4位EAX,X = 4位AX) 在小端系統上(1表示爲0x01000000,我想... )?

  4. GCC不會讓我輸入pop %eaxpush %eax。它只會讓我輸入64位版本或16位版本。那麼我如何將RAX的32個EAX位推入堆棧呢?我怎麼彈出只有32位?

  5. test.working.s(我想這是回答1,但如果不是...)通過更改寄存器來調用printf,而不是將東西壓入堆棧。我認爲這是因爲它更快?你怎麼知道什麼時候調用c函數,按照什麼順序來做到這一點?

  6. 這也適用於Windows x86-64嗎?我知道printf的操作可能會有所不同,但如果我在printf後清理並恢復寄存器,我應該沒問題?

  7. 你應該如何清理和恢復寄存器?根據http://www.cs.uaf.edu/2005/fall/cs301/support/x86/,它說我「必須保存%esp,%ebp,%esi,%edi」。這是指這樣一個事實,即當我編寫函數時,這些寄存器必須以它們進入的方式返回,或者在我調用某個函數之前我應該​​自己保存它們。這可能是前者,因爲%esp,但只是檢查!

  8. 很明顯,我不會需要x86-64,特別是因爲我剛剛開始,所以如何才能改變x86的compile.sh?

  9. .asciz只是表示.ascii + "\0"

  10. 我可以返回駐留在堆棧中的大型結構(> 64位)。這在裝配中如何實現?

乾杯的任何幫助!

回答

2
  1. 因爲在64位模式下,您應該將參數傳遞到寄存器而不是堆棧上(請參閱this answer)。即使情況並非如此,您沒有push $0x40的尺寸說明符,所以很可能您只推送16位值而不是32位。

  2. 堆棧的頂部將包含返回地址,以便呼叫main的呼叫來自(例如__libc_start_main)。在下面你會發現argcargv。你不需要彈出其中的任何一個(因爲你需要保留返回地址,所以你不應該彈出其中的任何一個)。

  3. 32位值1將被寫爲0x00000001(最左下方是nybble),並且將以小尾數配置存儲爲(low address) 01 00 00 00 (high address)。由於通常首先編寫數字的最高有效位,而不是根據它們的存儲方式,因此將RAX的描述編寫爲RR RR RR RR EE EE XX XX,如果不清楚訂單是什麼,可能使用位索引標記是有意義的。


  4. 再次,這是在所描述this answer調用約定對64位x86代碼。

  5. 由於Windows is slightly different(用於傳遞參數的寄存器是RCX, RDX, R8, R9)使用的64位調用約定不會發生一些變化。

  6. 通過將它們保存在堆棧上例如。有調用者保存的寄存器和調用者保存的寄存器。
    被調用者(被調用的函數)必須保存某些寄存器並在返回之前恢復它們以符合調用約定。對於Linux型系統上的一個64位程序,該程序將爲RBX, RBP, R12-R15(在64位Windows上,這也包括RSIRDI)。
    調用者(調用函數的代碼)必須將某些寄存器視爲易失性(即可以由函數進行更改),並且應該在函數返回後需要保存並恢復它們。在Linux類型的系統上,這些將是RAX, RCX, RDX, RSI, RDI, R8-R11

  7. GNU彙編程序應該支持一個-m32命令行選項來指定您正在裝配32位代碼。

  8. 是的。

+0

非常感謝!任何關於10的想法?另外,如果little endian指示最低有效位位於第一個字節中,您確定它不是'01 00 00 00'嗎?維基百科表示,大字節表示最低有效位位於最後一個字節 – AStupidNoob 2013-05-14 06:25:43

+0

這取決於您如何查看數據。如果你在一個文件中有32位小端值1,2,並在一個十六進制編輯器中逐字節地查看它,你會看到「01 00 00 00 02 00 00 00」。但是,如果您將視圖更改爲雙字模式,您會看到'00000001 00000002'。 – Michael 2013-05-14 06:34:20

+0

我對第10點的建議是用'-S'選項編譯一個小C例子並檢查生成的彙編代碼。 – Michael 2013-05-14 06:39:18