https://zhuanlan.zhihu.com/p/656740329
在 x86-64 架构中,rbp 和 rsp 寄存器分别是栈帧基址指针( Base Pointer )和栈指针( Stack Pointer )。
看完了这篇文章,完全搞懂了函数调用过程中,rbp 和 rsp 的使用情况。
但是还是有一点不太理解,就是假如 CPU 只设计 rsp 可以吗?从我简单来看,好像也是可以够用的啊。我总结一下,无外乎是这些:
返回过程:
那从上面分析来看,只有 rsp ,好像也能完成函数调用的工作啊?
![]() |
1
shawnsh 98 天前 via Android
怎么取参数
|
2
ThirdFlame 98 天前
RSP 可以移动
RBP 是不动的 你去取变量肯定是靠 RBP+偏移来取啊。 靠 RSP 那你还得设计个地方去记录 RSP 的变化,还不如直接设计一个 RBP 呢 |
![]() |
3
amiwrong123 OP @shawnsh #1
你是指,函数调用传参,不是通过寄存器传递;而是通过栈来传递参数的情况呗? |
![]() |
4
luxor 98 天前
gcc: -fomit-frame-pointer msvc: /Oy
|
![]() |
5
shawnsh 98 天前 via Android
@amiwrong123 寄存器才能放多少东西,多次的函数调用不用栈内存保存,你用啥
|
![]() |
6
amiwrong123 OP @shawnsh #1
@ThirdFlame #2 我好像懂你俩的意思了,比如一个函数的汇编里面: sub $0x10, %rsp 之后取局部变量时,都是用-0x10(%rsp)来取 局部变量的。 是这个意思吧? |
![]() |
7
amiwrong123 OP |
![]() |
8
amiwrong123 OP @amiwrong123 #6
这里写了,比如一个函数的汇编里面: sub $0x30, %rsp 之后取局部变量时,都是用-0x10(%rbp)、-0x20(%rbp)、-0x30(%rbp)来取 各个局部变量的。 |
![]() |
9
amiwrong123 OP 这里写错了
|
10
vituralfuture 98 天前 via Android ![]() 不要 rbp 是可行的,只不过追溯函数调用栈变得困难
不要 rbp 的时候,编译器知道每个函数分配的栈帧大小,但是没有保存起来,例如函数返回时需要恢复 rbp ,而 rbp 就保存在当前 rbp 指向的地址,这时 rsp 减去编译器知道的栈帧大小就能得到 rbp call 指令会将 pc(指向下一条指令)和 rbp 压栈,这样子程序能够恢复栈帧,并回到函数调用发生的位置的下一条指令,如果要追溯函数调用栈,只需要拿到 rbp-8 指向的返回地址。读取 rbp 可以使用内联汇编。这就需要知道 rbp 的值,而这个值编译器知道却没有保存 gcc 有个参数-fomit-frame-pointer ,就是省略了 rbp 的使用,但不难以调试程序 |
![]() |
11
adoal 98 天前
别说是没有 BP 可以,就算没有 SP 也不是不可以的
|
![]() |
12
adoal 98 天前 ![]() 从正常的 C 编译成 X86 汇编的代码来看,为了维护栈帧而对 BP 的所有操作,其实都是显式的,所以实际上就是个普通寄存器,只不过出于约定,用 BP 来做这事就是了,没有 BP ,也可以拿其它寄存器用。
SP 就比 BP 特殊了,call/ret/push/pop 等指令要隐式操作 SP……但实质上无非就是移动 SP 和读写数值而已,这些指令的设计是因为对应的操作太常见了,所以就为之做了优化,设计出专用指令,但从指令集完整性的角度完全可以编译器拆开做,SP 的功能也可以由任意通用寄存器来承担,在很多 RISC 设计里就是约定到某个通用寄存器。只有 IP (一般叫 PC 的更多)才是无论如何也不可能通用化的。 |
![]() |
13
ysc3839 98 天前
x86 bp 只是叫 base pointer 吧,跟 stack 无关,用户想怎么使用都可以。sp 是真的 stack pointer ,push pop 等操作会影响 sp 。
|
![]() |
14
amiwrong123 OP @vituralfuture #10
谢谢。这就是我想要的答案。 也就是说,默认编译出来的汇编,每个函数都会去使用 rbp 的。 但是如果加了-fomit-frame-pointer 参数,那么每个函数就不会去使用 rbp 了。 所以结论就是:函数调用过程中,可以不使用 rbp 。 |
![]() |
15
amiwrong123 OP |
![]() |
16
amiwrong123 OP @vituralfuture #10
不过 1 楼那个问题,要是 函数调用时参数是通过栈来传递参数的话(不然可以直接通过寄存器传参),没有 rbp 的话,会不会有点不好办。 不过我想,有 rbp 的话(在刚进入函数时),就是 rbp 减一个数来 获得传参;如果没有 rbp ,那就通过 rsp 加一个数 来获得传参。好像一样能解决问题。 |
![]() |
17
PTLin 98 天前
rbp 主要就是为了 backtrack ,假如完全不需要这个功能就可以取消 push rbp,mov rbp rsp 什么的,在 Linux 内核中完全不需要 ftrace 什么的功能也可以编译成不用 rbp 的。
|
18
sMil3 98 天前 via Android
mips 就是按 sp 找局部变量吧
|
19
iceheart 98 天前 via Android
rbp 是链表指针,
这个链表就是 callstack 链表 |
20
kingly 97 天前
基础不行真的不建议思考这些问题,没啥用,纯浪费精力。你调试的时候看堆栈回溯,没有 rbp 你看个屁啊,栈底上面保存了返回地址,除此之外还有很多其他小用处,比如优化了寻找速度,这都是用处。建议基础没砸实之前不要去产生过多疑问,浪费人生。
|