我有这个程序集编译得很好但在恢复时会出现分段错误。有人可以验证它。这适用于x86_64架构
save_context:
mov %rdi,%rax /* Get our context pointer */
/* Don't need to save A */
mov %rbx, 16(%rax) /* Save B */
mov %r12, 24(%rax) /* Save r12 */
mov %r13, 32(%rax) /* Save r13 (8*3+16)*/
mov %r14, 40(%rax) /* Save r13 */
mov %r15, 48(%rax) /* Save r13 */
mov %rbp, 56(%rax) /* Save frame pointer */
mov %rsp, 64(%rax) /* Save stack pointer */
mov (%rsp), %rdx /* Fetch our return address */
mov %rdx, (%rax) /* Save our return address */
xor %rax,%rax /* Construct return code of 1 */
inc %rax
ret
恢复就像这样
restore_context:
mov %rdi,%rax /* Get our context pointer */
mov 64(%rax), %rsp /* Restore stack pointer */
mov (%rax), %rdx /* Fetch our return address */
mov %rdx, (%rsp)
mov 16(%rax),%rbx /* Restore B */
mov 24(%rax), %r12 /* Restore r12 */
mov 32(%rax), %r13 /* Restore r13 */
mov 40(%rax), %r14 /* Restore r14 */
mov 48(%rax), %r15 /* Restore r15 */
mov 56(%rax), %rbp /* Restore frame pointer */
xor %rax,%rax /* Return 0 */
ret
当我使用gdb来调试函数时,我得到了这个。在分段错误之后。
0x0000000000424c4c <+0>: mov %rdi,%rax
0x0000000000424c4f <+3>: mov 0x18(%rax),%rsp
0x0000000000424c53 <+7>: mov (%rax),%rbx
=> 0x0000000000424c56 <+10>: mov %rbx,(%rsp)
0x0000000000424c5a <+14>: mov 0x10(%rax),%rbx
0x0000000000424c5e <+18>: mov 0x20(%rax),%rbp
0x0000000000424c62 <+22>: mov 0x28(%rax),%r12
0x0000000000424c66 <+26>: mov 0x30(%rax),%r13
0x0000000000424c6a <+30>: mov 0x38(%rax),%r14
0x0000000000424c6e <+34>: mov 0x40(%rax),%r15
0x0000000000424c72 <+38>: xor %rax,%rax
0x0000000000424c75 <+41>: retq
0x0000000000424c76 <+42>: nop
0x0000000000424c77 <+43>: nop
反汇编save_context
0x0000000000424c1c <+0>: mov %rdi,%rax
0x0000000000424c1f <+3>: mov %rbx,0x10(%rax)
0x0000000000424c23 <+7>: mov %rsp,0x18(%rax)
0x0000000000424c27 <+11>: mov %rbp,0x20(%rax)
0x0000000000424c2b <+15>: mov %r12,0x28(%rax)
0x0000000000424c2f <+19>: mov %r13,0x30(%rax)
0x0000000000424c33 <+23>: mov %r14,0x38(%rax)
0x0000000000424c37 <+27>: mov %r15,0x40(%rax)
0x0000000000424c3b <+31>: mov (%rsp),%rdx
0x0000000000424c3f <+35>: mov %rdx,(%rax)
0x0000000000424c42 <+38>: xor %rax,%rax
0x0000000000424c45 <+41>: inc %rax
0x0000000000424c48 <+44>: retq
0x0000000000424c49 <+45>: nopl (%rax)
有关上下文的更多信息
save_context(上下文) context = {4243415,0,0,4242944,140737488348624,0,0,140737488348368,140737488348312,0}
restore_context(new_context) new_context = {4249788,0,0,0,0,0,6719200,6719184,0,0}
恢复上下文后失败。我尝试了save_context,然后是restore_context。这样可行。只是检查64bit的上下文和新上下文是否有问题?!?!
这是32位版本
save_context:
movl 4(%esp),%eax /* Get our context pointer */
/* Don't need to save A */
movl %ebx, 12(%eax) /* Save B */
movl %esi, 16(%eax) /* Save SI */
movl %edi, 20(%eax) /* Save DI */
movl %ebp, 24(%eax) /* Save frame pointer */
movl %esp, 28(%eax) /* Save stack pointer */
movl 0(%esp), %edx /* Fetch our return address */
movl %edx, 0(%eax) /* Save our return address */
xorl %eax,%eax /* Construct return code of 1 */
incl %eax
ret
恢复上下文:
restore_context:
movl 4(%esp),%eax /* Get our context pointer */
movl 28(%eax), %esp /* Restore stack pointer */
movl 0(%eax),%edx /* Get our return address */
movl %edx, 0(%esp) /* Put it on the stack in the right
spot. */
movl 12(%eax),%ebx /* Restore B */
movl 16(%eax), %esi /* Restore SI */
movl 20(%eax), %edi /* Restore DI */
movl 24(%eax), %ebp /* Restore frame pointer */
xorl %eax,%eax /* Return 0 */
ret
知道如何解决这个问题吗?
答案 0 :(得分:4)
首先,我将保存和恢复并排放置(并恢复恢复指令),以便我可以看到偏移是否错误。从那个角度来看,它看起来很不错。
save_context: restore_context:
mov %rdi,%rax mov %rdi,%rax
mov %rbx, 16(%rax) mov 16(%rax),%rbx
mov %r12, 24(%rax) mov 24(%rax), %r12
mov %r13, 32(%rax) mov 32(%rax), %r13
mov %r14, 64(%rax) mov 64(%rax), %r14
mov %r15, 48(%rax) mov 48(%rax), %r15
mov %rbp, 56(%rax) mov 56(%rax), %rbp
mov %rsp, 64(%rax) mov 64(%rax), %rsp
mov %rdx, (%rsp) mov (%rsp), %rdx
mov %rdx, (%rax) mov (%rax), %rdx
xor %rax,%rax xor %rax,%rax
inc %rax
ret ret
到目前为止一切顺利。
但是,现在我想看看上述偏移量:
16
24
32
64 <-- why 64 here?
48
56
64 <-- Oops 64 again?
我认为那是罪魁祸首。您可以在同一地址保存两个寄存器。
现在奇怪的是在gdb输出中它看起来是正确的。所以我猜你没有告诉我们你原来的来源。
为避免此类问题,通常最好使用定义不同偏移的定义指令来定义结构。
否则,你能不能再描述一下你的背景了。这看起来不像你可以用OS运行的代码,但你提到了gdb,所以听起来你会在Linux上运行。我可以想到两种可能性:在恢复发生之前你的上下文被覆盖,保存后的堆栈发生了很大的变化(我想象),当你恢复时你最终会得到一个完全不同的堆栈,但是你还原{{1现在是什么&#34;随机数据&#34;就堆栈而言。所以ret应该工作(你恢复那么多)但在那之后......谁知道!
更新评论中的问题:
1)是的我在Linux上运行,因为32版本工作正常,我猜这将是这样的......我错误地认为??
阿。可能是堆栈的管理方式不同,但如果你自己调用你的函数,那么这应该不重要。你还修改了可能不被允许的rdx吗?
2)我看到你在代码中被覆盖的上下文意味着什么。所以我尝试在彼此之后立即进行保存和恢复。它仍然给我分段错误,这意味着我的汇编代码出错了。
实际上,您的代码看起来像这样:
%esp
当您从保存功能返回时,您保存的返回地址就在 MOV ..., %rdi
CALL save
TEST %rax
JNE done
[...do things, but no RET...]
MOV ..., %rdi
CALL restore
---not reached---
done:
RET
之后,这就是您CALL save
为0或1的原因所以您知道是否从保存返回或恢复。到目前为止,我没有看到任何问题。
我能想到的一件事就是%rdi在保存和恢复调用之间发生了变化,但是如果你改变你的代码只做那两个调用,我会想到情况并非如此。是否在您的问题中指出%rax
的SEGV?或者它之前是SEGV吗?
3)如何调试此类错误?
我会使用=>
进行跟踪,并验证您在每条指令上发生的确切结果。看到保存的堆栈指针是您在还原中返回的堆栈指针,并且看到返回地址是正确的。
每次运行命令时,GDB都可用于stepi
一组条目。因此,如果您这样做,然后print
+在此之后输入任意数量的时间,您应该看到每个步骤的信息,看看它何时出错。 (即在%rdi打印寄存器和数据?)