x86_64汇编程序中RBP寄存器的用途是什么?

45 c assembly x86-64 stack-pointer

所以我正在尝试学习一点装配,因为我需要它用于计算机体系结构类.我写了一些程序,比如打印Fibonacci序列.

我认识到每当我编写程序时,我都会使用这3行(正如我从比较生成的汇编代码中学gcc到的那样C):

pushq   %rbp
movq    %rsp, %rbp
subq    $16, %rsp
Run Code Online (Sandbox Code Playgroud)

我有2个问题:

  1. 首先,我为什么需要使用%rbp?使用起来不是很简单%rsp,因为它的内容被移到%rbp了第二行吗?
  2. 为什么我要从中减去任何东西%rsp?我的意思是并非总是16(当我在printf第7行或第8行变量时,我会减去24或减去28

我在虚拟机(4 GB RAM),Intel 64位处理器上使用Manjaro 64位

Gov*_*mar 41

rbp是x86_64上的帧指针.在生成的代码中,它获取堆栈指针(rsp)的快照,以便在进行调整时rsp(即为局部变量或push堆栈上的值保留空间),仍可从常量偏移量访问局部变量和函数参数来自rbp.

许多编译器提供帧指针省略作为优化选项; 这将使生成的汇编代码相对于rsp相反的访问变量,并释放rbp为另一个用于函数的通用寄存器.

在GCC的情况下,我猜测你正在使用AT&T汇编语法,那个开关是-fomit-frame-pointer.尝试使用该开关编译代码并查看您获得的汇编代码.您可能会注意到,当访问相对于rsp而不是的值时rbp,指针的偏移在整个函数中变化.

  • @FrynioS编译器会在函数enter上为本地值分配一些空间。这就是为什么它在输入时从%rsp减去值的原因。这与%rbp是否用作帧指针无关。此后,此位置以%rsp的正偏移量使用。同样,如果此函数调用另一个函数,则每次调用时%rsp应在16字节边界上对齐,因此,在这种情况下,编译器应在每次输入时从%rsp中减去8。 (2认同)

Nom*_*mal 39

Linux使用System V ABI for x86-64(AMD64)架构; 有关详细信息,请参阅OSDev Wiki上的System V ABI.

这意味着堆栈会逐渐减少 ; 较小的地址在堆栈中"更高".典型的C函数编译为

        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
Run Code Online (Sandbox Code Playgroud)

为局部变量保留的内存量始终是16个字节的倍数,以使堆栈保持对齐为16个字节.如果局部变量不需要堆栈空间,则没有subq $16, %rsp或类似的指令.

(注意,返回地址和前面%rbp推送到堆栈的大小都是8个字节,总共16个字节.)

%rbp指向当前堆栈帧,%rsp指向堆栈的顶部.因为编译器知道之间的差别%rbp,并%rsp在函数中的任何一点上,它是免费使用的任何一个作为基地的局部变量.

堆栈框架只是本地函数的操场:当前函数使用的堆栈区域.

每当使用优化时,当前版本的GCC都会禁用堆栈帧.这是有道理的,因为对于用C编写的程序,堆栈帧对调试最有用,但不是很多.(但是,您可以使用eg -O2 -fno-omit-frame-pointer来保持堆栈帧,否则启用优化.)

虽然相同的ABI适用于所有二进制文件,但无论它们是用什么语言编写的,某些其他语言都需要堆栈帧来"展开"(例如,向当前函数的祖先调用者"抛出异常"); 即,"展开"堆栈帧,可以中止一个或多个函数并将控制传递给某个祖先函数,而不会在堆栈上留下不需要的东西.

当省略堆栈帧时 - -fomit-frame-pointer对于GCC - ,函数实现基本上改变为

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

        ; ... function ...

        addq    $8, %rsp
        ret
Run Code Online (Sandbox Code Playgroud)

因为没有堆栈帧(%rbp用于其他目的,并且它的值永远不会被推送到堆栈),每个函数调用只将返回地址推送到堆栈,这是一个8字节的数量,所以我们需要从中减去8 %rsp保持16的倍数.(一般来说,从中减去并加上的值%rsp是8的奇数倍.)

函数参数通常在寄存器中传递.看到这个答案细节开始的ABI链接,但在短,整型和指针寄存器传递%rdi,%rsi,%rdx,%rcx,%r8,和%r9在与浮点参数%xmm0%xmm7寄存器.

在某些情况下,你会看到rep ret而不是rep.不要混淆:rep ret意思是完全相同的东西ret; 的rep前缀,尽管通常与字符串指令(重复指令)用的,当施加到不执行任何ret指令.只是某些AMD处理器的分支预测器不喜欢跳转到ret指令,推荐的解决方法是使用rep ret那里的.

最后,我省略了堆栈顶部上方的红色区域(地址小于的128个字节%rsp).这是因为它对于典型函数并不真正有用:在普通的has-stack-frame情况下,您需要将本地内容放在堆栈框架内,以便进行调试.在omit-stack-frame的情况下,堆栈对齐要求已经意味着我们需要从中减去8 %rsp,因此在该减法中包含局部变量所需的内存不需要任何费用.