• function call process


    摘自自博客网址 http://www.cnblogs.com/bangerlee/archive/2012/05/22/2508772.html

    • ax(accumulator): 可用于存放函数返回值
    • bp(base pointer): 用于存放执行中的函数对应的栈帧的栈底地址
    • sp(stack poinger): 用于存放执行中的函数对应的栈帧的栈顶地址
    • ip(instruction pointer): 指向当前执行指令的下一条指令

    example code:

    //func_call.c

    int bar(int c, int d)

    {

        int e = c + d;

        return e;

    }

    int foo(int a, int b)

    {

        return bar(a, b);

    }

    int main(void)

    {

        foo(2, 5);

        return 0;

    }

     

    main函数汇编代码:

    (gdb) disassemble /rm

    Dump of assembler code for function main:

           {

    0x0000000000400521 :     55                push %rbp

    0x0000000000400522 :     48 89 e5          mov %rsp,%rbp

                  foo(2, 5);

    0x0000000000400525 :     be 05 00 00 00    mov $0x5,%esi

    0x000000000040052a :     bf 02 00 00 00    mov $0x2,�i

    0x000000000040052f :    e8 d2 ff ff ff    callq 0x400506

                  return 0;

    0x0000000000400534 :    b8 00 00 00 00    mov $0x0,eax

           }

    0x0000000000400539 :     c9               leaveq 

    0x000000000040053a :     c3               retq

    End of assembler dump.

    函数调用关系

    main->foo->bar

     # gcc -g func_call.c -o func_call

    反汇编分析

    (gdb) start
    
    start命令用于拉起被调试程序,并执行至main函数的开始位置
    
    现进程跑在main函数中,我们disassemble命令显示当前函数的汇编信息:
    
    (gdb) disassemble /rm
    
    /m指示显示汇编指令的同时,显示相应的程序源码;/r指示显示十六进制的计算机指令(raw instruction)
    
    一个函数被调用,首先默认要完成以下动作:
    将调用函数的栈帧栈底地址入栈,即将bp寄存器的值压入调用栈中
    建立新的栈帧,将被调函数的栈帧栈底地址放入bp寄存器中
    以下两条指令即完成上面动作:
    push %rbp
    mov  %rsp, %rbp
    
    main并不是程序拉起后第一个被执行的函数,它被_start函数调用
    
    传递参数
    
    mov $0x5, %esi
    mov $0x2, �i
    
    或者:\x86 is diffrent from x86_64
    
    sub $0x8, %esp 
    mov $0x5, -0x4(%ebp) //栈底对应低地址
    mov $0x2, -0x8(�p)
    
    call指令完成控制全转移
    
    0x000000000040052f 14>:     e8 d2 ff ff ff    callq  0x400506 
    
    执行完start命令后,现在程序停在0x400522的位置,下面我们通过gdb的si指令,让程序执行完call指令
    
    (gdb) si 3
    foo (a=0, b=4195328) at func_call.c:8
    8    {
    (gdb) 
    
    查看rsp、rbp寄存器的值,它们保存了程序实际用到的物理内存地址
    
    (gdb) info registers rbp rsp
    rbp            0x7fffffffe8e0    0x7fffffffe8e0
    rsp            0x7fffffffe8d8    0x7fffffffe8d8
    (gdb)
    
    将main函数栈帧的栈底地址入栈,建立foo函数的栈帧:
    
    0x0000000000400506 0>:     55             push   %rbp
    0x0000000000400507 1>:     48 89 e5       mov    %rsp,%rbp //rsp的值从哪里得到的?
    
    将参数传入esi、edi寄存器,然后执行call指令
    
    0x000000000040051a 20>:     e8 cd ff ff ff    callq  0x4004ec 
    10    }
    0x000000000040051f 25>:     c9    leaveq 
    0x0000000000400520 26>:     c3    retq   
    
    最后一个被调用的函数结果保存在eax寄存器中,以作为结果返回
    
    函数调用过程对应着调用栈的建立,而函数返回则是进行调用栈的销毁,
    
    0x0000000000400504 24>:     c9    leaveq//相当于mov %rbp, %rsp/pop %rbp
    
    0x0000000000400505 25>:     c3    retq
    
    修改sp、bp寄存器记录栈帧的高、低地址,以此完成函数调转;
    
    push/mov操作保存caller变量、指令信息,保证callee返回之后caller继续正常执行;
    
    下一个函数的bp由sp传入,从kernel传入的?
    

    调用栈变化过程:
    
    call foo之前 ox400534是call后main的下一条指令地址
    
    函数调用过程--理解
    call bar后,汇编bar前,0x40051f是foo执行bar后的下一条指令
    
    函数调用过程--理解
    汇编bar后
    
    函数调用过程--理解
    执行完bar后
    
    函数调用过程--理解
  • 相关阅读:
    使用truffle测试部署合约
    nodejs promise深度解析
    pthread线程特定数据
    基于信号量与互斥锁实现的生产者和消费者
    Linux coredump 的打开和关闭
    Linux 双网卡配置两个IP同时只有一个会通的原因
    进程间通信-共享内存
    进程间通信-消息队列
    TCP/IP SIGPIPE信号
    Select模式和超时
  • 原文地址:https://www.cnblogs.com/eiguleo/p/3474876.html
Copyright © 2020-2023  润新知