2012-06-29 40 views
1

我正試圖學習x86程序集。我正在使用的書是Assembly Language - Step by Step, Programming With Linux(我不得不說它很好)。到目前爲止,我學到了很多東西,但我覺得自己也應該挑戰自我,在很多方面保持領先地位,這樣我就可以更快地學習(我可以繼續學習,自上而下學習,但是我覺得它很乏味慢)。x86程序集:在mul(seg故障)後向控制檯輸出整數

所以,我認爲嘗試乘兩個寄存器(32位)然後將數據輸出到控制檯是一個很酷的主意。

問題是,當我執行程序時(我正在使用NASM,就像本書所做的那樣 - 雖然沒有Insight調試器),但我收到了一個分段錯誤。我在gdb中進行了相當數量的調試,但是出於某種原因,我似乎無法弄清楚問題所在。

我想知道我爲什麼會收到分段錯誤,以及 譴責此問題的好方法。另外,如果我在代碼中所做的評論與實際發生的事情不匹配,如果有人能糾正我的錯誤,我將不勝感激。

這是我的代碼到目前爲止(它很好評論)

謝謝。

德codez

section .data 
;TODO 

section .bss 
valueToPrint: resb 4   ;alloc 4 bytes of data in 'valueToPrint' 

section .text 

global _start 

_mul: 
    mov eax, 0x2A ;store 42 in eax 
    mov edx, 0x2A ;store 42 in edx 
    mul eax 
    ret 

_safe_exit: 
    mov eax, 1 ;initiate 'exit' syscall 
    mov ebx, 0 ;exit with error code 0 
    int 0x80 ;invoke kernel to do its bidding 

_start: 
    nop       ;used to keep gdb from complaining 

    call _mul      ;multiply the values 
    mov [valueToPrint], eax   ;store address of eax in the contents of valueToPrint 
    mov eax, 4      ;specify a system write call - aka syswrite 
    mov ebx, 1      ;direction used to make the syswrite call output to console - i.e. stdout 
    mov dword [ecx], valueToPrint ;store valueToPrint in ecx: ecx represents the syswrite register 
    int 0x80      ;invoke kernel based on the given parameters 

    call _safe_exit 

編輯

而且,我跑Arch Linux的,如果有差別。

+0

,使用了'MUL eax',它乘EAX,EAX與...因爲你在eax和edx中都使用了42,你會得到(當然,你會得到)正確的答案,但是你不涉及edx中的值。 @Adam Rosenfield(下面)雖然有正確的答案,但您並不是首先創建要打印的TEXT/ASCII字符串。程序集並不會自動爲你做任何事情。這就是爲什麼你有這麼多控制權但你必須告訴它如何去做所有事情。您不會創建txt字符串來指示ecx進行打印。 (你還需要一個長度) – lornix

+0

此外,'mul'(乘法)將eax(ax/rax等)中的值與指定的值相乘,並將得到的64位值放入EDX:EAX中。如果你'重新尋找完整的答案,記得也使用edx值。 42 * 42 = 1764,所以你遠離edx的答案,但你仍然應該知道發生了什麼,所以你可以期望edx改變,如果需要可用。 – lornix

+1

(天哪,我今晚很健談!)小挑剔的東西....你的'_safe_exit'子程序...因爲它永遠不會返回,所以你應該用'jmp'來代替它。是的,當程序退出時,系統會丟棄所有內容......但是,您應該知道事情是如何工作的。 'call _save_exit'很好,但對我來說,它會在堆棧中留下一個值。挑剔的東西。我很抱歉。當我開始在16K機器上裝配時,內存很貴,而且你編寫了嚴格的代碼。我仍然這麼想,即使是8G的RAM。一般來說運行速度更快。 – lornix

回答

2

此行是造成分段錯誤:

mov dword [ecx], valueToPrint 

你告訴它的內存位置存儲valueToPrint地址ecx。你永遠不會初始化ecx(內核可能會在程序啓動時將它初始化爲0),所以當你取消引用時,你將訪問一個無效的內存位置。

write(2)系統調用需要3個參數:在寄存器ebx文件描述符號碼,一個指針到字符串中ecx寫和字節數在edx寫。因此,如果只想打印結果的原始二進制數據,則可以傳遞valueToPrint的地址,並告訴它從該地址打印4個字節。在這種情況下,valueToPrint是1764(0x6e4十六進制),所以這個代碼將打印出的4個字節e4 06 00 00在x86,這是小尾​​數:

mov [valueToPrint], eax ; store the result into memory 
mov eax, 4    ; system call #4 = sys_write 
mov ebx, 1    ; file descriptor 1 = stdout 
mov ecx, valueToPrint  ; store *address* of valueToPrint into ecx 
mov edx, 4    ; write out 4 bytes of data 
int 0x80     ; syscall 
+0

首先,我想說明謝謝澄清。然而,我很好奇打印10位數的整數有多困難? – zeboidlund

+0

要以10爲底進行打印,您需要保留足夠大的字符數組來保存輸出(對於有符號的32位整數,至少需要11個字節)。然後,重複將數字除以10直到達到0,將剩餘的加48保存到數組中的一個字符中(48爲ASCII「0」),從右到左。最後,將該緩衝區的地址傳遞給'sys_write()',並跟蹤打印的字節數。或者,如果你想跳過所有這些,只需調用'printf(3)'。 –

1

EDITIT! (忘了CMP!)

要以10爲基數產值...

; assuming value is in EAX (only) 
.loop1: 
    div 10  ; divide by 10, leave result in eax, REMAINDER in edx 
    push eax  ; save value 
    mov eax,edx 
    or eax,0x30 ; convert 0-9 to '0'-'9' (ascii 0x30='0') 
    call display_a_char ; (you write this!) 
    pop eax 
    or eax,eax ; set flags (edit!) 
    jnz .loop1 
; all done, maybe put a \n return or something here 
在_mul子程序
+0

這是無袖...所以它應該工作得很好,只有小問題 – lornix

+0

作爲旁邊...改變一條線,增加兩條線...這將轉換輸出到任何基地, 36很容易。好極了! – lornix