# 0x00
本文参考GDB官方文档的“A Sample gdb Session”部分。
# 0x01 所使用例程
本文所使用的例程为一段由C语言实现的栈代码,代码如下:
#include <stdio.h>
#include "stdlib.h"
#define Size 4
int top = -1, Stack[Size];
void push(void);
void show(void);
void pop(void);
void enter0(void);
void enter1(void);
int main(void) {
printf("U r in stack.c.\n");
int choice;
while (1) {
printf("\nPerform operations on the stack:");
printf("\n1.Push the element\n2.Pop the element\n3.Show\n4.End");
printf("\n\nEnter the choice: ");
scanf("%d", &choice);
switch (choice) {
case 1:
push();
break;
case 2:
pop();
break;
case 3:
show();
break;
case 4:
exit(0);
default:
printf("Wrong number\n");
}
}
return 0;
}
zz
void push(void) {
int input;
if (top >= Size - 1) {
printf("error:overflow\n");
return;
}
top += 1;
enter0();
scanf("%d", &input);
Stack[top] = input;
}
void pop(void) {
if (top == -1) {
printf("error:no more element\n");
return;
}
//Stack[top] = 0;
top -= 1;
}
void show(void) {
if (top == -1) {
printf("empty!\n");
return;
} else {
for (int i = 0; i <= top; ++i) {
printf("No:%d,value:%d\n", i, Stack[i]);
}
printf("Done!\n");
}
}
void enter0(void){
/*
* 相当于 printf("Enter the element to be added onto the stack: \n");
* 为了演示step功能,在函数里调用函数
*/
printf("Enter the element ");
enter1();
}
void enter1(void){
printf("to be added onto the stack: \n");
}
代码包含了压栈、出栈、遍历栈和退出四个功能,本文主要使用压栈的push()函数进行演示。为了演示GDB的step功能,将一句printf语句里面的内容拆分成了两个函数进行调用,两个函数分别为enter0()和enter1()。
# 0x02 编译
若要使用GDB进行debug,在使用GCC编译的时候需要加入-g参数。例如本文所使用的命令为:
gcc -g -o stacks stack.c
运行后会生成名为stacks的文件。
# 0x03 预处理程序
stacks为我们需要调试的文件,使用
gdb stacks
进入调试。输出:
前面是一大段的官方信息,最后一行才是我们需要看到的,GDB已经从stacks程序中读取了信息。
# 0x04 打断点
在run程序之前,我们需要对程序打断点,使其停到我们想要debug的位置。在本文中,我们针对push()函数进行debug,所以我们可以使用命令:
break push
# 或者使用break的简写b
b push
# 或者使用push()所在的行号 41行作为参数也可以
break 41
b 41
进行打断点。其中b和break等价,push和41等价。
# 0x05 运行程序
在设置好断点之后,使用
run
命令运行程序,程序会在断点处暂停运行。
运行程序后,我们输入选项1执行压栈子程序,由于我们之前设置了断点,GDB会在push()处暂停,如上图所示。
# 0x06 逐行调试
使用如下命令执行单步调试:
next
# 或简写
n
由上图可以看出,每执行一次逐行调试,程序会向下执行一行(当然44-46行不满足条件的代码会被跳过)。
# 0x07 查看变量值
在0x06中,我们可以看到,top执行了+1操作,由于其初始化为-1,此时其值应该为0。我们可以使用如下命令进行变量数值的查看。
print 变量名
# 或简写
p 变量名
由上图可以看出,此时栈顶top的值为0.
# 0x08 单步调试
在0x06中,我们可以看到程序即将执行第48行代码,如果我们此时使用“next”命令,那么会执行完函数到第49行。但如果我们想查看enter0()函数内部的执行情况,我们可以使用如下命令:
step
# 或简写
s
由上图我们可以看出,我们使用了step(即s)命令,调试从48行跳转到了第80行。同样,使用step命令进入enter1()后,程序从81跳转到了84行。
# 0x09 栈帧查看
进入子程序时,可以使用如下命令查看栈帧:
breaktrace
# 或缩写
bt
在本例中,我们首先进入了main(),后来进入push()、enter0()、enter1()。按照栈LIFO的原则,栈帧应该为以上函数的倒序。执行"bt"命令验证如下:
# 0x0A 变量值修改
例程中,我们设置了栈的数组中最多存放4个数,我们可以设置栈顶变量top值为3(从0开始编号)表示栈数组已满,在下一次执行push()的时候显示执行44行代码打印溢出报错。修改变量值可以使用如下命令:
p 变量名=设定数值
执行命令我们可以看出,top已经被修改为3.
# 0x0B 继续运行
使用如下命令解除程序暂停状态,继续运行,直到下一个断点。
continue
# 或简写
c
由于在0xA0中,我们已经设置top为3,再次执行push()会打印溢出错误,验证如下:
# 0x0C 退出调试
使用如下命令退出调试:
quit
# 或简写
q
在退出过程中,程序在运行会有退出确认提示,输入y回车即可退出。
--------------------------------------------------------------EOF-------------------------------------------------------------