分享

GDB查看栈信息

 lycbje 2014-10-25

栈:是程序存放数据内存区域之一,特点是LIFO(后进先出)。

PUSH:入栈

POP:出战

使用场景:

1.保存动态分配的自动变量使用栈

2.函数调用时,用栈传递函数参数,半寸返回地址,返回值

代码sum.c

  1. #include <stdio.h>        
  2. #include <ctype.h>        
  3. #include <stdlib.h>        
  4. #define MAX  (1UL << 20)        
  5. typedef unsigned long long u64;        
  6. typedef unsigned int u32;        
  7. u32 max_addend=MAX;        
  8. u64 sum_till_MAX(u32 n)        
  9. {            
  10.     u64 sum;            
  11.     n++;            
  12.     sum=n;            
  13.     if(n<max_addend)                
  14.     sum+=sum_till_MAX(n);            
  15.     return sum;        
  16. }        
  17. int main(int argc,char** argv){            
  18.     u64 sum=0;            
  19.     if((argc==2) && isdigit(*(argv[1])))                
  20.     max_addend=strtoul(argv[1],NULL,0);            
  21.     if(max_addend>MAX||max_addend==0){                
  22.     fprintf(stderr,"Invalid number is specified\n");                
  23.     return 1;            
  24.     }            
  25.     sum=sum_till_MAX(0);            
  26.     printf("sum(0..%lu)=%llu\n",max_addend,sum);            
  27.     return 0;        
  28. }  


 

该程序计算0到命令行参数传递过来的数字之间所有的正数和。

图a是函数调用前的栈,图b是函数调用后的栈,图c是再次函数调用后的栈。

栈上保存了函数参数,调用返回地址,上层栈帧指针和函数内部使用的自动变量。

有时,还会用栈保存寄存器,每个函数独有,称作栈帧。

此时,需要设置表示栈帧起始地址的帧指针(FP)。

栈指针(SP)永远指向栈顶!

编译程序

#gcc -g -Wall -o sum sum.c

启动gdb调试sum

#gdb sum

(gdb) disas main

从图中可见call指令自动把返回地址0x8048494压入栈中

再看sum_till_MAX函数

在栈上保存上层帧的帧指针0x8048494,

然后将新的栈帧赋给帧指针%esp,

然后在栈上分配用于保存自动变量的空间,至此完成栈帧。

0x8(%ebp)指向帧指针+8字节的地址,

-0x10(%ebp)为指针-10字节的地址

leave指令删除栈帧,释放当前的栈,

ret指令为函数返回,将栈中保存的返回地址POP到程序计数寄存器,控制权返回给调用者。

这是第二次进入sum_till_MAX函数时使用backtrace

获取当前执行位置,可以通过程序计数器PC获得,在x86上是eip寄存器,FP是ebp寄存器

下面查看栈的内容,从表示栈顶的SP开始。

(gdb) x/40xw $sp

从栈上的返回地址信息可以看到和之前backtrace结果相同的调用跟踪信息0x080484c1和0x0804858d

用frame命令查看现在选择的帧,

frame 1选择帧1

up选择下一层的帧

用info命令的frame选项可以查看更详细的栈帧信息

栈的大小限制

注意,如果上述sum程序不带参数会引发段错误。

#./sum

发生栈溢出。

下面用gdb跟踪,查看程序计数器PC即可看到程序执行位置

#gdb sum

(gdb) r

(gdb) x/i $pc

这正是将sum_till_MAX的参数PUSH到栈顶的命令

查看栈指针SP的位置

(gdb) p $sp

查看该进程的内存映射

(gdb) i proc mapping

最后一行的[stack]表示栈空间,顶端是0xbf401000,之前看到的栈指针是0xbf3ffff0,超出了栈的范围发生溢出。

分析内核转储的时候无法使用上述命令,可以使用

(gdb) info files或者(gdb) info target得到相同信息

 

linux栈大小

显示本机系统支持的栈大小

# ulimit -s
10240

修改栈大小扩大10倍

#ulimit -Ss 102400

这样就不会栈溢出了

 

Linux下默认的栈空间大小是10M,可以通过ulimit -s进行查看,不同的Linux发行版本可能不太一样。下面介绍栈空间大小修改方法。
 
临时修改方法:
ulimit -s <新的栈空间大小>
 
永久修改方法:
1. 可以修改配置文件/etc/security/limits.conf
2. 可以将ulimit -s命令放到/etc/profile中,在任何用户启动的时候调用

参考《Debug.Hacks中文版 深入调试的技术和工具-调试时必须的栈知识》

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多