栈帧

栈帧和指针可以说是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()等技术,其目的就是为了获取函数调用栈信息。

Linux内核调试方法总结之栈帧的更多相关文章

  1. Linux内核调试方法总结

    Linux内核调试方法总结 一  调试前的准备 二  内核中的bug 三  内核调试配置选项 1  内核配置 2  调试原子操作 四  引发bug并打印信息 1  BUG()和BUG_ON() 2   ...

  2. Linux内核调试方法总结【转】

    转自:http://my.oschina.net/fgq611/blog/113249 内核开发比用户空间开发更难的一个因素就是内核调试艰难.内核错误往往会导致系统宕机,很难保留出错时的现场.调试内核 ...

  3. 【转】Linux内核调试方法总结

    目录[-] 一  调试前的准备 二  内核中的bug 三  内核调试配置选项 1  内核配置 2  调试原子操作 四  引发bug并打印信息 1  BUG()和BUG_ON() 2  dump_sta ...

  4. Linux内核调试方法总结之反汇编

    Linux反汇编调试方法 Linux内核模块或者应用程序经常因为各种各样的原因而崩溃,一般情况下都会打印函数调用栈信息,那么,这种情况下,我们怎么去定位问题呢?本文档介绍了一种反汇编的方法辅助定位此类 ...

  5. Linux内核调试方法总结之序言

    本系列主要介绍Linux内核死机.异常重启类稳定性问题的调试方法. 在Linux系统中,一切皆为文件,而系统运行的载体,是一类特殊的文件,即进程.因此,我尝试从进程的角度分析Linux内核的死机.异常 ...

  6. Linux内核调试方法【转】

    转自:http://www.cnblogs.com/shineshqw/articles/2359114.html kdb:只能在汇编代码级进行调试: 优点是不需要两台机器进行调试. gdb:在调试模 ...

  7. Linux内核调试方法总结之调试宏

    本文介绍的内核调试宏属于静态调试方法,通过调试宏主动触发oops从而打印出函数调用栈信息. 1) BUG_ON 查看bug处堆栈内容,主动制造oops Linux中BUG_ON,WARN_ON用于调试 ...

  8. 转载:Linux内核调试方法

    转载文章请注明作者和二维码及全文信息. 转自:http://blog.csdn.net/swingwang/article/details/72331196 不会编程的程序员,不是好的架构师,编程和内 ...

  9. Linux内核调试方法总结之coredump

    什么是core dump? 分析core dump是Linux应用程序调试的一种有效方式,像内核调试抓取ram dump一样,core dump主要是获取应用程序崩溃时的现场信息,如程序运行时的内存. ...

随机推荐

  1. 【7.24校内交流赛】T3【qbxt】复读警告

    数据范围:N,key<=1000; 首先看题目背景,显然不是DP就是图论,但是这显然不是个图论,因此这就是个DP: 接下来考虑怎么DP 我们定义dp[i][j]表示现在dp到了第i个数,当前i个 ...

  2. Java中的sort

    Java中对集合排序有两种方式 Comparable和Comparator public static <T> void sort(List<T> list); 将集合中的数据 ...

  3. javascript xml转json

    1.代码 //加载xml数据 function loadXml(str) { if (str == null) { return null; } var doc = str; try{ doc = c ...

  4. Ionic创建混合App(二)

    ionic 2 启动应用进入欢迎引导页 1.首先,使用CLI命令,创建引导页面 ionic g page welcome 2.需改welcome.html模板文件 <ion-slides pag ...

  5. js的抖动及防抖和节流

     js的抖动 在 js 中 改变窗口大小 & 上下滚动滚动条 & 反复向输入框中输入内容 ... , 如果绑定了相应的事件 , 这些事件的触发频率非常高, 严重影响用户体验和服务器的性 ...

  6. python multiprocessing模块

    python multiprocessing模块 原文地址 multiprocessing multiprocessing支持子进程.通信和共享数据.执行不同形式的同步,提供了Process.Queu ...

  7. 解决springmvc 乱码的方法

    post乱码: 在web.xml添加post乱码filter: <filter> <filter-name>CharacterEncodingFilter</filter ...

  8. 《Java核心技术卷I》——第5章 继承

    在C++中,没有提供用于表示抽象类的特殊关键字.只要有一个纯虚函数,这个类就是抽象类. hashCode()方法是定义在Object类中,因此每个对象都有一个默认的散列码,其值为对象的存储地址. 绝大 ...

  9. centOS7 通过nmtui和nmcli图形配置网络服务

    一.通过nmtui配置网络参数 Linux系统配置网络参数的方式有很多种,其中最简单最直接的方式就是直接修改网卡配置文件,但这种方式也很容易出错,比如说IPADDR.NETMASK.GATEWAY等参 ...

  10. openGL坐标系

    从我们构造模型的局部坐标系(Local/Object Space)经过一系列的处理最终渲染到屏幕坐标系(Screen Space)下,这个过程有6种坐标系. 一.世界坐标系(World Coordin ...