已知%rsp指向堆栈帧的顶部,%rbp指向堆栈帧的底部。那么我不明白为什么在这段代码中%rbp是0x0:
(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_在注释中所说,linux下的gdb在将控制权交给应用程序之前,会将所有寄存器(除了RSP
)预初始化为0。如果您在调试器之外运行这个程序,并简单地将RBP
的初始值打印到stdout,您将看到它将是非零的。但是,准确的价值对你来说不重要。理解上面调用堆栈的示意图是关键。假设帧指针没有被省略,编译器不知道何时生成序言和结尾代码,因为它不知道函数在调用堆栈上的何处被调用,所以在输入时
RBP
值会是什么。关于linux - 为什么%rbp指向什么?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/44687662/