2017-06-21 67 views
0

已知%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堆栈,如果它指向什么?

+0

分享您的原代码 –

+2

其实,RSP指向栈帧的顶部,但程序框架和RBP指向程序框架顶部的底部。 Linux下的GDB开始将除RSP之外的所有寄存器设置为NULL的应用程序。跟踪到4000B4然后'x/xg $ rsp'将会工作。 –

+0

@Shift_Left你是对的。但要成为事实,我无法理解这种行为的原因。如果系统需要知道堆栈的范围/大小,那么%rbp从零开始的可能性如何?你不同意在程序开始时不知道堆栈帧的起始位置吗? – alacerda

回答

4

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艺术中。

然后函数就执行,在执行任何代码就是了执行。如附图中建议,可以通过使用积极偏移从RBPRBP+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将有入境时,因为它不知道在哪里调用堆栈上的功能将最终被称为不知道。

相关问题