V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
• 请不要在回答技术问题时复制粘贴 AI 生成的内容
amiwrong123
V2EX  ›  程序员

x86 栈的使用有 rbp 和 rsp,只设计 rsp 可不可以?为什么?

  •  
  •   amiwrong123 · 206 天前 · 1905 次点击
    这是一个创建于 206 天前的主题,其中的信息可能已经有所发展或是发生改变。

    https://zhuanlan.zhihu.com/p/656740329

    在 x86-64 架构中,rbp 和 rsp 寄存器分别是栈帧基址指针( Base Pointer )和栈指针( Stack Pointer )。

    看完了这篇文章,完全搞懂了函数调用过程中,rbp 和 rsp 的使用情况。

    但是还是有一点不太理解,就是假如 CPU 只设计 rsp 可以吗?从我简单来看,好像也是可以够用的啊。我总结一下,无外乎是这些:

    • 函数使用过程中,减 rsp ,用来放局部变量
    • 调用函数瞬间,硬件自动减 rsp ,用来放 return Address
    • 下一个函数,继续减 rsp ,用来放局部变量
    • 以此类推

    返回过程:

    • 如果是“减 rsp ,用来放局部变量”,那么直接加 rsp ,就回收了栈空间。
    • 如果是“硬件自动减 rsp ,用来放 return Address”,那么 pop 到 pc 即可( x86 里,ret 指令就是这个效果)

    那从上面分析来看,只有 rsp ,好像也能完成函数调用的工作啊?

    20 条回复    2024-06-02 15:23:15 +08:00
    shawnsh
        1
    shawnsh  
       206 天前 via Android
    怎么取参数
    ThirdFlame
        2
    ThirdFlame  
       206 天前
    RSP 可以移动
    RBP 是不动的

    你去取变量肯定是靠 RBP+偏移来取啊。 靠 RSP 那你还得设计个地方去记录 RSP 的变化,还不如直接设计一个 RBP 呢
    amiwrong123
        3
    amiwrong123  
    OP
       206 天前
    @shawnsh #1
    你是指,函数调用传参,不是通过寄存器传递;而是通过栈来传递参数的情况呗?
    luxor
        4
    luxor  
       206 天前
    gcc: -fomit-frame-pointer msvc: /Oy
    shawnsh
        5
    shawnsh  
       206 天前 via Android
    @amiwrong123 寄存器才能放多少东西,多次的函数调用不用栈内存保存,你用啥
    amiwrong123
        6
    amiwrong123  
    OP
       206 天前
    @shawnsh #1
    @ThirdFlame #2
    我好像懂你俩的意思了,比如一个函数的汇编里面:
    sub $0x10, %rsp
    之后取局部变量时,都是用-0x10(%rsp)来取 局部变量的。

    是这个意思吧?
    amiwrong123
        7
    amiwrong123  
    OP
       206 天前
    @shawnsh #5
    嗯,确实。
    很多时候,可能必须用栈 来传递参数。这个时候,只有 rbp 记录了 上一个函数的栈帧基地址(刚进入下一个函数时),如果没有 rbp 就不好办了还。
    amiwrong123
        8
    amiwrong123  
    OP
       206 天前
    @amiwrong123 #6
    这里写了,比如一个函数的汇编里面:
    sub $0x30, %rsp
    之后取局部变量时,都是用-0x10(%rbp)、-0x20(%rbp)、-0x30(%rbp)来取 各个局部变量的。
    amiwrong123
        9
    amiwrong123  
    OP
       206 天前
    这里写错了
    vituralfuture
        10
    vituralfuture  
       206 天前 via Android   ❤️ 1
    不要 rbp 是可行的,只不过追溯函数调用栈变得困难

    不要 rbp 的时候,编译器知道每个函数分配的栈帧大小,但是没有保存起来,例如函数返回时需要恢复 rbp ,而 rbp 就保存在当前 rbp 指向的地址,这时 rsp 减去编译器知道的栈帧大小就能得到 rbp

    call 指令会将 pc(指向下一条指令)和 rbp 压栈,这样子程序能够恢复栈帧,并回到函数调用发生的位置的下一条指令,如果要追溯函数调用栈,只需要拿到 rbp-8 指向的返回地址。读取 rbp 可以使用内联汇编。这就需要知道 rbp 的值,而这个值编译器知道却没有保存

    gcc 有个参数-fomit-frame-pointer ,就是省略了 rbp 的使用,但不难以调试程序
    adoal
        11
    adoal  
       206 天前
    别说是没有 BP 可以,就算没有 SP 也不是不可以的
    adoal
        12
    adoal  
       206 天前   ❤️ 1
    从正常的 C 编译成 X86 汇编的代码来看,为了维护栈帧而对 BP 的所有操作,其实都是显式的,所以实际上就是个普通寄存器,只不过出于约定,用 BP 来做这事就是了,没有 BP ,也可以拿其它寄存器用。

    SP 就比 BP 特殊了,call/ret/push/pop 等指令要隐式操作 SP……但实质上无非就是移动 SP 和读写数值而已,这些指令的设计是因为对应的操作太常见了,所以就为之做了优化,设计出专用指令,但从指令集完整性的角度完全可以编译器拆开做,SP 的功能也可以由任意通用寄存器来承担,在很多 RISC 设计里就是约定到某个通用寄存器。只有 IP (一般叫 PC 的更多)才是无论如何也不可能通用化的。
    ysc3839
        13
    ysc3839  
       206 天前
    x86 bp 只是叫 base pointer 吧,跟 stack 无关,用户想怎么使用都可以。sp 是真的 stack pointer ,push pop 等操作会影响 sp 。
    amiwrong123
        14
    amiwrong123  
    OP
       206 天前
    @vituralfuture #10
    谢谢。这就是我想要的答案。
    也就是说,默认编译出来的汇编,每个函数都会去使用 rbp 的。
    但是如果加了-fomit-frame-pointer 参数,那么每个函数就不会去使用 rbp 了。

    所以结论就是:函数调用过程中,可以不使用 rbp 。
    amiwrong123
        15
    amiwrong123  
    OP
       206 天前
    @adoal #12
    嗯嗯,你的主要意思是:
    如果没有 BP ,那么用其他 通用寄存器来当 BP 来用,也是一样的。

    在 RISC 里,甚至 SP 都可以用 其他通用寄存器 来代替。
    amiwrong123
        16
    amiwrong123  
    OP
       206 天前
    @vituralfuture #10
    不过 1 楼那个问题,要是 函数调用时参数是通过栈来传递参数的话(不然可以直接通过寄存器传参),没有 rbp 的话,会不会有点不好办。

    不过我想,有 rbp 的话(在刚进入函数时),就是 rbp 减一个数来 获得传参;如果没有 rbp ,那就通过 rsp 加一个数 来获得传参。好像一样能解决问题。
    PTLin
        17
    PTLin  
       206 天前
    rbp 主要就是为了 backtrack ,假如完全不需要这个功能就可以取消 push rbp,mov rbp rsp 什么的,在 Linux 内核中完全不需要 ftrace 什么的功能也可以编译成不用 rbp 的。
    sMil3
        18
    sMil3  
       206 天前 via Android
    mips 就是按 sp 找局部变量吧
    iceheart
        19
    iceheart  
       205 天前 via Android
    rbp 是链表指针,
    这个链表就是 callstack 链表
    kingly
        20
    kingly  
       205 天前
    基础不行真的不建议思考这些问题,没啥用,纯浪费精力。你调试的时候看堆栈回溯,没有 rbp 你看个屁啊,栈底上面保存了返回地址,除此之外还有很多其他小用处,比如优化了寻找速度,这都是用处。建议基础没砸实之前不要去产生过多疑问,浪费人生。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2980 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 22ms · UTC 13:27 · PVG 21:27 · LAX 05:27 · JFK 08:27
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.