函数调用栈学习

来源:互联网 时间:1970-01-01

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

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

 

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

#include <stdio.h>
short fun2(int pa, unsigned char pb)
{
        unsigned char ss[5] = {0x03, 0x02, 0x01};
        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] = {0x01, 0x02, 0x03};
        int a = 0x12345678;

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

        fun2(0x87654321, 0xaa);

        return s[6];
}

int main ()
{
        unsigned char s[20] = {0x0a, 0x0b, 0x0c};

        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文件(怎么读是个问题- -!),获取符号表等信息,类似:

[[email protected] Debug]# nm elf文件
。。。
080483c4 T fun
08048427 T main
         U [email protected]@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

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

 


相关阅读:
Top