【问题标题】:Need help verifying assembly code需要帮助验证汇编代码
【发布时间】:2014-07-10 19:03:09
【问题描述】:

我有这个程序集,它编译得很好,但在恢复时会出现分段错误。有人可以验证吗?这是针对 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

解除保存上下文

  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(上下文) 上下文 = {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。这样可行。只是检查 64 位的上下文和新上下文是否有问题?!?!

这里是 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

知道如何解决这个问题吗?

【问题讨论】:

  • 返回地址不在8(%rbp),你还没有设置rbp指向任何地方(除非它是由调用者隐式设置的)。
  • 返回地址为(%rsp)。如果您使用push %rbp; mov %rsp, %rbp 的遗留函数序言,那么同样的位置确实也可以作为8(%rbp) 寻址。
  • 是的,看起来应该可以了。
  • 还有rax ;) 来吧,你在评论中做得对了一点......
  • 反汇编与源不匹配,您从0x18(%rdx) 加载rsp。确保这是正确的。

标签: assembly gdb segmentation-fault x86-64 cpu-registers


【解决方案1】:

首先,我将保存和恢复并排放置(并随机播放恢复指令),以便查看偏移量是否有问题。从这个角度来看,它看起来不错。

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 输出中它看起来是正确的。所以我猜你没有向我们展示你的原始来源。

为避免此类问题,通常最好使用定义不同偏移量的定义指令来定义结构。

否则,您能否再描述一下您的上下文。这看起来不像可以在操作系统上运行的代码,但你提到了 gdb,所以听起来你会在 Linux 中运行。我能想到两种可能性:您的上下文在恢复之前被覆盖,保存后的堆栈发生了很大变化(我想),当您恢复时,您最终会得到一个完全不同的堆栈,但您恢复 %esp就堆栈而言,现在是“随机数据”。所以 ret 应该可以工作(你恢复了那么多),但在那之后......谁知道呢!


从评论中的问题更新:

1) 是的,我在 linux 上运行,因为它的 32 版本运行良好,我猜这也会如此.. 我认为这是错误的吗??

啊。可能是堆栈的管理方式不同,但是如果您自己调用函数,那么在这里应该没关系。您还修改了可能不允许的rdx?

2) 我明白上下文在某处的代码中被覆盖是什么意思。所以我尝试将保存和恢复放在一起。它仍然给我分段错误,这意味着我的汇编代码有问题。

实际上,您的代码如下所示:

  MOV ..., %rdi
  CALL save
  TEST %rax
  JNE done
  [...do things, but no RET...]
  MOV ..., %rdi
  CALL restore
  ---not reached---
done:
  RET

当您从保存功能返回时,您保存的返回地址就在CALL save 之后,这就是为什么您将%rax 设置为 0 或 1,以便您知道您是从保存返回还是从还原返回。到这里为止,我没有发现任何问题。

我能想到的一件事是 %rdi 在 save 和 restore 调用之间发生了变化,但是如果您将代码更改为只执行这两个调用,我想情况并非如此。在您的问题中指出=&gt; 的位置是否是SEGV?还是会先SEGV上线?

3) 如何调试此类错误?

我将使用stepi 进行跟踪,并验证您所期望的每条指令都会发生什么。看到保存的栈指针就是你restore中取回来的那个,看看返回地址是否正确。

每次运行命令时,GDB 可用于print 一组条目。因此,如果您这样做,然后stepi + 在此之后输入任意数量的时间,您应该在每个步骤中看到您的信息,并查看它何时出错。 (即在 %rdi 打印寄存器和数据?)

【讨论】:

  • 感谢您的意见。是的,我更改了源代码并忘记在此处进行更改。正如您所指出的,GDB 输出是最新的。几个问题!! 1) 是的,我在 linux 上运行,因为它的 32 版本运行良好,我猜这也会如此.. 我假设错了吗?? 2)我明白你所说的上下文在代码中被覆盖是什么意思 somehwere 。所以我尝试将保存和恢复放在一个之后。它仍然给我分段错误,这意味着我的汇编代码有问题。 3) 如何调试此类错误?
  • (即在 %rdi 打印寄存器和数据?).. 我试过这个 .. 是的,确实 rdi 的值发生了变化。但是对于 i386(32 位)也会发生这种情况 :: 我确实更改了输入参数 ..so 在 32 位案例 4(esp)中更改以进行保存和恢复。这两个输入是我的问题(context 和 new_context)。那为什么它适用于32位呢?我想切换到新的上下文,可以吗? mov 4(esp) eax */ * 切换到新的上下文。这不会返回。 */ cpu_restore_context (new_context); **
  • 您没有向我们展示您的 32 位版本,但是如果 rdi 没有指向完全相同的位置,它如何以相同的方式恢复事物,因为您对 save 和restore?! mov %rsp, 64(%rax) 必须与mov 64(%rax), %rsp 1 比 1 对应,对吧?
  • 实际上如果4(%esp) 永远不会改变,那么你在保存和恢复中的%eax 中有相同的指针。所以这是你的罪魁祸首。 %rdi 需要在您的 64 位版本中保持不变。也许您可以在调用save 之前将其压入堆栈,然后从8(%rsp) 检索它而不是使用%rdi?
  • 如果调用 restore 时堆栈指针不一样,那么它也不起作用。如果在调用还原时堆栈更负,那么将%rdi 保存在堆栈上将毫无用处。所以我不太确定你怎么能做到这一点。尤其是没有看到更多你的代码在两者之间做了什么。这可能是很多代码!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-08-26
  • 2010-10-21
  • 2011-11-10
  • 1970-01-01
  • 2018-09-30
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多