2

我正在尝试编写 ac 程序,该程序将调用汇编函数来反转字符串。但是,我很难让汇编代码遍历字符串以找到结束字符“0”。

我的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”后似乎立即失败。

在第 25 行使用中断运行 GDB,同时使用“display/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 寄存器时,我做错了吗?

4

3 回答 3

0

函数序言设置EBP后,第一个参数位于[ebp + 8] NOT [esp + 8]。自从进入 以来strrev(),您已经推送了 EBP、EBX、EDI 并因此在您想要访问该函数的第一个参数时移动了 ESP。您还应该使用LEA的MOV instea ,否则您会得到.((char*)$ebp + 8)(*(char**)($ebp + 8))

您的原始mainLoop尝试strlen()通过一次处理 4 个字节来实现,但您检测'\0'字节和维护字符串长度计数的方式只有在一次完成一个字节时才是正确的。

另一个不相关的错误是您将 (length-of-string / 2) 存储在EAX中,但在交换字符时也使用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
于 2013-05-06T12:29:50.927 回答
0

您正在将寄存器的内容edi打印为字符,但内容实际上是一个地址。您真正想要的是显示地址 inedi指向的内容。

也许试试

display *((char *) $edi)
于 2013-05-06T12:00:30.910 回答
0

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

于 2013-05-06T12:00:40.680 回答