2013-05-06 50 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