从零开始学Linux之Linux调试器
在讲调试器之前,我先强调下自己使用gcc时遇到的一些问题,在网上找了但都没有合适的解答,后来重新试了一遍又正常了,结果隔了一天又报错了,我先把gcc的整个流程来一遍。例如:在gcc编译器中编译如下一段代码:
1.gcc的使用
1 #include<stdio.h>
2 #include<string.h>
3 void test()
4 {
5 printf("this is test function\n");
6 return;
7 }
8 int main()
9 {
10 printf("hello Word\n");
11 int i=0;
12 for(i=0;i<5;i++)
13 {
14 printf("---------\n");
15 }
16 test();
17 printf("over ~~\n");
18 return 0;
19
20 }
1.首先使用vim test.c
指令先创建一个test.c
文件,
2.进行预处理使用gcc -E test.c -o test.i
进行预处理,预处理是展开所有代码,引入头文件,进行宏替换,删除注释等工作。
3.使用gcc -S test.i -o test.s
进入编译阶段,编译阶段主要是检查语法语义错误,如果没有错误便将C语言解释为汇编语言。
4.使用gcc -c test.s -o test.o
进入汇编阶段,将汇编代码解释成为二进制的cpu指令
5.在使用gcc test.o -o test.exe
生成.exe文件将所有的指令打包在一起。在这里如果不是超级用户则需要进行一个提权操作,我这里总是出现问题,所以在这里要加一句chmod a+x test.o
进行提权操作。
或者可以直接gcc -g test.c -o test.exe
。
生成exe文件后,我们进行调试操作的学习
2.gdb调试器的一些基本概念
调试器的功能:调试,观察程序的运行过程,通常目的是为了排查程序的运行错误。
程序的错误分类:
1.编译错误
2.链接错误
3.运行时错误(运行中逻辑不对以及程序运行时崩溃)
gdb ./test.exe
进入调试,程序进行调试的前提是程序必须是一个debug版本的。gcc和g++默认生成release版本的,生成debug版本的需要加入-g
。例如:gcc -g test.c -o test.exe
可执行程序的分类:
1.debug
-调试版–不对代码进行优化,并且加入程序调试信息
2. release
-发布版–不包含调试信息,并且对代码进行优化
3.gdb调试器的常用调试指令
3.1流程控制
1.run
:直接运行程序;简写:r
2.start
:开始逐步调试,停留在main
函数的第一行;没有简写
3.list
查看调试行附近的代码 list 11
或者list test.c :11
;简写:l
4.next
:下一步-逐过程遇到函数直接运行完毕;简写:n
5.step
:下一步,逐语句,遇到函数进入函数继续调试;简写:s
6.until
:直接运行到指定位置 例如:until test.c:16
;
7.continue
:继续运行从当前调试位置继续运行;简写:c
;
8.break
: 打断点: break test.c:13
;简写:b
;
另外的一种方式: break test
:直接在函数处打断点
9.info break
:查看断点信息 简写:ib
10.delete
:删除断点 例如:delete 2
简写:d
11.watch
:watch
变量名称;给变量打断点,当变量发生改变时停下来。没有简写
12.退出是quit
3.2内存控制
1.print
:打印变量数据,设置或查看变量内容;简写:p
2.back trace
:查看函数调用栈,通常检测程序运行崩溃的位置,栈顶位置的函数就是程序崩溃的位置,简写bt
。
举个栗子:
有如下的代码:
#include<stdio.h>
2 #include<string.h>
3 void test()
4 {
5 printf("this is test function\n");
6 char *tmp=NULL;
7 memcpy(tmp,"hello bit!",10);//此处肯定有错误
8 printf("%s\n",tmp);
9 return;
10 }
11 int main()
12 {
13 printf("hello word\n");
14 int i=0;
15 for(i=0;i<5;i++)
16 {
17 printf("---------\n");
18 }
19 test();
20 printf("over ~~\n");
21 return 0;
22
23 }
我们对此程序进行编译连接后进入调试模式,使用back trace
或者bt
命令查看调用栈的信息
错误信息告诉我们在main()
函数调用test函数时的的第7行出现崩溃。
2.make/Makefile
make/Makefile:
项目的自动化构建工具
Makefile
:是一个文本文件,记录一个项目的构建规则流程
Makefile
的编写规则:
目标:依赖对象
\t
为了生成目标对象要执行的命令。
1 test:test.c
2 gcc test.c -o test
预定义变量的使用: $@
$^
$ <
,用于指令中, $@
表示目标对象, $^
表示依赖对象, $ <
表示对象的第一个。
1 test:test.c test1.c test2.c
2 gcc $^ -o $@
1 #$(lecard ./*.c) 获取当前目录下以.c结尾的文件的文件名称
2 src=#$(lecard ./*.c)
3 #保存到变量src中
4 #$表示对变量的使用
5 test:$(src)
6 gcc $^ -o $@
make
:是一个解释程序,对Makefile
记录的构建规则流程逐步解释执行。
make
的解释执行规则:
1.在命令行中敲击
make
指令,则表示运行make
解释程序,程序会在当前目录下找到名称为makefile/Makefile
的文件,解释执行其中的项目构建规则。
2.在规则中,找到要生成的第一个目标对象,判断目标对象是否已经存在,存在的话是否需要重新生成,根据源码文件的最后一次修改时间对比。执行对象生成命令。
3.make在每一个Makefile
中只会找到第一个目标对象进行生成,生成后就会退出。
7 test.o:test.c
8 gcc $^ -o $@
9 test1.o:test1.c
10 gcc $^ -o $@
11 test2.o:test2.c
12 gcc $^ -o $@
13 test: test.o test1.o test2.o
14 gcc $^ -o $@
4.
make
在生成目标对象的目标时先找到依赖对象,再寻找目标对象。
8 test: test.o test1.o test2.o
9 gcc $^ -o $@
10 test.o:test.c
11 gcc -c $^ -o $@
12 test1.o:test1.c
13 gcc -c $^ -o $@
14 test2.o:test2.c
15 gcc -c $^ -o $@
或者:
8 test: test.o test1.o test2.o
9 gcc $^ -o $@
10 %.o:%.c
11 gcc -c $< -o $@ #只汇编不链接
或者:
5.可以使用
make clean
指令删除.o
文件
1 #$(lecard ./*.c) 获取当前目录下以.c结尾的文件的文件名称
2 src=$(wildcard ./*.c)
3 #保存到变量src中
4 #$表示对变量的使用
5 #test:$(src)
6 # gcc $^ -o $@
7 # 将src变量的内容中的.c替换为.o然后存放到obj变量中
8 obj=$(patsubst %.c, %.o, $(src))
9
10 test:$(obj)
11 gcc $^ -o $@
12
13 %.o:%.c
14 gcc -c $< -o $@
15 #.PHONY:clean#每次生成都是最新的
16 clean:
17 rm -rf $(obj) test
伪对象
声明一个对象与外部文件无关,每次都要重新生成(不会因为当前文件是最新的不需要重新生成)如果有对象不管外部是否存在,都要执行语句,则可以声明为伪对象。
PHONY:clean