GCC为x86生成的"push%ebp; movl%esp,%ebp"有什么用?

mah*_*esh 35 x86 assembly gcc

这两个指令在gcc为x86机器生成的汇编代码中会产生什么影响:

push %ebp
movl %esp, %ebp
Run Code Online (Sandbox Code Playgroud)

JUS*_*ION 59

放松的解释是字面上的事实(尽管有一个小的方向错误),但没有解释原因.

%ebp是堆栈框架的"基指针".它是C运行时用于访问堆栈上的局部变量和参数的指针.这是GCC生成的一些典型的函数序言代码(准确地说是g ++)首先是C++源代码.

// junk.c++
int addtwo(int a)
{
    int x = 2;

    return a + x;
}
Run Code Online (Sandbox Code Playgroud)

这会生成以下汇编程序.

.file   "junk.c++"
    .text
.globl _Z6addtwoi
    .type   _Z6addtwoi, @function
_Z6addtwoi:
.LFB2:
    pushl   %ebp
.LCFI0:
    movl    %esp, %ebp
.LCFI1:
    subl    $16, %esp
.LCFI2:
    movl    $2, -4(%ebp)
    movl    -4(%ebp), %edx
    movl    8(%ebp), %eax
    addl    %edx, %eax
    leave
    ret
.LFE2:
    .size   _Z6addtwoi, .-_Z6addtwoi
    .ident  "GCC: (Ubuntu 4.3.3-5ubuntu4) 4.3.3"
    .section    .note.GNU-stack,"",@progbits
Run Code Online (Sandbox Code Playgroud)

现在解释一下序言代码(之前的所有内容.LCFI2:),首先:

  1. pushl %ebp调用函数的堆栈帧存储在堆栈中.
  2. movl %esp, %ebp获取当前堆栈指针并将其用作被调用函数的帧.
  3. subl $16, %esp 为局部变量留出空间.

现在您的功能已准备就绪.具有%ebp%寄存器负偏移的任何引用都是本地变量(x在本例中).任何与%ebp%寄存器具有正偏移量的引用都是传入的参数.

最后一点是leave指令,它是一个x86汇编程序指令,它执行恢复调用函数堆栈帧的工作.这通常被优化为C代码中的更快move %ebp %esppop %ebp%顺序.但是,出于说明的目的,我根本没有进行任何优化编译.


Roa*_*alt 9

这是您在函数开头看到的典型代码.

它将EBP寄存器的内容保存在堆栈中,然后将当前堆栈指针的内容存储在EBP中.

在函数调用期间使用堆栈来存储本地参数.但是在函数中,堆栈指针可能会改变,因为值存储在堆栈中.

如果保存堆栈的原始值,则可以通过EBP寄存器引用存储的参数,同时仍然可以使用(添加值)堆栈.

在函数结束时,您可能会看到该命令

pop %ebp   ; restore original value 
ret        ; return 
Run Code Online (Sandbox Code Playgroud)


Guf*_*ffa 7

push %ebp
Run Code Online (Sandbox Code Playgroud)

这将推动堆栈上的32位(扩展)基址指针寄存器,即堆栈指针(%esp)减去4,然后将%ebp的值复制到堆栈指针指向的位置.

movl %esp, %ebp
Run Code Online (Sandbox Code Playgroud)

这会将堆栈指针寄存器复制到基址指针寄存器.

将堆栈指针复制到基指针的目的是创建堆栈帧,即堆栈中子程序可以存储本地数据的区域.子例程中的代码将使用基指针来引用数据.