Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/assembly/6.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Linux 为什么%rbp没有指向任何东西?_Linux_Assembly_Gdb_X86 64_Stack Pointer - Fatal编程技术网

Linux 为什么%rbp没有指向任何东西?

Linux 为什么%rbp没有指向任何东西?,linux,assembly,gdb,x86-64,stack-pointer,Linux,Assembly,Gdb,X86 64,Stack Pointer,众所周知,%rsp指向堆栈框架的顶部,%rbp指向堆栈框架的底部。然后我不明白为什么在这段代码中%rbp是0x0: (gdb) x/4xg $rsp 0x7fffffffe170: 0x00000000004000dc 0x0000000000000010 0x7fffffffe180: 0x0000000000000001 0x00007fffffffe487 (gdb) disas HelloWorldProc Dump of assembler code for function H

众所周知,%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
(gdb)x/4xg$rsp
0x7FFFFFE170:0x00000000004000dc 0x0000000000000010
0x7FFFFFE180:0x0000000000000001 0x00007FFFFFE487
(gdb)disas HelloWorldProc
函数HelloWorldProc的汇编程序代码转储:
=>0x00000000004000b0:推送%rbp
0x00000000004000b1:mov%rsp,%rbp
0x00000000004000b4:mov$0x1,%eax
0x00000000004000b9:mov$0x1,%edi
0x00000000004000be:movabs$0x6000ec,%rsi
0x00000000004000c8:mov$0xd,%edx
0x00000000004000cd:syscall
0x00000000004000cf:leaveq
0x00000000004000d0:retq
汇编程序转储结束。
(gdb)x/xg$rbp
0x0:无法访问地址0x0处的内存

如果rbp不指向任何内容,为什么要将其“保存”(推入)%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
leaveq
第一条指令将
RBP
的原始值推送到堆栈上保存,然后第二条指令将
RBP
设置为
RSP
的原始值。在这之后,堆栈看起来与上面描述的完全一样,采用了漂亮的ASCII艺术

然后,函数执行它自己的事情,执行它想要执行的任何代码。如图所示,它可以通过使用
RBP
(即
RBP+x
)的正偏移量访问它在堆栈上传递的任何参数,并且可以通过使用
RBP
(即
RBP-x
)的负偏移量访问它在堆栈上分配空间的任何局部变量。如果您理解堆栈在内存中向下增长(地址变小),那么这种偏移方案是有意义的

最后,结束函数的样板结束代码是:

push   %rbp
mov    %rsp, %rbp
leaveq
或者,相当于:

mov %rbp, %rsp
pop %rbp
第一条指令将
RSP
设置为
RBP
(整个函数代码中使用的工作值),第二条指令将“原始/保存的RBP”从堆栈中弹出,放入
RBP
。这与我们在上面看到的序言代码中所做的恰恰相反,这并非巧合

但请注意,这只是一个惯例。除非ABI要求,否则编译器可以自由使用
RBP
作为通用寄存器,与堆栈指针无关。这是因为编译器可以在编译时计算从
RSP
所需的偏移量,这是一种常见的优化,称为“帧指针省略”(或“帧指针省略”)。它在32位模式下尤其常见,在32位模式下,可用的通用寄存器数量非常少,但有时您也会在64位代码中看到它。当编译器省略了帧指针时,它不需要序言和尾声代码来操作它,因此这也可以省略

您看到所有这些帧指针簿记的原因是因为您正在分析未优化的代码,其中帧指针永远不会被忽略,因为有它在身边通常会使调试更容易(而且因为执行速度不是一个重要的问题)

进入函数时,RBP
RBP
为0的原因似乎是,而不是您真正需要关心的问题。正如Shift_在注释中留下的注释,Linux下的GDB在将控制权移交给应用程序之前将所有寄存器(RSP除外)预初始化为0。如果您在调试器之外运行此程序,并且只需将
RBP
的初始值打印到stdout,您就会看到它将是非零的


但是,同样,准确的数值对你来说并不重要。理解上面调用堆栈的示意图是关键。假设未省略帧指针,编译器不知道何时生成序言和尾声代码,因为它不知道函数将在调用堆栈的何处被调用。

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
leaveq
第一条指令将
RBP
的原始值推送到堆栈上保存,然后第二条指令将
RBP
设置为
RSP
的原始值。在这之后,堆栈看起来与上面描述的完全一样,采用了漂亮的ASCII艺术

然后,函数执行它自己的事情,执行它想要执行的任何代码。如图所示,它可以通过使用
RBP
(即
RBP+x
)的正偏移量访问它在堆栈上传递的任何参数,并且可以通过使用
RBP
(即
RBP-x
)的负偏移量访问它在堆栈上分配空间的任何局部变量。如果您理解堆栈在内存中向下增长(地址变小),那么这种偏移方案是有意义的

最后,结束函数的样板结束代码是:

push   %rbp
mov    %rsp, %rbp
leaveq
或者,相当于:

mov %rbp, %rsp
pop %rbp
第一条指令将
RSP
设置为
RBP
(整个函数代码中使用的工作值),第二条指令将“原始/保存的RBP”从堆栈中弹出,放入
RBP
。这并非巧合