装配分段故障恢复
Posted
技术标签:
【中文标题】装配分段故障恢复【英文标题】:Assembly Segmentation Fault Ret 【发布时间】:2021-09-22 14:56:03 【问题描述】:我正在编写一个调用子例程阶乘的程序,然后计算 5 的阶乘。返回后将打印答案。但是,在我尝试运行我的程序后,我得到了一个分段错误。调试器显示错误来自第 41 行,该行仅包含要从子例程返回的 Ret 语句。是什么导致了这种分段错误?
代码如下:
1
2 .data
3 .text
4
5 .global main
6 main:
7 pushq %rbp #prologue
8 mov %rsp, %rbp
9
10 push $5 #push value on stack for it to be operated
11 call factorial
12 mov -4(%rbp), %rdi #move answer into %rdi
13 mov $0, %rax #no vectors
14 call printf
15
16 mov %rbp, %rsp #epilogue
17 popq %rbp
18
19 mov $0, %rdi #exit of the program
20 call exit
21
22 factorial:
23 pushq %rbp #epilogue
24 mov %rsp, %rbp
25
26 mov 8(%rbp), %rdi #move value from line 10 into %rdi
27 mov $1, %rax # move 1 into %rax, here the result will come
28
29 factorial_loop_start:
30 cmpb $1, (%rdi) #compare %rdi to 1 to see if loop ends
31 je factorial_end
32 imul %rdi, %rax #multiply %rdi with %rax and put the answer in %rax
33 dec %rdi #decrement %rdi and return to start of loop
34 jmp factorial_loop_start
35
36 factorial_end:
37 mov %rax, 8(%rbp) #move the result in %rax to the original stack position of
38 #the value that needs to be processed
39 mov %rbp, %rsp #epilogue
40 popq %rbp
41 ret
【问题讨论】:
在调试器中单步验证每条指令是否达到预期效果;跟踪堆栈,以及压入堆栈的内容。call
会压入一个返回地址,所以在调用指令之后,栈顶应该有一个指向第 12 行的指针;还要注意rsp
值。当ret
执行时,同样的rsp
值应该在那里,同样的第12 行代码指针作为堆栈的顶部项目。首先从一个小测试开始,例如 factorial(1)
并确保它有效,然后使用 factorial(2)
进入循环。
您正在使用cmpb
来测试终端值1,但您可能需要cmpl
或cmpq
。此外,由于 5 已按值传递,因此您希望将其直接用作值,而不是用作指针。
【参考方案1】:
如果 ret 崩溃,那么要么是 RSP 错误,要么是它弹出到 RIP 的返回地址是错误的。看看RSP
在ret
执行之前指向的内容,与函数入口处的内容。您的函数会覆盖其返回地址。
由于您已经在使用调试器,请查看 RDI 实际指向的位置。
奇怪的是,您在 64 位代码中在堆栈上传递了一个 arg,看起来您从 32 位代码中复制了帧指针的 8 字节偏移量。
在 64 位代码中,每次推送(包括call
推送的返回地址)为 8 个字节。所以你实际上是从8(%rbp)
加载了你的返回地址,我想。 (然后用mov %rax, 8(%rbp)
覆盖它。)
这就是为什么 cmpb $1, (%rdi)
在您通过将 ()
放在它周围来取消引用 %rdi
以获取 RDI 指向的内存时不会出错的原因。这显然与您使用裸寄存器作为imul
的源操作数的方式不匹配。
如果 RDI 持有你想要的整数,你的代码就会出错。
mov -4(%rbp), %rdi
也是错误的,加载推送的上半部分并重叠到 main 保存的 RBP。如果您修复factorial
中的偏移量,您可以 让该函数更新其堆栈参数,但这是一个奇怪的复杂自定义调用约定。只需让它像普通函数一样返回 %rax
中的值。
我给你正确的代码并不是为你做功课;我故意只是告诉你用调试器在哪里寻找有问题的东西。
【讨论】:
以上是关于装配分段故障恢复的主要内容,如果未能解决你的问题,请参考以下文章