如何手工展开函数栈来定位问题

简介:
作者:gfree.wind@gmail.com
博客:blog.focus-linux.net     linuxfocus.blog.chinaunix.net

当程序crash的时候,我们可以通过coredump文件,来定位问题。比如使用bt命令可以完整的展开函数的调用栈。但是有些时候,部分栈的数据可能被损坏,导致gdb无法直接显示函数的调用栈。那么这时就需要我们手工展开函数栈。

关于x86的函数调用栈的示意图基本如下图所示:
23629988_1322490468QN79.gif

关于参数的压栈顺序,上图为cdecl方式,这个可以通过编译选项修改。GCC默认使用cdecl。

下面看一下例子:
  1. #include <stdlib.h>
  2. #include <stdio.h>


  3. static int test(int a, int b, int c)
  4. {
  5.     return a+b+c;
  6. }

  7. int main()
  8. {
  9.     int a = 1;
  10.     int b = 2;
  11.     int c = 3;

  12.     int d = test(a, b, c);

  13.     printf("%d\n", d);

  14.     return 0;
  15. }
编译:gcc -g -Wall test.c
进入test,查看函数调用栈:
  1. Breakpoint 1, test (a=1, b=2, c=3) at test.c:7
  2. 7 return a+b+c;
  3. Missing separate debuginfos, use: debuginfo-install glibc-2.11-2.i686
  4. (gdb) bt
  5. #0 test (a=1, b=2, c=3) at test.c:7
  6. #1 0x08048412 in main () at test.c:16
那么现在查看一下寄存器:
  1. eax 0x1 1
  2. ecx 0x2c0187d8 738297816
  3. edx 0x1 1
  4. ebx 0x73fff4 7602164
  5. esp 0xbffff048 0xbffff048
  6. ebp 0xbffff048 0xbffff048
  7. esi 0x0 0
  8. edi 0x0 0
  9. eip 0x80483c7 0x80483c7 <test+3>
  10. eflags 0x286 [ PF SF IF ]
  11. cs 0x73 115
  12. ss 0x7b 123
  13. ds 0x7b 123
  14. es 0x7b 123
  15. fs 0x0 0
  16. gs 0x33 51
得到ebp的地址为0xbffff048,现在检查这个地址的内存
  1. (gdb) x /8x 0xbffff048
  2. 0xbffff048: 0xbffff078 0x08048412 0x00000001 0x00000002
  3. 0xbffff058: 0x00000003 0x0073fff4 0x00000001 0x00000002
下面分析一下这些内存的内容:
1. 0xbffff078:为test的调用者,即main函数的bp地址;BP地址即为该函数的栈顶指针。
2. 0x08048412:为test的返回地址,与前面的bt的输出相符;
3. 后面的0x00000001,0x00000002,0x00000003,为传给test的三个参数,且参数顺序为由右向左压栈——注意这个顺序是可以通过改变编译参数改变的。

回到main中,验证一下bp寄存器的内容:
  1. 0x08048412 in main () at test.c:16
  2. 16 int d = test(a, b, c);
  3. Value returned is $2 = 6
  4. (gdb) info registers
  5. eax 0x6 6
  6. ecx 0x39ff7a48 973044296
  7. edx 0x1 1
  8. ebx 0x73fff4 7602164
  9. esp 0xbffff050 0xbffff050
  10. ebp 0xbffff078 0xbffff078
可见BP的地址确实为0xbffff078,与之前的分析相符。

注:关于压栈顺序,参数的传递方式等等,都可以通过编译选项来指定或者禁止的。本文的情况为GCC的默认行为。
相关实践学习
阿里云图数据库GDB入门与应用
图数据库(Graph Database,简称GDB)是一种支持Property Graph图模型、用于处理高度连接数据查询与存储的实时、可靠的在线数据库服务。它支持Apache TinkerPop Gremlin查询语言,可以帮您快速构建基于高度连接的数据集的应用程序。GDB非常适合社交网络、欺诈检测、推荐引擎、实时图谱、网络/IT运营这类高度互连数据集的场景。 GDB由阿里云自主研发,具备如下优势: 标准图查询语言:支持属性图,高度兼容Gremlin图查询语言。 高度优化的自研引擎:高度优化的自研图计算层和存储层,云盘多副本保障数据超高可靠,支持ACID事务。 服务高可用:支持高可用实例,节点故障迅速转移,保障业务连续性。 易运维:提供备份恢复、自动升级、监控告警、故障切换等丰富的运维功能,大幅降低运维成本。 产品主页:https://www.aliyun.com/product/gdb
目录
相关文章
|
5月前
|
存储 NoSQL 算法
从一个crash问题展开,探索gcc编译优化细节
问题分析的过程也正是技术成长之路,本文以一个gcc编译优化引发的crash为切入点,逐步展开对编译器优化细节的探索之路,在分析过程中打开了新世界的大门……
|
10月前
|
编译器 Linux C语言
函数栈帧的创建和销毁(以C语言代码为例,汇编代码的角度分析)(上)
函数栈帧的创建和销毁(以C语言代码为例,汇编代码的角度分析)
|
2月前
hyengine 代码块问题之跳转目标地址如何解决
hyengine 代码块问题之跳转目标地址如何解决
|
10月前
线上排查堆栈
线上排查堆栈
51 1
|
10月前
|
编译器 C语言
函数栈帧的创建和销毁(以C语言代码为例,汇编代码的角度分析)(下)
函数栈帧的创建和销毁(以C语言代码为例,汇编代码的角度分析)
|
运维 监控 前端开发
记一次线上 bug 的排查分析过程及总结
记一次线上 bug 的排查分析过程及总结
记一次线上 bug 的排查分析过程及总结
|
Java
【线上问题排查】内存泄漏排查(模拟真实环境)
【线上问题排查】内存泄漏排查(模拟真实环境)
187 0
|
消息中间件 运维 监控
线上踩坑记:项目中一次OOM的分析定位排查过程!
线上踩坑记:项目中一次OOM的分析定位排查过程!
|
存储 运维 安全
基于VS调试分析 + 堆栈观察问题代码段
面对眼前两段有问题的代码,你会通过什么去解决这个问题?本文将通过调试进行逐步分析💻,带你步步观察程序的运行逻辑
21375 0
基于VS调试分析 + 堆栈观察问题代码段