【问题标题】:Calling Assembly functions in C, reverse a string在 C 中调用汇编函数,反转字符串
【发布时间】:2013-04-30 04:14:29
【问题描述】:

我正在尝试编写一个调用汇编函数来反转字符串的 c 程序。但是,我很难让汇编代码遍历字符串以找到结束字符“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 寄存器时是否做错了什么?

【问题讨论】:

  • 顺便问一下,这是在 32 位还是 64 位系统上?如果是 64 位系统,则删除地址的前 32 位,使指针指向内存中完全不同的位置。
  • 第一个参数是 [ebp + 8] 而不是 [esp + 8]。您还应该使用 LEA 的 MOV instea,否则您会得到 ((char*)$ebp + 8) 而不是 (*(char**)($ebp + 8))。看看我的回答

标签: c arrays assembly cpu-registers


【解决方案1】:

mov cl, [ebp+8] 代替 lea ecx, [esp+8] 怎么样?

【讨论】:

  • 然后将所有对“ecx”的引用改为“cl?”
  • 对不起,我错了。应该是lea ecx, [ebp+8]。 (我混淆了str*str
  • 对不起,我又错了。 mov ecx, [ebp+8] 是对的。它是 C 中的 str。您的 cmp byte [edi], ... 是 C 中的 *str
  • 您的答案与另一个答案似乎有效。不知道我为什么要使用 lea,只是在某个地方看到它,它之前在我正在处理的另一个程序中工作过一次。将其更改为 mov 纠正了该问题。谢谢!
【解决方案2】:

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

不妨试试

display *((char *) $edi)

【讨论】:

  • 那么在调试时我可以使用某个标志将该地址转换为数组中的相应字符吗?
  • @user2252004 更新了我的答案,请尝试一下。
  • 第一次迭代显示的结果是:断点 1, reverseLoop () at strrev.asm:25 25 cmp byte[edi], 0 1: *((char *) $edi) = 120'x'
  • 我似乎通过应用另一个答案的建议来解决这个问题,即将 lea ecx, [esp + 8] 更改为 mov ecx, [ebp+8]。通过这种更改,字符可以正确打印出来。感谢您的帮助:)。我肯定会回来,因为现在我必须弄清楚如何扭转它们:P。
【解决方案3】:

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

您最初的 mainLoop 尝试通过一次处理 4 个字节来实现 strlen(),但是您检测 '\0' 字节并保持字符串长度计数的方式只有在一次处理一个字节时才是正确的。

另一个不相关的错误是您在 EAX 中存储 (length-of-string / 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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-06-06
    • 1970-01-01
    • 1970-01-01
    • 2014-05-06
    • 2014-03-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多