【问题标题】:X86 assembly: got segmentation fault after executing "ret" in the main function, why?X86 汇编:在主函数中执行“ret”后出现分段错误,为什么?
【发布时间】:2020-09-06 13:24:47
【问题描述】:
.data
  format: .asciz "%lx, %lx, %lx, %lx\n"
.text
.global main
.extern printf
main:
    movq $6, %rax  # op1
    movq $7, %rbx  # op2
    movq $8, %r8   # id
    movq $9, %r9   # signed
    
    cmpq %rax, %rbx
    jne _fakertn
_fakertn2:
    pushq %rax
    pushq %rbx
    pushq %r8
    pushq %r9
    call _myfunc
    xorq %rax, %rax
    movq %rbp, %rsp
    pop %rbp
    ret

_fakertn:
    jmp _fakertn2
    
_myfunc:
    pushq %rbp
    movq %rsp, %rbp
    leaq format(%rip), %rdi
    movq 16(%rbp), %r8
    movq 24(%rbp), %rcx
    movq 32(%rbp), %rdx
    movq 40(%rbp), %rsi
    xorq %rax, %rax
    call printf          
    movq %rbp, %rsp
    pop %rbp
    movq $0, %rax
    ret

我使用以下命令编译:gcc test.s -g -no-pie -o test

这是标准输出:

6、7、8、9

分段错误(核心转储)

在main函数中执行ret后出现分段错误。

我在这上面花了 5 个小时,仍然找不到原因。请帮助确定问题。谢谢!

【问题讨论】:

  • 对于像这样的小程序,最好的调试方法是一次单步执行一条指令,并在每条指令之后检查寄存器和内存以确认它们是否符合您的预期他们要包含。在某些时候,您的期望会与现实背道而驰。这就是错误所在。

标签: assembly gcc x86


【解决方案1】:

_myfunc中查看您的序言:

_myfunc:
    pushq %rbp
    movq %rsp, %rbp

还有后序,它“取消”序言:

    movq %rbp, %rsp
    pop %rbp
    movq $0, %rax
    ret

现在检查_main的后序:

    movq %rbp, %rsp
    pop %rbp
    ret

_main 的序言在哪里?

您将返回地址弹出到rbp - 假设rbp 包含您在上一条指令中想要的内容!

[编辑]

@RaymondChen 指出您正在将参数推送到堆栈上,但没有明确清理它们。这是一种不好的做法 - 但是(幸运的是?)您不会受此影响,因为无论如何堆栈在调用后立即“修复”。

当您将参数压入堆栈时,必须再次将其取出。只有两种方法可以做到这一点:在函数返回时由函数本身完成,或者在返回后由调用者完成。

例如,如果您将四个整数压入堆栈,然后调用一个函数,该函数可以执行ret $4*8 来弹出这四个八字节寄存器。您的 _myfunc 函数可以做到这一点。

当被调用函数不知道推送了多少参数时使用另一种替代方法,例如printf() 不知道。您的示例使用寄存器而不是堆栈作为 printf() 的参数,但有时会使用堆栈 - 特别是在有很多参数的情况下!如果printf() 的四个参数被push 编入堆栈,则printf 不能执行ret $4*8,因为它不知道有多少。因此,调用者(您的_myfunc)应该在返回后直接将$4 * 8 添加到rsp

就像我说的那样,下一条指令是“将rbp 移动到rsp”,所以添加(严格)是不必要的 - 但还是很好的做法!

【讨论】:

  • _fakertn2处还有四个不匹配的推送指令。
  • @RaymondChen 您好 Raymond,感谢您发现问题。我是组装新手。您能否更具体地介绍一下无与伦比的推送实例?
  • @syacer 您压入堆栈的所有内容都必须在返回之前从堆栈中弹出。你不这样做,所以从函数返回失败。回想一下,返回指令只是弹出堆栈的返回地址。如果堆栈上还有其他垃圾,ret 会将其弹出并尝试返回它找到的任何地址,通常会导致崩溃。
  • @syacer 请检查我对答案所做的编辑。 fuz 通常是正确的,但实际上不是我解释的你的情况
  • @JohnBurger 感谢您的详细回答。我完全同意这两种方法并学到了很多东西。但是我还有一个疑问:“movq %rbp, %rsp”的目的不是要从堆栈元素中弹出吗?我很困惑为什么这种方法是一种不好的做法?谢谢!或者提供一个案例说明这种方法可能会导致问题
猜你喜欢
  • 1970-01-01
  • 2021-02-25
  • 1970-01-01
  • 2020-03-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多