【问题标题】:Assembly code - cannot find error汇编代码 - 找不到错误
【发布时间】:2016-06-25 15:19:53
【问题描述】:

如果能在我的代码中发现错误,我将不胜感激。我本来应该写一个函数,它接受一个缓冲区的地址、字母 l、数字 n 和只能接受 2 个值的增量变量:0 和 1。 如果增加变量为 0,则该函数应该重复同一个字母 n 次。如果增加变量为 1,则该函数应返回一串后续字母,例如“abcd....”(后续 ascii 字符)。 字母 l 决定了我们以字符串开头的字母。

我尝试使用 ddd,它告诉我问题出在线路上 移动 %ecx, (%edx) 而且我知道寄存器 edx 和 ecx 中有错误的值。 尽管如此,我还是不明白出了什么问题以及如何纠正它。我将非常感谢您的帮助。

#include <stdio.h>
#include <stdlib.h>
extern char * generate_str(char * s, int c, int n, int inc);

int main()
{
    char s[100] = "something";
    char c = 'a';
    int n = 5;
    int inc = 0;
    printf("String %s\n", generate_str(s, (int)c, n, inc));
}

汇编代码:

.data
character: .int 0

# char -> 1
# int -> 4
# arguments: char * s, int c, int n, int inc

.equ bufor,8
.equ c,12
.equ n,16
.equ inc,20
#eax, ebx, ecx, edx

.text
.type generate_str, @function
.global generate_str

generate_str:

    PUSHL %ebp            #prolog of the function
    MOVL %esp, %ebp
    MOVL inc(%esp), %eax  #copy variable inc into eax
    MOVL n(%esp), %ebx    #copy variable n into ebx
    PUSHL %ecx            #save contents of ecx
    MOVL c(%esp), %ecx    #copy variable c into ecx temporarily
    MOVL %ecx, character  #copy variable c into reserved memory called character
    POPL %ecx             #restore contents of c
    MOVL bufor(%esp), %edx #copy addres of a buffer into edx

    CMP $0, %eax # eax > 0 ?  #is inc variable 0 or 1
    JA one                    #if it is 1, go to line "one"
    MOVL %ebx, %ecx           %copy value of variable n into ecx, it tells how many letters should be placed in the buffer 
p:
    PUSHL %ecx                #save contents of ecx
    MOVL character, %ecx      #copy character into ecx
    MOVL %ecx, (%edx)         #copy character into the place in the memory which address is given in edx
    POPL %ecx                 #restore contents of ecx
    ADDL $4, %edx             #increase value of edx by 4, so we move forwards in the memory to save another letter there
    loop p                    #loop until ecx is 0
    jmp end                   #jump to the final part of the function

one:                          #if the value of inc is 1 then do another loop  
    PUSHL %ecx                #save ecx and use this register to copy character into the place in memory which address is in the edx registry
    MOVL character, %ecx
    MOVL %ecx, (%edx)
    POPL %ecx
    ADDL $1, character        #increase ascii character by 1
    ADDL $4, %edx             #move in memory by 4 bytes so we can save the next letter
    loop one                  #continue loop until ecx is zero
    jmp end

end:
    MOVL %edx, %eax          #copy address of the final string into eax
    movl %ebp,%esp           #restore registers
    popl %ebp
RET

【问题讨论】:

  • MOVL bufor(%esp), %edx 也许你的意思是LEAL bufor(%esp), %edx。很难说,因为您没有正确注释您的代码。此外,由于您现在显然可以使用ddd,单步执行代码并查看它在哪里出错,不要只查看错误指令。 PS:ebx是被调用者保存的寄存器。
  • 我在代码中添加了 cmets。 ddd中的一步如何?每次我按“步骤”时,它都会告诉我程序没有运行。每次我运行程序时,它都会在我按下“step”之前完成。
  • 您在函数的开头或任何您想要的地方放置一个断点,然后您可以从那里单步执行。是的,copy address 你需要使用lea 而不是mov

标签: assembly i386


【解决方案1】:

感谢您的建议。 DDD 完成了它的工作。这是更正后的代码。老实说,当我增加应该插入下一个字符的地址时(因为它已转换为 int),我仍然无法理解为什么我应该添加 1 美元而不是 4 美元,但它可以工作。

#include <stdio.h>
#include <stdlib.h>
extern char * generate_str(char * s, int c, int n, int inc);

int main()
{
    char s[100] = "cos tam";
    char c = 'a';
    int n = 5;
    int inc = 0;
    printf("String %s\n", (char*) generate_str(s, (int)c, n, inc));

}

汇编代码:

#dane
.data
character: .int 0

# char -> 1
# int -> 4
# arguments: char * s, int c, int n, int inc

.equ bufor,8
.equ c,12
.equ n,16
.equ inc,20
#eax, ebx, ecx, edx

.text
.type generate_str, @function
.global generate_str

generate_str:
    PUSHL %ebp
    MOVL %esp, %ebp
    MOVL inc(%esp), %eax 
    MOVL n(%esp), %ecx 
    MOVL c(%esp), %ebx
    MOVL bufor(%esp), %edx 
    PUSHL %ebx      

    CMP $0, %eax
    JA one
p:
    MOVL %ebx, (%edx)
    ADDL $1, %edx
    loop p
    ADDL $0, %edx
    jmp end

one:
    MOVL %ebx, (%edx)
    ADDL $1, %ebx
    ADDL $1, %edx
    loop one    
    ADDL $0, %edx
    jmp end

end:
    MOVL bufor(%esp), %edx
    MOVL %edx, %eax
    movl %ebp,%esp
    popl %ebx
    popl %ebp
    RET

以及使用变量的版本

#dane
.data
letter: .int 0

# char -> 1
# int -> 4
# arguments: char * s, int c, int n, int inc

.equ bufor,8
.equ c,12
.equ n,16
.equ inc,20
#eax, ebx, ecx, edx

.text
.type generate_str, @function
.global generate_str

generate_str:
     PUSHL %ebp
     MOVL %esp, %ebp
     MOVL inc(%esp), %eax 
     MOVL n(%esp), %ecx 
     MOVL c(%esp), letter
     MOVL bufor(%esp), %edx 
     PUSHL %ebx 

     CMP $0, %eax
     JA one
 p:
     MOVL letter, %edi
     MOVL %edi, (%edx)
     ADDL $1, %edx
     loop p
     jmp end

one:
     MOVL %ebx, (%edx)
     ADDL $1, %ebx
     ADDL $1, %edx
     loop one   
     ADDL $0, %edx
     jmp end

end:
      MOVL bufor(%esp), %edx
      MOVL %edx, %eax
      MOVL %ebp,%esp
      popl %ebx
      popl %ebp
      RET

【讨论】:

  • 了解为什么我应该在增加应该插入下一个字符的地址时添加 $1 而不是 $4(因为它已转换为 int) .字符串是字节数组。在内存中,每个元素都是一个字节。当然,您可以即时转换为 int,但在内存中,每个 char 是一个字节。
  • 看起来您仍在破坏调用者的%ebx,因此如果使用更复杂的调用函数和/或在启用优化的情况下编译,您的代码将会崩溃。请参阅 stackoverflow.com/questions/8335582/…,以及 the x86 tag wiki 中有关 ABI 的其他内容
  • 我添加了 PUSHL %ebx 和 POPL %ebx。现在怎么样?
  • 我认为您在修复 %ebx 错误时导致了另一个错误,因为您没有更改任何其他内容来解释您压入堆栈的额外 4 个字节。例如您没有更改 MOVL n(%esp), %ecx 的偏移量。通常,您首先会保留push %ebp,并在堆栈上相对于它引用args。 (从%ebp 到任何给定参数的偏移量是整个函数的常量,与%esp 的偏移量不同。这是使用 ebp 制作“堆栈帧”的主要好处:您不必跟踪%esp 指向你的参数和本地人。)
  • 查看一个小函数(来自gcc -m32 -fno-omit-frame-pointer -O3)的一些编译器输出作为示例。例如this example on the Godbolt compiler explorer 演示了制作一个堆栈帧,然后保存 %ebx,并访问相对于 %ebp 的函数 arg。奇怪的是,Clang 的版本不是最优的(使用push %eax 来保留空间/对齐堆栈,然后在计算一个值之后使用mov %eax, (%esp)。)gcc 的版本有一个有趣的方式在调用后清理堆栈,使用@987654338 @ 加载 ebx,然后是 leave 而不是 pop %ebp
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-11-16
  • 2015-03-17
  • 2014-12-02
相关资源
最近更新 更多