C语言 在x86_64汇编器中,RBP寄存器的作用是什么?

zpf6vheq  于 2023-11-16  发布在  其他
关注(0)|答案(2)|浏览(155)

我想学一点汇编,因为我在计算机体系结构课上需要它。我写了一些程序,比如打印斐波那契数列。
我认识到,每当我编写一个函数时,我都会使用这三行代码(正如我在比较gcc生成的汇编代码与它的C等效代码时所了解到的那样):

pushq   %rbp
movq    %rsp, %rbp
subq    $16, %rsp

字符串
1.首先,为什么我需要使用%rbp?使用%rsp不是更简单,因为它的内容被移动到第二行的%rbp
1.为什么要从%rsp中减去什么呢,我的意思是,不总是16,当我计算7个或8个变量时,我会减去2428
我在虚拟机(4GB RAM)上使用Manjaro 64位,英特尔64位处理器。

ie3xauqp

ie3xauqp1#

rbp是x86_64上的帧指针。在您生成的代码中,它会获取堆栈指针(rsp)的快照,以便在对rsp进行调整时(即为局部变量保留空间或将push的值添加到堆栈),仍然可以从rbp的常量偏移量访问局部变量和函数参数。
许多编译器提供帧指针省略作为优化选项;这将使生成的汇编代码访问相对于rsp的变量,并释放rbp作为另一个通用寄存器用于函数。
在GCC的情况下,我猜你是从AT&T汇编语法中使用的,那个开关是-fomit-frame-pointer。试着用那个开关编译你的代码,看看你得到了什么汇编代码。你可能会注意到,当访问相对于rsp而不是rbp的值时,指针的偏移量在整个函数中是变化的。

qc6wkl3g

qc6wkl3g2#

Linux使用System V ABI for x86-64(AMD 64)架构;有关详细信息,请参阅OSDev Wiki上的System V ABI。
这意味着堆栈 * 向下增长 *;更小的地址在堆栈中“更高”。

pushq   %rbp        ; Save address of previous stack frame
        movq    %rsp, %rbp  ; Address of current stack frame
        subq    $16, %rsp   ; Reserve 16 bytes for local variables

        ; ... function ...

        movq    %rbp, %rsp  ; \ equivalent to the
        popq    %rbp        ; / 'leave' instruction
        ret

字符串
为局部变量保留的内存量总是16字节的倍数,以保持堆栈对齐为16字节。如果局部变量不需要堆栈空间,则没有subq $16, %rsp或类似指令。
(Note返回地址和前一个推送到堆栈的%rbp都是8字节,总共16字节。
%rbp指向当前堆栈帧时,%rsp指向堆栈顶部。因为编译器知道%rbp%rsp在函数内任何一点的区别,所以可以自由地使用任何一个作为局部变量的基础。
堆栈帧只是局部函数的Playground:当前函数使用的堆栈区域。
当前版本的GCC在使用优化时禁用堆栈帧。这是有道理的,因为对于用C编写的程序,堆栈帧对调试最有用,但其他用处不大。(然而,您可以使用例如-O2 -fno-omit-frame-pointer来保留堆栈帧,同时启用优化。)
虽然相同的ABI适用于所有的二进制文件,无论它们是用什么语言编写的,但某些其他语言确实需要堆栈框架来“展开”(例如,向当前函数的祖先调用者“抛出异常”);即“展开”堆栈框架,一个或多个函数可以被中止,控制权传递给一些祖先函数,而不会在堆栈上留下不需要的东西。
当堆栈帧被省略时--GCC为-fomit-frame-pointer--,函数实现本质上改变为

subq    $8, %rsp    ; Re-align stack frame, and
                            ; reserve memory for local variables

        ; ... function ...

        addq    $8, %rsp
        ret


因为没有堆栈帧(%rbp用于其他用途,它的值从不压入堆栈),每个函数调用只将返回地址压入堆栈,这是一个8字节的量,所以我们需要从%rsp中减去8,以保持它是16的倍数。(一般来说,从%rsp中减去和添加的值是8的奇数倍。)
函数参数通常在寄存器中传递。有关详细信息,请参阅本答案开头的ABI链接,但简而言之,整数类型和指针在寄存器%rdi%rsi%rdx%rcx%r8%r9中传递,浮点参数在%xmm0%xmm7寄存器中传递。
在某些情况下,您会看到rep ret而不是rep。不要混淆:rep ret的意思与ret完全相同; rep前缀,尽管通常用于字符串指令(重复指示),当应用于ret指令时什么也不做。只是某些AMD处理器的分支预测器不喜欢跳到ret指令,推荐的解决方法是使用rep ret
最后,我省略了堆栈顶部上方的red zone(地址小于%rsp的128个字节)。这是因为它对典型函数并不真正有用:在普通的have-stack-frame情况下,你会希望你的本地内容在栈帧内,以使调试成为可能。在omit-stack-frame情况下,堆栈对齐要求已经意味着我们需要从%rsp中减去8,因此在减法中包含局部变量所需的内存是没有成本的。

相关问题