2013-05-06 84 views
2

我正在嘗試編寫一個將調用匯編函數來反轉字符串的c程序。但是,我很難獲取彙編代碼來遍歷字符串來查找結尾字符「0」。在C中調用匯編函數,反轉字符串

我的C代碼如下:

#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 
#include <ctype.h> 

// These functions will be implemented in assembly: 
// 
void strrev(char *str) ; 

int main(){ 
    char *str1; 
    str1 = strdup("Hello World"); 
    strrev(str1); 
    printf("str1 reversed: \"%s\"\n", str1) ; 
    free(str1); 
    return 0; 
} 

任何我的彙編代碼很簡單:

; File: strrev.asm 
; A subroutine called from C programs. 
; Parameters: string A 
; Result: String is reversed and returned. 


    SECTION .text 
    global strrev 
_strrev: nop 
strrev: 
    push ebp 
    mov ebp, esp 

    ; registers ebx,esi, and edi must be saved if used 
    push ebx 
    push edi 

    xor esi, esi  
    xor eax, eax 
    lea ecx, [esp+8] ; load the start of the array into ecx 
    jecxz end  ; jump if [ecx] is zero 
    mov edi, ecx 

reverseLoop: 
    cmp byte[edi], 0 
    je end 
    inc  edi 
    inc eax 
    jmp reverseLoop 

end: 
    pop edi  ; restore registers 
    pop ebx 
    mov esp, ebp ; take down stack frame 
    pop ebp 
    ret 

所有我想這個代碼,現在所要做的只是通過串迭代,直到它找到在reverseLoop內部結束。但是,如果我嘗試使用gdb並逐步執行程序,則在查看第一個字符「H」後,它似乎立即失敗。

運行與GDB與線25斷裂,同時顯示與「顯示/ C $ EDI」的EDI寄存器產生以下輸出:

(gdb) 
reverseLoop() at strrev.asm:25 
25  cmp byte[edi], 0 
1: /c $edi = 72 'H' 

哪個是正確的,但如果我通過下臺到INC edi,edi立即變得不正確。它應該是「E」,因爲「Hello World」中的第二個字符是「e」。但是,gdb的輸出列出其爲「I」:

27  inc  edi 
1: /c $edi = 72 'H' 
(gdb) 
28  inc eax 
1: /c $edi = 73 'I' 

難道我做錯了什麼,當我通過EDI註冊迭代?

+0

順便說一句,這是一個32位或64位系統上?如果它是一個64位系統,則刪除地址的前32位,使指針指向內存中完全不同的地方。 – 2013-05-06 12:14:25

+0

第一個參數是[ebp + 8] NOT [esp + 8]。你也應該使用LEA的MOV instea,否則''((char *)$ ebp + 8)'而不是'(*(char **)($ ebp + 8))''。看看我的回答 – scottt 2013-05-06 12:36:58

回答

0

您正在將edi寄存器的內容打印爲字符,但內容實際上是一個地址。你真正想要的是顯示edi指向的地址。

也許嘗試

display *((char *) $edi) 
+0

那麼在調試時可以使用某個標誌來將該地址轉換爲數組中的相應字符嗎? – user2252004 2013-05-06 12:03:35

+0

@ user2252004更新了我的答案,請嘗試。 – 2013-05-06 12:08:46

+0

在第一次循環的顯示的結果是:斷點1,reverseLoop()在strrev.asm:25 \t \t CMP字節[EDI],0 1:*((字符*)$ EDI)= 120 'x' – user2252004 2013-05-06 12:10:47

0

如何mov cl, [ebp+8]代替lea ecx, [esp+8]

+0

然後將所有對「ecx」的引用改爲「cl?」 – user2252004 2013-05-06 12:02:43

+0

對不起,我錯了。它應該是'lea ecx,[ebp + 8]'。 (我混淆了'str'和'* str') – habe 2013-05-06 12:05:10

+0

對不起,我又錯了。 'mov ecx,[ebp + 8]'是對的。它是C中的'str',而你的'cmp byte [edi],...''是C中的'* str'。 – habe 2013-05-06 12:09:51

0

函數序言建立EBP後,第一個參數是在[ebp + 8][esp + 8]。自從進入strrev()以來,您已經推動了EBP,EBX,EDI,並因此在您想要訪問該函數的第一個參數時移動了ESP。您還應該使用MOV instea LEA,否則您將獲得((char*)$ebp + 8)而不是(*(char**)($ebp + 8))

你原來mainLoop試圖通過一次處理4個字節,但是你的方式進行檢測'\0'字節和維護字符串長度計數是唯一正確的,如果在時間內完成一個字節來實現strlen()

其它不相關的缺陷已得到你存儲在EAX但(/ 2串長度的-)還使用AL作爲臨時存儲,同時交換字符。 ALEAX的最低字節,字符串長度被破壞,字符交換循環在正確的迭代次數後不會終止。

看看下面的補丁,在那裏我固定從早期版本你的問題的代碼:

--- strrev.asm.orig 2013-05-06 20:25:58.000497875 +0800 
+++ strrev.asm 2013-05-06 20:26:29.583835308 +0800 
@@ -17,16 +17,37 @@ 

    xor esi, esi  
    xor eax, eax 
- lea ecx, [esp+8] ; load the start of the array into ecx 
+ mov ecx, [ebp+8] ; load the start of the array into ecx 
    jecxz end  ; jump if [ecx] is zero 
- mov edi, ecx 
+ 
+mainLoop: 
+ add eax, 1  ; icn eax would work as well 
+ add  ecx, 1  
+ mov dl, [ecx] ; load ecx 
+ cmp dl, 0  ; compare with 0 
+ je reverseLoop  ; if ecx is zero, we're done 
+ jmp mainLoop  ; if ecx isn't zero, keep looping 
+ 

reverseLoop: 
- cmp byte[edi], 0 
- je end 
- inc  edi 
- inc eax 
- jmp reverseLoop 
+ mov ecx, [ebp + 8] ; reload the start of the array into ecx 
+ mov esi, ecx ; esi points to start of array 
+ add ecx, eax 
+ mov edi, ecx  
+ dec edi ;edi points to end of array 
+ shr eax, 1 ;eax is the count 
+ jz end ; if string is 0 or 1 chars long, jump to end 
+ 
+reverseLoop_1: 
+ 
+ mov cl, [esi] ;load initial array 
+ mov bl, [edi] ;load end of array 
+ mov [esi], bl ;swap 
+ mov [edi], cl 
+ inc esi 
+ dec edi 
+ dec eax  ;loop 
+ jnz reverseLoop_1 

end: 
    pop edi  ; restore registers