函数调用栈学习

这段时间写程序,一直有个想法,就是知道当前执行处的函数调用栈。今天网上搜到原来有backtrace这个函数,还没来得及试验,先把原来的整理一下,算是了解下。 

根据网上的图片,根据程序画了图增强下理解。

 

试验程序(均在32位机子上)

#include <stdio.h>

short fun2(int pa, unsigned char pb)
{
        unsigned 
char ss[5= {0x030x020x01};
        
int v = 0xabcd;

        printf(
"fun2:\n%x\n%x\n", ss, &v);
        ss[
4= 0;

        
return v;
}

char fun(short p)
{
        unsigned 
char s[10= {0x010x020x03};
        
int a = 0x12345678;

        printf(
"fun:\n%x\n%x\n%x\n"&p, s, &a);
        s[
4= 0;
        s[
5= 0;

        fun2(
0x876543210xaa);

        
return s[6];
}

int main ()
{
        unsigned 
char s[20= {0x0a0x0b0x0c};

        fun(
10);

        
return 0;
}

 

堆栈图:

 

 

 

内存数据:
(gdb) x/48xb 0xbffff598-32
0xbffff578:     0xd8    0xf5    0xff    0xbf    0xaa    0xef    0xaa    0x42
0xbffff580:     0xd8    0xf5    0xff    0xbf    0xcd    0xab    0x00    0x00
0xbffff588:     0xff    0xef    0xaa    0x03    0x02    0x01    0x00    0x00
0xbffff590:     0xcd    0x85    0x04    0x08    0xa4    0xf5    0xff    0xbf
0xbffff598:     0xd8    0xf5    0xff    0xbf    0x84    0x84    0x04    0x08
0xbffff5a0:     0x21    0x43    0x65    0x87    0xaa    0x00    0x00    0x00

(gdb) x
/48xb 0xbffff5d8-32
0xbffff5b8:     0x62    0x56    0xae    0x42    0x0a    0x00    0xff    0xbf
0xbffff5c0:     0x78    0x56    0x34    0x12    0x01    0x00    0x01    0x02
0xbffff5c8:     0x03    0x00    0x00    0x00    0x00    0x00    0x00    0x00
0xbffff5d0:     0x60    0xf6    0xff    0xbf    0xd8    0x97    0x04    0x08
0xbffff5d8:     0x18    0xf6    0xff    0xbf    0xd6    0x84    0x04    0x08
0xbffff5e0:     0x0a    0x00    0x00    0x00    0x1d    0xa8    0x00    0x00

(gdb) x
/48xb 0xbffff618-32
0xbffff5f8:     0x2e    0xa1    0xa7    0x42    0x0a    0x0b    0x0c    0x00
0xbffff600:     0x00    0x00    0x00    0x00    0x00    0x00    0x00    0x00
0xbffff608:     0x00    0x00    0x00    0x00    0x00    0x00    0x00    0x00
0xbffff610:     0xe0    0x84    0x04    0x08    0x00    0x00    0x00    0x00
0xbffff618:     0x00    0x00    0x00    0x00    0x13    0xa5    0xa7    0x42
0xbffff620:     0x01    0x00    0x00    0x00    0xb4    0xf6    0xff    0xbf

 

可知:当前ebp寄存器的值一直递推下去,一直到0,每个数值的下4个字节就是返回地址。

写个程序打印当前的调用栈:

#include <stdio.h>

#define GET_EBP(x)  __asm__ __volatile__("movl %%ebp, %0;":"=r"((x)))

void fun(int a)
{
        
int t;
        
int i;

        
if (a == 5)
        {
                GET_EBP(t);

                
for (i = 0; t != 0; t = *(int *)t, i++)
                {
                        printf(
"#%d:0x%08x\n", i, *(int*)(t + 4));
                }

                
return;
        }

        fun(a 
+ 1);
}

int main()
{
        fun(
0);

        
return 0;
}

运行结果:

#0:0x08048421
#
1:0x08048421
#
2:0x08048421
#
3:0x08048421
#
4:0x08048421
#
5:0x0804843c
#
6:0x42a7a513

gdb的调用栈对比:

(gdb) bt
#0  fun (a=5) at ../main.c:12
#
1  0x08048421 in fun (a=4) at ../main.c:22
#
2  0x08048421 in fun (a=3) at ../main.c:22
#
3  0x08048421 in fun (a=2) at ../main.c:22
#
4  0x08048421 in fun (a=1) at ../main.c:22
#
5  0x08048421 in fun (a=0) at ../main.c:22
#
6  0x0804843c in main () at ../main.c:27

结果相同,它的内容更多一些,有函数名,文件名等信息。

一种简单方法,根据地址获取函数名:(地址前面加上(void(*)()) 。 )

(gdb) p  (void(*)())0x08048421
$1 = (void (*)()) 0x8048421 <fun+93>

其他方法估计得读elf文件(怎么读是个问题- -!),获取符号表等信息,类似:

[root@localhost Debug]# nm elf文件
。。。
080483c4 T fun
08048427 T main
         U printf
@@GLIBC_2.0

 

参考:

图片参照:http://www.cnblogs.com/shanzy/articles/1439370.html

汇编参考:http://oss.lzu.edu.cn/modules/newbb/viewtopic.php?viewmode=thread&topic_id=1319&forum=13&post_id=5048

书:《程序员的自我修养》

 

posted on 2011-06-19 22:31  单刀不进  阅读(402)  评论(0编辑  收藏  举报

导航