函数调用栈维护过程

涉及到以下汇编指令:

pushl: 入栈

ebp指向栈底 esp指向栈顶

入栈时只需要将esp下移, 将入栈的值放入esp指向的地址,如下图所示:

函数调用栈维护过程

popl: 出栈

先将栈顶的数据保存,再将esp上移

函数调用栈维护过程

call:顾名思义,调用一个函数

调用一个函数需要将当前执行到的指令位置(eip寄存器的值)保存,也就是使用pushl指令入栈。再将eip寄存器的值置为要跳转的地址。

函数调用栈维护过程

ret: 结束当前函数

恢复上个函数的执行位置,也就是将esp指向的地址保存至eip,再将esp上移。

函数调用栈维护过程

观察以下代码生成的汇编语言:

#include <iostream>

using namespace std;

int power(int x)
{
        return x*x;
}

int main()
{
        int res = power(3);

        cout << res <<endl;

        return 0;
}

g++ -S test.cpp -o test.S

得到生成的汇编代码:

_Z5poweri:
        pushl   %ebp
        movl    %esp, %ebp
        movl    8(%ebp), %eax
        imull   8(%ebp), %eax
        popl    %ebp
        ret
main:
        leal    4(%esp), %ecx
        andl    $-16, %esp
        pushl   -4(%ecx)
        pushl   %ebp
        movl    %esp, %ebp
        pushl   %ecx
        subl    $20, %esp
        pushl   $3
        call    _Z5poweri
        addl    $4, %esp
        movl    %eax, -12(%ebp)
        subl    $8, %esp
        pushl   -12(%ebp)
        pushl   $_ZSt4cout
        call    _ZNSolsEi
        addl    $16, %esp
        subl    $8, %esp
        pushl   $_ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_
        pushl   %eax
        call    _ZNSolsEPFRSoS_E
        addl    $16, %esp
        movl    $0, %eax
        movl    -4(%ebp), %ecx
        leave
        leal    -4(%ecx), %esp
        ret

power函数名经过name-mangling转化为_Z5poweri,_是开始标志,5是函数名长度,i表示返回值为int。

调用power函数的流程如下:

main函数先将函数参数保存,再调用call指令调用power函数,保存eip的值。就有了下图的第一个状态。

函数调用栈维护过程