我设置了一个实验,看它是否有效
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是数组的第一个内存。
如何解释此汇编代码?谢谢。
答案 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
将指针状态恢复到函数调用之前。