zoukankan      html  css  js  c++  java
  • Linux内核调试方法总结之栈帧

    栈帧

    栈帧和指针可以说是C语言的精髓。栈帧是一种特殊的数据结构,在C语言函数调用时,栈帧用来保存当前函数的父一级函数的栈底指针,当前函数的局部变量以及被调用函数返回后下一条汇编指令的地址。如下图所示:

                           

    栈帧位于栈内存中,接下里我们用一个实例展示一下栈帧的入栈和退栈过程。

     stackframe.c

    #include <stdio.h>

    int func(int m, int n)

    {

      return m+n;

    }

    int main()

    {

      int m = 8;

      int n = 6;

     

      func(m, n);

     

      return 0;

    }

    #gcc –g stackframe.c –o stackframe (编译)

    #objdump –dS stackframe > stackframe.S (反汇编)

     

    从133~139行我们可以看到main函数栈帧的形成过程(入栈操作):

    1)    push %rbp 将上一级函数栈帧的栈底指针压栈

    2)    mov %rsp, %rbp 将BP指针指向SP,因为上一级函数的栈顶指针是下一级函数的栈底指针,证明栈帧是依次向下增长的

    3)    sub $0x10, %rsp SP栈顶指针向下位移16个字节,即创建main函数栈帧。这个地方为什么是16个字节呢?是因为上一级函数栈底指针和当前函数返回时下一条指令地址各占4个字节,m和n两个整形变量各占4个字节,加起来就是16个字节。

    4)    movl $0x8, -0x4(%rbp) 将变量m压栈

    5)    movl $0x6, -0x8(%rbp) 将变量n压栈

    6)    mov -0x8(%rbp), %edx      将m变量值加载到edx寄存器

    mov -0x4(%rbp), %eax     将n变量值加载到eax寄存器

    mov %edx, %esi

    mov %eax, %edi

    7)callq 4004c4 <func> 调用callq指令跳转到func函数段,同时压栈EIP+4,即返回func函数时下一条可执行指令的地址

     

    从func函数的反汇编代码可以看到,0x4004c4地址就是func函数开始处,和前面的callq对应。在进入func函数段之后,就是func函数压栈的动作,基本顺序和前面的main函数压栈过程一致。这个地方需要注意的是,首先是mov %edi, -0x4(rbp),从前面的汇编代码可以看到%edi保存的是n变量的值,其次才执行mov %esi, -0x8(rbp)压栈m变量值,证明函数参数的传递顺序是从右往左。另外,这个操作过程演示了实参传递的过程,那么形参传递和指针传递又是怎么样的呢?有兴趣可以试一下。

     

    前面的描述详细介绍了函数栈帧的形成过程,也就是函数调用的底层实现。之所以要重点介绍这一部分内容,是因为在Linux系统出现死机或者异常重启情况时,我们通常会获取死机时的backtrace信息,即函数调用顺序和函数入参,这样我们就可以精准地定位到导致死机的代码段进一步分析。而backtrace就是通过对函数栈帧进行逆推得到的。

     

    前面介绍的是Intel X86架构栈帧的实现原理,和ARM平台上的栈帧实现略有差异,ARM平台栈帧会依次压栈PC指针寄存器值,LR返回指针寄存器值,SP栈顶指针寄存器值,FP栈底指针寄存器值,函数入参,局部变量,即将调用的函数的参数。从本质上讲,栈帧是为了记录函数调用过程,不同平台会压栈不同的数据,但是基本上都是大同小异的。

    之所以要重点介绍栈帧这一部分内容,是因为代码调试的最有效方法是获取系统崩溃时的系统运行上下文,包括寄存器组值、函数调用栈,这样我们就能缩小定位范围,详细地知道函数的入参,执行到哪一条指令出的问题。这种backtrace的思路贯穿Linux内核调试、Native层应用调试(用户空间C/C++可执行文件、静态库、动态库)、Java层调试,因此衍生出了ramdump, coredump, Exception.printStack()等技术,其目的就是为了获取函数调用栈信息。

  • 相关阅读:
    KNN算法
    mysql必须知道的
    励志的演讲
    30条程序员名言警句
    别人的文章:为什么软件开发,人多,事少,还会工作量大?
    分享一个比较受启发的文章“学历代表过去,能力代表现在,学习力代表未来”
    mvc 学前必知
    启动docker容器 防火墙问题报错 ! -i docker0' failed: iptables: No chain/target/match by that name.
    git指令详解总结
    git reset 版本回退的三种用法总结
  • 原文地址:https://www.cnblogs.com/justin-y-lin/p/5424465.html
Copyright © 2011-2022 走看看