zoukankan      html  css  js  c++  java
  • 函数调用过程分析

    函数调用过程分析

    1. 静态变量和初始化

    int static_variable = 5;
    --------------------------------------------
            .data ;进入程序的数据区
            .even ;确保变量开始于内存的偶数地址
            .global _static_variable ;声明变量为全局类型
     _static_variable:
            .long 5 ;创建空间,初始化
    

    2. 堆栈帧

    一个函数分为:函数序、函数体、函数跋

    函数序:执行启动工作,如:为局部变量保存堆栈中的内存

    函数跋:在函数即将返回之前清理堆栈。

    函数体:执行工作的地方

    void f()
    {
        register int i1, i2, i3, i4, i5,
                     i6, i7, i8, i9, i10;
        register char *c1, *c2, *c3, *c4, *c5,
                      *c6, *c7, *c8, *c9, *c10;
        extern int a_very_long_name_to_see_...
        double dbl;
        int func_ret_int();
        double func_ret_double();
        char *func_ret_char_ptr();
    -------------------------------------------------------
            .text    ;进入程序代码段
            .global _f    ;函数的全局声明
    _f:     link a6, #-88     ;创建堆栈帧,堆栈帧是堆栈中的一个区域,存储变量和其他值
            moveml 0x3cfc, sp@    ;选定寄存器中的值复制到堆栈中
    

    0x3cfc表示寄存器d2至d7、a2到a5中的值需要被保存

    局部变量声明和函数原型不会产生任何汇编代码。但局部变量声明时进行了初始化,也会出现指令用于赋值操作

    3. 寄存器变量

    i1 = 1; i2 = 2; i3 = 3; i4 = 4; i5 = 5;
    i6 = 6; i7 = 7; i8 = 8; i9 = 9; i10 = 10;
    c1 = (char *)110; c2 = (char *)120;
    c3 = (char *)130; c4 = (char *)140;
    c5 = (char *)150; c6 = (char *)160;
    c7 = (char *)170; c8 = (char *)180;
    c9 = (char *)190; c10 = (char *)200; 
    -------------------------------------------
    moveq #1,d7
    moveq #2,d6
    moveq #3,d5
    moveq #4,d4
    moveq #5,d3
    moveq #6,d2
    movl #7,a6@(-4)
    movl #8,a6@(-8)
    movl #9,a6@(-12)
    movl #10,a6@(-16)
    movl #110,a5
    movl #120,a4
    movl #130,a3
    movl #140,a2
    movl #150,a6@(-20)
    movl #160,a6@(-24)
    movl #170,a6@(-28)
    movl #180,a6@(-32)
    movl #190,a6@(-36)
    movl #200,a6@(-40)
    

    值1至6被放在数据寄存器,7至10放在其他地方

    指针变量前4个存放在寄存器

    其他变量,机器执行间接寻址和索引操作。a6称为帧指针,指向堆栈帧内部的一个引用位置

    这台机器(motorola 68000),a6用作帧指针,a7是堆栈指针sp,d0和d1用于存函数返回值

    4. 堆栈帧的布局

    运行时堆栈保存了每个函数运行时所需要的数据,包括它的自动变量和返回地址。

    4.1 传递函数参数

    i2 = func_ret_int(10, i1, i10);
    ---------------------------------
    movl a6@(-16),sp@-
    movl d7,sp@-
    pea  10
    jbsr _func_ret_int
    

    前3条指令把函数的参数压入堆栈中。以参数列表相反的次序逐个压入栈。

    接下来跳转子程序,把返回地址压入堆栈中,并跳转到_func_ret_int的起始位置,当被调用函数结束任务返回调用位置时,就需要用到压入堆栈的返回地址。

    5d3178bbba95520405

    4.2 函数序

    int func_ret_int(int a, int b, register int c)
    {
        int d;
    ---------------------------------------------------
            .global  _func_ret_int
    _func_ret_int:
            link    a6,#-8
            moveml  #0x80,sp@
            movl    a6@(16),d7
    

    link指令分成几个步骤:

    • a6的内容被压入堆栈中

    • 堆栈指针的当前值被复制到a6

    • link指令从堆栈指针中减去8

    • 这将创建空间用于保存局部变量和被保存的寄存器值*

      5d317bbfca55072652

    下一条指令把单一寄存器保存到堆栈帧,操作数0x80指定寄存器d7。寄存器存储在堆栈的顶部,剩余部分必然是局部变量存储的地方。如上图右边。

    函数序最后才能够堆栈复制一个值到d7,函数把第三个参数声明为寄存器变量。

    4.3 堆栈中的参数次序

    被调用函数使用帧指针加一个偏移量来访问参数,当参数以反序压入到堆栈时,参数列表的第一个参数便位于堆栈中这堆参数的顶部,它距离帧指针的偏移量是一个常数。

    4.4 最终的堆栈帧布局

    5d317e8fc517c97106

        d = b - 6;
        return a + b + c;
    --------------------------------
    movl    a6@(12),d0
    subl    #6,d0
    movl    d0,a6@(-4)
    movl    a6@(8),d0
    movl    a6@(12),d0
    addl    d7,d0
    moveml  a6@(-8),#0x80
    unlk    a6
    rts    
    

    4.5 函数跋

    • mveml恢复以前被保存的寄存器值

    • unkl(unlink)把a6的值复制给堆栈指针并把从堆栈中弹出的a6旧值装入a6中。清除堆栈帧中返回地址以上的那部分内容

    • rts指令通过把返回地址从堆栈中弹出到程序计数器,从而从该函数返回。

    现在,执行流从调用程序的地点继续。堆栈尚未完全清理

    i2 = func_ret_int(10, i1, i10);
    ------------------------------------
    lea    sp@(12),sp
    movl    d0,d6
    

    第1条指令把参数从堆栈中弹出,此时堆栈的状态就和调用前状态完全一样了。

    4.6 返回值

    函数跋没有使用d0,因此依然保存函数的返回值。第二条指令把d0复制给d6,后者存放变量i2的存放位置。

    dbl = func_ret_double();
    c1 = func_ret_char_ptr();
    -------------------------------------
    jbsr    _func_ret_double
    movl    d0,a6@(-48)
    movl    d1,a6@(-44)
    
    pea    a5@
    jbsr    _func_ret_char_ptr
    addqw   #4,sp
    movl    d0,a5
    

    第一个函数double是8字节,无法放入一个寄存器中,因此返回值需要d0和d1两个寄存器。

    最后那个函数调用说明了指针变量时如何从函数中返回的:也是通过d0进行传递的。

    参考:C和指针-第18章 运行时环境

  • 相关阅读:
    hdu acm 2844 Coins 解题报告
    hdu 1963 Investment 解题报告
    codeforces 454B. Little Pony and Sort by Shift 解题报告
    广大暑假训练1 E题 Paid Roads(poj 3411) 解题报告
    hdu acm 2191 悼念512汶川大地震遇难同胞——珍惜现在,感恩生活
    hdu acm 1114 Piggy-Bank 解题报告
    poj 2531 Network Saboteur 解题报告
    数据库范式
    ngnix 配置CI框架 与 CI的简单使用
    Vundle的安装
  • 原文地址:https://www.cnblogs.com/luoxiao23/p/11214289.html
Copyright © 2011-2022 走看看