如何解决错误:我的代码中出现分段错误(核心转储)? 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_loop
和 operatie
中的每个 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:
中失败。没什么好期待的!
对于 opsub 和 opdiv,参数的顺序很重要。减法没问题,但除法错误。
考虑“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 %ebx
popl %eax
add %ebx, %eax
pushl %eax
jmp et_loop
。还要验证新代码现在是否有 operatie:
push cuvant
push $add
call strcmp
。
还是不行
@VeryNice 考虑将最新版本的代码作为编辑添加到您的问题中。注意不要更改现有问题中的任何内容!在已经存在的内容下方写一个新的 EDIT 部分。
我添加了编辑部分以上是关于如何解决错误:我的代码中出现分段错误(核心转储)? AT&T 语法的主要内容,如果未能解决你的问题,请参考以下文章