2013-07-02 69 views
6

因此,我正在學習使用NASM語法的x86 Linux程序集(哦,上帝,而不是這個,你們都在想)。我正在嘗試創建一個子例程,它將簡單地將EAX中的值打印到標準輸出。代碼運行並退出,沒有錯誤,但沒有打印。我無法弄清楚爲什麼。首先,這裏是我的工作文件:Linux x86 NASM - 子程序:從EAX打印一個dword

segment .bss 
    to_print: resd 1 

segment .text 
    global print_eax_val 

print_eax_val:     ;  (top) 
    push dword ebx   ;Stack: edx 
    push dword ecx   ;  ecx 
    push dword edx   ;  ebx 
           ;  (bot) 

    mov  ecx,eax    ;ecx = eax 

    mov  [to_print],ecx  ;to_print = ecx 

    mov  eax, 4    ;sys_write 
    mov  ebx, 1    ;to stdout 
    add  ecx, 47    ;add 47 for ASCII numbers 
    mov  edx, 2    ;double word = 2 bytes 
    int  0x80 

    mov  eax, [to_print]  ;eax = original val 
    pop  edx     ;pop the registers back from the stack 
    pop  ecx 
    pop  ebx     ;Stack: empty 

    ret 

這是從我的主文件,它看起來像這樣所謂的(這可能是無關緊要的,除非我失去了一些激烈的)。

segment .data 
     hello db  "Hello world!", 0 
     newline db  0xA 
     len  equ $ - hello 
     len2 equ $ - newline 

segment .text 
     extern print_nl 
     extern print_eax_val 
     global main 

main: 
     enter 0,0 

     call print_nl 

     mov  eax, 1 

     call print_eax_val 

     mov  ebx, 0   ;exit code = 0 (normal) 
     mov  eax, 1   ;exit command 
     int  0x80   ;ask kernel to quit 

print_nl只是定義和打印換行符的另一個子例程。這將成功運行並按預期打印新行。

問題是否與我的sys_write調用的長度參數有關?我給它2,這是dword的大小,這是EAX登記冊和我的to_print標籤的大小,我用resd 1保留了這個標籤。我試圖改變長度到1,4,8,16,和32絕望......沒有任何工作。

編輯:的人誰不知道,這裏是我固定的代碼:(我將把星號上,我改變行):

segment .bss 
    to_print: resd 1 

segment .text 
     global print_eax_val 

print_eax_val:      ;  (top) 
     push dword ebx   ;Stack: edx 
     push dword ecx   ;  ecx 
     push dword edx   ;  ebx 
            ;  (bot) 

     mov  ecx,eax    ;ecx = eax 

     mov  [to_print],ecx  ;to_print = ecx 

**** add  dword [to_print], 48 

     mov  eax, 4    ;sys_write 
     mov  ebx, 1    ;to stdout 
**** mov  ecx, to_print 
     mov  edx, 2 
     int  0x80 

**** sub  dword [to_print], 48 
     mov  eax, [to_print]  ;eax = original val 
     pop  edx     ;pop the registers back from the stack 
     pop  ecx 
     pop  ebx     ;Stack: empty 

     ret 

基本上,ecx必須包含地址你想要打印的塊的大小,而不是值本身。正如在選定的答案中指出的那樣,如果eax在0-9範圍內,這將僅工作

編輯2:所以我對sys_write的第二個參數(存儲在edx中的第二個參數)有點困惑。我認爲它只是指一些字節。因此,對於dword,正如我使用的那樣,在那裏使用4是合適的,因爲雙字是4字節或32位。我猜測它是有效的,因爲x86是小端。因此,在內存,to_print十六進制值應該是這樣的:

90 00 00 00

而且隨着兩個提供長度,SYS_WRITE得到:

90 00

所以價值幸運的是沒有被損壞。

後來我改變了代碼存儲to_print作爲一個字節,而不是使用resb 1使用byte代替dword訪問它...字節是在這裏很好,因爲我知道我不會給to_print以上的值9.

回答

4

我在彙編程序中很生鏽,但它看起來像ECX包含值48,當它應該包含要寫入的緩衝區的地址。

我推測你打算在print_eax_val中取的是EAX的二進制值,將ASCII偏移量加到數字0(本應該是48,而不是47),然後打印那個單個字符。爲此,請在將值存儲在to_print之前添加48,將地址to_print放入ECX中,並將長度(EDX)設置爲1,因爲您只寫入一個字符。

現在請記住,這隻適用於0x0000和0x0009之間的EAX值。當你過去9歲時,你會得到其他的ASCII字符。

解釋如何獲取EAX的任意二進制值並將其轉換爲可打印的十進制字符串遠遠超出了SO的範圍。

+0

非常感謝!你的回答非常有幫助。是的,我意識到這隻會在0到9之間工作,我只是試圖弄清楚一些基礎知識。也許我會最終開始一些數組/指針int到字符串的業務。我將使用更新的代碼編輯我的帖子,供任何想知道的人使用。 –

+3

我讚賞你處理彙編語言,並祝你好運。順便說一句,你的問題是一個典型的好SO問題。您準確地提供了所需的信息,顯示了您所嘗試的內容,並提出了具體問題。 +1 –

+1

再次感謝,我一定會將您的答案歸類爲「原型好的SO」答案。在我正在開發的一個項目中,我決定在一個巨大的C++ segfault/pointer崩潰之後學習一些x86程序集。首先,這是一個很好的轉換,其次,我想知道我在編寫代碼時實際做了什麼。 –

3

系統調用write需要打印字符的緩衝區,指向%ecx,長度由%edx給出。另一方面,%eax等寄存器包含一個32位整數。

所以,如果你真的想要打印%eax,你需要首先將該整數轉換爲一個字符序列。你可以將它轉換爲ASCII十進制,十六進制或其他任何你喜歡的基礎,但是你需要把它變成字符。

+0

感謝您的幫助!你的回答是完全有用的(因爲互聯網實際上對於彙編語言q&a來說實在是太稀少了,或者我可能只是不知道在哪裏尋找)。我首先得到了另一個答案,但如果我能選擇兩個正確的答案,我會的。 –

+0

非常小的nit:「dword」的大小是4,而不是2 ... –

+0

哈哈,沒有錯誤是組裝中的「次要」(從我迄今爲止學到的)。我很困惑我應該把這個長度參數放進去。有關更多說明,請參閱我的文章中的編輯2。但是,謝謝! –