软件调试栈回溯实验

实验要求

编写一个C/C++程序例子,至少有三层的函数调用存在。通过GDB对编写的软件进行调试,需在第三层函数内设置断点使得程序暂停,然后进行如下的分析:

要求1

如何利用当前的栈基址和调试信息得到函数的调用关系,实现类似bt的显示结果,需进行解释说明;

要求2

在栈帧进行切换后,例如切换到第二层的函数上,分析如何根据当前的栈基址和调试信息进行获取变量的值,实现frame切换后利用print显示变量的效果,并说明其正确性。

实验原理

函数调用栈布局如图1所示:
在这里插入图片描述

图1 函数调用栈

函数调用的过程为主调函数首先将参数(从右向左)依次入栈,然后将指令指针EIP入栈以保存函数的返回地址(也是下一条待执行指令的地址)。然后被调函数将主调函数的栈帧基地址指针EBP入栈,将主调函数的栈顶指针ESP值赋给被调函数的EBP,然后改变ESP的值为被调函数的局部变量预留空间,之后将局部变量压栈。函数调用结束之后,将EBP指针的值赋给ESP从而释放局部变量,然后将已压栈的主调函数栈帧基址弹出到EBP,弹出返回地址到EIP。
从上面的分析中可以发现,函数调用过程中,被调用函数EBP指针所指向的内存里存储着主调函数的栈帧基地址,每层函数调用可通过当前EBP的值向栈底方向偏移(即EBP+4)得到返回地址,所以可以通过逐层递推找到最顶层的主调函数。根据EBP的值进行偏移可找到参数以及局部变量的地址,然后可以查找对应内存地址处所保存的数据。

实验过程

编写程序调试运行

编写一个C程序例子main.c,见附录,然后使用gcc -g -o main.exe main.c产生带有调试信息的可执行文件,然后使用命令gdb main.exe进行调试,首先使用命令b 18在第3层函数内设置一个断点,然后使用命令r运行程序,程序会在设置的断点处暂停,如图2所示,当前的函数调用栈是在max2函数那一层。
在这里插入图片描述

图2 程序在断点处暂停

利用当前的栈基址和调试信息得到函数的调用关系,实现类似bt的显示结果。

使用命令info register ebp查看当前栈帧基地址,如图3所示,为0x60fec8,
使用命令x/2x 0x60fec8查看从内存地址0x60fec8开始8个字节内的内存数据,结果如图3所示。其中,前四个字节存放的的是主调函数的栈帧基地址值,为0x0060fee8,后四个字节存放的是返回地址,为0x004013c2。
在这里插入图片描述

图3 查看被调、主调函数ebp值,函数返回地址

使用命令查看指令地址0x004013c2所对应的在源代码中的位置,如图4所示,可以看到所对应的位置是在函数max3内。所 以可知:max3调用了max2函数。
在这里插入图片描述

图4 查看指令地址所对应的源码中的位置

然后利用上一步得到的栈帧基地址0x0060fee8继续回溯,结果如图5所示。可知是main函数那一层的栈帧基地址为0x0060ff18,main函数调用了max3函数。

在这里插入图片描述

图5 继续回溯得到的结果
综上可知,函数调用关系是main函数调用max3函数,然后max3函数再调用max2函数。 然后我们使用bt命令打印函数调用栈进行验证,如图6所示,函数调用关系以及函数返回地址与我们前面分析的一致。

在这里插入图片描述

图6 使用bt命令进行验证

实现frame切换后利用print显示变量的效果

如图7所示,使用命令f(1)切换到max3函数那一层。
在这里插入图片描述

图7 切换栈帧
由上面的一小节分析可知,当前栈帧基地址为0x0060fee8。 通过源码可知,这一层函数形参为a,b,c,还有一个函数内定义的一个局部变量d。 由图1中的函数调用栈分压栈过程分析可知,局部变量d所在的地址为EBP-0x4,参数a的地址为EBP+0x8,参数b的地址为EBP+0xc,参数c的地址为EBP+0x10。然后使用x/1d命令查看相应内存地址处所保存的数据。结果如图8所示,可知此时d为1133587373 ,a为5,b为7,c为6。

在这里插入图片描述

图8 查看对应内存地址处的数据
然后使用print命令进行验证,结果如图9所示,和上面的结果一致。

在这里插入图片描述

图9 使用print命令验证
如果要查看全局变量的值,已初始化的全局变量保存在数据段,未初始化的全局变量保存在BSS段。需要找到全局变量所在地址,然后访问相应内存单元里的数据。结果如图10所示,此时g_1的值为2,g_2的值为0。使用print命令验证也是一致的。

在这里插入图片描述

图10 查看全局变量的值

附录

main.c程序为

#include<stdio.h>
int max2(int a,int b);
int max3(int a,int b,int c);
int g_1=2;
int g_2;
int main()
{
    int a=5;
    int b=7;
    int c=6;
    int result;
    result=max3(a,b,c);
    printf("%d\n",result);
    return 0;
}
int max2(int a,int b)
{
    if(a>=b)
    {
        return a;
    }
    else
    {
        return b;
    }
}

int max3(int a,int b,int c)
{
    int d;
    d=max2(a,b);
    return max2(c,d);
}   
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值