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



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

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    

我在谷歌上搜索,mov是英特尔风格,movl是AT&T风格。这两种风格是如何结合在一起的?

在这一行:

mov    -0x8(%rbp),%rax

似乎将地址 rbp-0x8 的值移动到注册 rax,对吧? 这个"-0x8(%rbp("是随机值到p吗?

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

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

我不是汇编专家,但代码看起来很清楚,所以我会尝试解释它。


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

(上图(这是编译器在未启用优化时生成的一些"过程"代码。它保存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>

这调用某种早期初始化函数。 它可能使用 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设置为零,并有效地将寄存器rax设置为零。 Windows x64(与每个标准调用约定一样(使用 RAX 作为返回值,因此这是在main底部实现隐式return 0;

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

将指针状态还原到函数调用之前。

最新更新