为什么%rbp指向没有?
已知%rsp指向堆栈帧的顶部,并且指向堆栈帧的基址。然后,我不明白为什么RBP%为0x0在这段代码:为什么%rbp指向没有?
(gdb) x/4xg $rsp
0x7fffffffe170: 0x00000000004000dc 0x0000000000000010
0x7fffffffe180: 0x0000000000000001 0x00007fffffffe487
(gdb) disas HelloWorldProc
Dump of assembler code for function HelloWorldProc:
=> 0x00000000004000b0 <+0>: push %rbp
0x00000000004000b1 <+1>: mov %rsp,%rbp
0x00000000004000b4 <+4>: mov $0x1,%eax
0x00000000004000b9 <+9>: mov $0x1,%edi
0x00000000004000be <+14>: movabs $0x6000ec,%rsi
0x00000000004000c8 <+24>: mov $0xd,%edx
0x00000000004000cd <+29>: syscall
0x00000000004000cf <+31>: leaveq
0x00000000004000d0 <+32>: retq
End of assembler dump.
(gdb) x/xg $rbp
0x0: Cannot access memory at address 0x0
,为什么它很“节约”(推)%RBP堆栈,如果它指向什么?
RBP
是一个通用寄存器,因此它可以包含你(或你的编译器),希望它包含任何价值。按惯例只有,RBP
用来指向程序框架。根据该约定,堆栈看起来是这样的:
Low |====================|
addresses | Unused space |
| |
|====================| ← RSP points here
↑ | Function's |
↑ | local variables |
↑ | | ↑ RBP - x
direction |--------------------| ← RBP points here
of stack | Original/saved RBP | ↓ RBP + x
growth |--------------------|
↑ | Return pointer |
↑ |--------------------|
↑ | Function's |
| parameters |
| |
|====================|
| Parent |
| function's data |
|====================|
| Grandparent |
High | function's data |
addresses |====================|
因此,对于一个功能样板序幕代码:
push %rbp
mov %rsp, %rbp
这第一条指令通过推动节约的RBP
原值将其装入堆栈,然后第二条指令将RBP
设置为原始值RSP
。在此之后,堆叠看起来完全像上面描述的那样,在漂亮的ASCII艺术中。
然后函数就执行,在执行任何代码就是了执行。如附图中建议,可以通过使用积极偏移从RBP
(即,RBP+x
)访问它是在栈中传递的任何参数,并可以通过使用访问任何局部变量分配给它的空间在堆栈上从RBP
负偏移(即,RBP-x
)。如果你明白内存中的堆栈增长为,则(地址变小),那么这种抵消方案是有意义的。
最后,样板尾声代码结束的功能是:
leaveq
或者,等同地:
mov %rbp, %rsp
pop %rbp
该第一指令集RSP
到的RBP
值(工作值在整个函数的代码中使用),并且第二条指令将“原始/保存的RBP”从堆栈中弹出,转换为RBP
。这不是巧合,这恰恰是与相对的,我们在上面看到的序言代码中做了什么。
但是请注意,这只是一个约定。除非ABI要求,否则编译器可以自由使用RBP
作为通用寄存器,与堆栈指针无关。这是可行的,因为编译器可以在编译时从RSP
计算所需的偏移量,这是一种常见的优化,称为“帧指针省略”(或“帧指针省略”)。它是在32位模式下,如果可用的通用寄存器的数量是非常小尤其常见,但有时候你会看到它在64位代码,太。当编译器忽略帧指针时,它不需要序言和尾声代码来操纵它,所以这也可以省略。
你看到这一切帧指针簿记的原因是因为你分析未优化代码,其中帧指针永远不会省略掉,因为有它周围往往使得调试更容易(并且由于执行速度是不一个重大关切)。
进入你的功能后,RBP
为0的原因似乎是a peculiarity of GDB,而不是你真正需要关注的东西。由于注释中的Shift_Left注释,Linux下的GDB在将控制权交给应用程序之前,将所有寄存器(RSP
除外)预初始化为0。如果你在调试器外运行这个程序,并简单地印刷的RBP
初始值到标准输出,你会看到,这将是非零。
但是,再说一次,确切的价值不应该的问题给你。了解上述调用堆栈的示意图是关键。假设帧指针没有被省略掉,编译器时,它产生的序幕和尾声代码什么值RBP
将有入境时,因为它不知道在哪里调用堆栈上的功能将最终被称为不知道。
分享您的原代码 –
其实,RSP指向栈帧的顶部,但程序框架和RBP指向程序框架顶部的底部。 Linux下的GDB开始将除RSP之外的所有寄存器设置为NULL的应用程序。跟踪到4000B4然后'x/xg $ rsp'将会工作。 –
@Shift_Left你是对的。但要成为事实,我无法理解这种行为的原因。如果系统需要知道堆栈的范围/大小,那么%rbp从零开始的可能性如何?你不同意在程序开始时不知道堆栈帧的起始位置吗? – alacerda