如何解决错误:我的代码中出现分段错误(核心转储)? AT&T 语法

Posted

技术标签:

【中文标题】如何解决错误:我的代码中出现分段错误(核心转储)? AT&T 语法【英文标题】:How do I solve the error: Segmentation fault(core dumped) in my code? AT&T syntax 【发布时间】:2021-11-27 19:30:07 【问题描述】:

我找了好几个小时,但在我的代码中找不到错误。该程序似乎在“opadd”标签处退出。我在 Linux 上使用 x86_64,使用 AT&T 语法。

程序将一个字符串作为输入,例如“2 3 add 4 mul”,然后,对于这个特定的示例,应该执行以下操作:

将 2 添加到堆栈中 将 3 添加到堆栈中 计算 2 和 3 的和,然后将 5 添加到堆栈中 将 5 和 4 相乘并将 20 添加到堆栈中,然后打印 20。
.data
    formatPrintf: .asciz "%d"
    sir: .space 1000
    delim: .asciz " "
    formatScanf: .asciz "%1000[^\n]"
    cuvant: .space 100
    primulNumar: .space 4
    atoiCuvant: .long 0
    x: .space 4
    y: .space 4
    eval: .long 0
    op: .space 4
    add: .asciz "add"
    sub: .asciz "sub"
    mul: .asciz "mul"
    div: .asciz "div"
.text

.global main

main:
    pushl $sir
    pushl $formatScanf
    call scanf
    popl %ebx
    popl %ebx

    pushl $delim
    pushl $sir
    call strtok
    popl %ebx
    popl %ebx

    pushl %eax
    call atoi
    popl primulNumar

    movl %eax, primulNumar

    pushl primulNumar

et_loop:
    pushl $delim
    pushl $0
    call strtok
    popl %ebx
    popl %ebx

    cmp $0, %eax
    je exit

    mov %eax, cuvant

    pushl %eax
    call atoi
    popl %ebx

    mov %eax, atoiCuvant

    cmp  $0, atoiCuvant
    je operatie

    pushl %eax

    jmp et_loop

operatie:

    push $cuvant
    push $add
    call strcmp
    popl %ebx
    popl %ebx

    cmp $0, %eax
    je opadd

    push $cuvant
    push $sub
    call strcmp
    popl %ebx
    popl %ebx

    cmp $0, %eax
    je opsub

    push $cuvant
    push $mul
    call strcmp
    popl %ebx
    popl %ebx

    cmp $0, %eax
    je opmul

    push $cuvant
    push $div
    call strcmp
    popl %ebx
    popl %ebx

    cmp $0, %eax
    je opdiv

opadd:
    popl %edx
    popl y

    add %edx, y
    push y

    jmp et_loop

opmul:
    popl %eax
    popl %ebx
    mul %ebx
    pushl %eax

    jmp et_loop
opdiv:
    popl %eax
    popl %ebx
    div %ebx
    pushl %eax

    jmp et_loop

opsub:
    popl %eax
    popl y
    sub %eax,y
    pushl y

    jmp et_loop

exit:
    popl eval

    pushl eval
    pushl $formatPrintf
    call printf
    popl %ebx
    popl %ebx

    pushl $0
    call fflush
    popl %ebx

    movl $1, %eax
    xorl %ebx, %ebx
    int $0x80

编辑:

.data
    formatPrintf: .asciz "%d"
    sir: .space 1000
    delim: .asciz " "
    formatScanf: .asciz "%1000[^\n]"
    cuvant: .space 100
    primulNumar: .space 4
    atoiCuvant: .space 4
    x: .space 4
    y: .long 4
    eval: .space 4
    op: .space 4
    add: .asciz "a"
    sub: .asciz "s"
    mul: .asciz "m"
    div: .asciz "d"
.text

.global main

main:
    pushl $sir
    pushl $formatScanf
    call scanf
    popl %ebx
    popl %ebx
    
    pushl $delim
    pushl $sir
    call strtok
    popl %ebx
    popl %ebx
    
    pushl %eax
    call atoi
    popl %ebx
    
    movl %eax, primulNumar
    
    pushl primulNumar
    
et_loop:
    pushl $delim
    pushl $0
    call strtok
    popl %ebx
    popl %ebx
    
    cmp $0, %eax
    je exit
    
    mov %eax, %esi
    
    pushl %eax
    call atoi
    popl %ebx
    
    cmp  $0, %eax
    je operatie
    
    pushl %eax
    
    jmp et_loop
    
operatie:
    mov $0, %ecx
    movb (%esi,%ecx,1), %al
    
    cmp $97,%al
    je opadd
    
    cmp $115, %al
    je opsub
    
    cmp $109, %al
    je opmul
    
    cmp $100, %al
    je opdiv
    
opadd:
    popl %ebx 
    popl %eax 
    add %ebx, %eax 
    pushl %eax 
    
    jmp et_loop
    
opmul:
    popl %eax
    popl %ebx
        mul %ebx
        pushl %eax
    
    jmp et_loop
opdiv:
    popl %ebx
    popl %eax
    xorl %edx, %edx
    div %ebx
    pushl %eax
    
    jmp et_loop

opsub:
    popl %eax
    popl y
    sub %eax,y
    pushl y
    
    jmp et_loop

exit:
    popl eval
    
    pushl eval
    pushl $formatPrintf
    call printf
    popl %ebx
    popl %ebx
    
    pushl $0
    call fflush
    popl %ebx
    
    movl $1, %eax
    xorl %ebx, %ebx
    int $0x80

【问题讨论】:

您是否尝试过使用 valgrind 等内存检查器?类似的东西应该可以帮助您确定问题出现在哪一行。 @user1280483 谢谢,我使用了 gdb,看起来程序在“opadd”标签处退出。我还是不知道怎么解决这个问题 opadd 是一个标签。它在哪个指令上产生段错误? @JosephSible-ReinstateMonica 如果我在operatie 放置一个断点,在两个stepi 之后,gdb 转到??() from /lib32/libc.so.6,我认为是strcmp,然后它以信号 SIGSEGV 这是 32 位代码;我假设您正在使用gcc -m32 foo.s 构建它。所有这些推送和弹出是怎么回事?你调用strtok,然后你通过将任何垃圾弹出到EAX中来重新平衡堆栈,覆盖返回值。然后通过推动 EAX 将其传递给 atoi!也许你很幸运,strtok 没有碰巧覆盖它的堆栈参数,而且你只在第一次调用而不是在循环中这样做,所以你得到了你传递的原始字符串,它恰好是第一个的开始令牌? 【参考方案1】:
opadd:
    popl %edx <-- there IS a push without a pop on the first line of code, but...
    popl y <-- now there is nothing left in the stack

当您到达opadd 时,堆栈中似乎没有任何内容。 et_loopoperatie 中的每个 push 操作都有一个对应的 pop。我还不能测试你的例子,因为你没有包含你的.data 部分,但我几乎可以肯定这就是问题所在。

【讨论】:

我添加了整个代码,但我认为这不是问题,因为输入总是以 2 个数字开头 您是否尝试过系统地注释行?您可能会发现其中popl 行之一的问题。 popl y 没有对应的pushl 我不认为这是问题所在,因为我已经尝试过一个一个地取出持久性有机污染物,但没有任何改变 @user1280483:与所有平衡推送/弹出对混合在一起,有一个不平衡的pushl atoiCuvant(而不是一个简单的push %eax,出于某种原因),所以提问者可能是对的是在达到je opadd 之前堆栈上的两个数字。它是jcc,而不是call,因此它不会弹出返回地址。这可能不是一个错误。不过,它确实看起来很奇怪,但可以同时使用 callstack 作为堆栈数据结构,同时也可以使用它来传递参数和返回地址给库函数,只要它在每次使用后都进行清理。【参考方案2】:
mov %eax, cuvant
pushl %eax
call atoi
popl %ebx

知道atoi 是一个指针,我们看到 cuvant 变量将保存一个地址。

operatie:
 push $cuvant
 push $add
 call strcmp
 popl %ebx
 popl %ebx

知道strcmp 需要两个指针,我们看到这段代码传递了 cuvant 变量的地址,而实际上 它应该传递包含在 中的地址cuvant 变量(指向当前标记)。 因此操作中的所有比较都会失败。

 cmp $0, %eax
 je opdiv

opadd:

当所有 4 次比较都失败时代码会在opadd: 中失败。没什么好期待的!


对于 opsubopdiv,参数的顺序很重要。减法没问题,但除法错误。 考虑“8 2 div”。红利 (8) 将最先推出,因此必须最后推出。在使用div 之前不要忘记将%edx 归零!

opdiv:
    popl  %ebx            divider (2)
    popl  %eax            dividend (8)
    xorl  %edx, %edx      true dividend %edx:%eax
    div   %ebx
    pushl %eax 
    jmp   et_loop

使用atoi 调度操作的一个缺点是,您将无法使用数字 0 作为参数(如在“5 0 add”中)。有没有关系...

【讨论】:

我已按照您的建议进行了所有更正,但我仍然收到分段错误(核心转储)错误 @VeryNice 尝试以下操作:opadd:popl %ebxpopl %eaxadd %ebx, %eaxpushl %eaxjmp et_loop。还要验证新代码现在是否有 operatie: push cuvant push $add call strcmp 还是不行 @VeryNice 考虑将最新版本的代码作为编辑添加到您的问题中。注意不要更改现有问题中的任何内容!在已经存在的内容下方写一个新的 EDIT 部分。 我添加了编辑部分

以上是关于如何解决错误:我的代码中出现分段错误(核心转储)? AT&T 语法的主要内容,如果未能解决你的问题,请参考以下文章

在我的模板类示例中,出现“分段错误(核心转储)”错误

我运行程序时出现“分段错误(核心转储)”

为啥我的代码会出现分段/核心转储错误?

如何防止我的程序出现此分段错误(核心转储)?

什么原因导致C中出现分段错误(核心转储)?

C++ std 线程和列表分段错误(核心转储)