函数调用栈学习
这段时间写程序,一直有个想法,就是知道当前执行处的函数调用栈。今天网上搜到原来有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;
}
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
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;
}
#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
#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
#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>
$1 = (void (*)()) 0x8048421 <fun+93>
其他方法估计得读elf文件(怎么读是个问题- -!),获取符号表等信息,类似:
[root@localhost Debug]# nm elf文件
。。。
080483c4 T fun
08048427 T main
U printf@@GLIBC_2.0
。。。
080483c4 T fun
08048427 T main
U printf@@GLIBC_2.0
参考:
图片参照:http://www.cnblogs.com/shanzy/articles/1439370.html
书:《程序员的自我修养》