了解取消引用未初始化指针的C的未优化asm会导致分段错误

时间:2018-05-25 02:00:54

标签: c pointers gcc assembly

我设置了一个实验,看它是否有效

int *p;
p[0] = 3;

我的想法是编译器给p一个随机值,我可以把它当作一个数组。

但事实证明分段错误并且我不理解汇编代码。

   0x0000000000401530 <+0>: push   %rbp
   0x0000000000401531 <+1>: mov    %rsp,%rbp
   0x0000000000401534 <+4>: sub    $0x30,%rsp
   0x0000000000401538 <+8>: mov    %ecx,0x10(%rbp)
   0x000000000040153b <+11>:    mov    %rdx,0x18(%rbp)
   0x000000000040153f <+15>:    callq  0x402170 <__main>
   0x0000000000401544 <+20>:    mov    -0x8(%rbp),%rax
=> 0x0000000000401548 <+24>:    movl   $0x3,(%rax)
   0x000000000040154e <+30>:    mov    $0x0,%eax
   0x0000000000401553 <+35>:    add    $0x30,%rsp
   0x0000000000401557 <+39>:    pop    %rbp
   0x0000000000401558 <+40>:    retq    

我在google上搜索过,mov是Intel风格,movl是AT&amp; T风格。为什么这两种风格结合在一起?

在这一行:

mov    -0x8(%rbp),%rax

似乎将地址rbp-0x8的值移动到寄存器rax,对吧? 这是“-0x8(%rbp)”随机值是否为p?

我认为%rax不是p,因为在下一行CPU给%rax提供$ 0x3。似乎%rax是数组的第一个内存。

如何解释此汇编代码?谢谢。

1 个答案:

答案 0 :(得分:3)

我不是集会专家,但代码看起来非常清楚,所以我试着解释它。

   0x0000000000401530 <+0>: push   %rbp
   0x0000000000401531 <+1>: mov    %rsp,%rbp

(上图)这是一些&#34;程序&#34;未打开优化时编译器生成的代码。它保存了RSP(64位堆栈指针)的状态。

   0x0000000000401534 <+4>: sub    $0x30,%rsp

这为局部变量保留了堆栈上的额外空间。

   0x0000000000401538 <+8>: mov    %ecx,0x10(%rbp)
   0x000000000040153b <+11>:    mov    %rdx,0x18(%rbp)

这会将argc和argv保存到堆栈中,因为您进行了调试构建,因此所有变量都必须在内存中。 (它使用的空间高于返回地址,其中main的调用者保留空间。这称为阴影空间,是Windows x64调用约定的一个特性。)

   0x000000000040153f <+15>:    callq  0x402170 <__main>

这会调用某种早期的init函数。它可能使用argc和argv(仍然在寄存器中),或者它可能不会;我们无法从代码中辨别出来。

   0x0000000000401544 <+20>:    mov    -0x8(%rbp),%rax

这会将未初始化的堆栈内存加载为int *p的值。自动变量放在堆栈上。你没有先编写p就读了它,而编译器只是读取了它为int *p;所选择的堆栈槽中已经存在的任何垃圾或零

=> 0x0000000000401548 <+24>:    movl   $0x3,(%rax)

此行将rax指向的地址设置为立即值3.

p的值在rax中,因此这是您的p[0] = 3;,取消引用任何垃圾p。你崩溃是因为它并没有指向可写内存。 (在内存中覆盖一些随机dword几乎不会更好,但至少你的代码不会在这里崩溃,如果p的垃圾值发生的话,可能会在某个时间点崩溃成为一个有效的指针。)

   0x000000000040154e <+30>:    mov    $0x0,%eax

这将寄存器eax设置为零,effectively setting rax to zero, too。 Windows x64(与每个标准调用约定一样)使用RAX作为返回值,因此这是在return 0;底部实现隐式main

   0x0000000000401553 <+35>:    add    $0x30,%rsp
   0x0000000000401557 <+39>:    pop    %rbp
   0x0000000000401558 <+40>:    retq

将指针状态恢复到函数调用之前。