lee-romantic 's Blog
Everything is OK!
Toggle navigation
lee-romantic 's Blog
主页
About Me
归档
标签
gcc(gdb调试)的使用简介与命令行参数说明
2019-06-19 16:05:45
106
0
0
lee-romantic
#gcc(g++)基础 ##(1) 编译过程 通常c/c++语言的编译通常分为如下几个阶段:**预处理,编译,汇编,链接。** ``` 预编译处理(.i、.c) -> 编译、优化程序(.s)->汇编程序(.obj、.o、.a、.ko) -> 链接程序(.exe、.elf、.axf等) ```  ##(2)理解编译过程 为了更好地理解gcc的工作过程,我们可以让在gcc工作的4个阶段中的任何一个阶段中停止下来,如下: ####第一步:进行预编译,使用 -E 参数 gcc -E test.c -o test.i 查看 test.i 文件中的内容,会发现 stdio.h 的内容确实都插到文件里去了,而其他应当被预处理的宏定义也都做了相应的处理。 ####第二步:生成汇编源文件 gcc -S test.c -o test.s ####第三步:将 test.s 编译为目标代码,使用 -c 参数 gcc -c test.s -o test.o ####第四步:将生成的目标文件链接成可执行文件 gcc test.o - o test ##(3)分步编译 我们很自然的会联想到,我们能不能分步编译,而不是在每个阶段暂停,以便于我们更好的理解编译过程,答案是可以的。 1,**预处理**:替换宏定义和头文件( 为什么不能同时编译多个.c 和.h? 因为.h 不用编译,gcc -E a.c -o a.i 再多来几个.c都行,但不能有.h) gcc -E a.h a.c -o a.i // 这样会报错 gcc -E a.c -o a.i // 应该去掉.h (#include就是预编译指令,预编译阶段主要处理以#开始的预编译指令,比如删除注释,添加行号、文件名标识....用include包含某.h文件时,将被包含文件插入该预编译指令的位置。生产的.o目标文件一般与.c对应而不是gcc可以编译.h成.o ) 2,**编译**:将代码编译为汇编文件(ccl c编译器) gcc -S a.i -o a.s 3,**汇编**:将汇编文件转换成二进制文件机器码(as 汇编工具) gcc -c a.s -o a.o 4,**链接**:对应用的库函数进行链接重定位 (ld),生成可执行文件 gcc a.o -o a.out // a.out可以替换成a ## (4) 生成动态库和静态库 https://www.cnblogs.com/fnlingnzb-learner/p/8059251.html ##(5) C++编译过程(包括中间文件): `.exe`:可执行文件,点击即可运行 `.ilk`:当选定渐增型编译链接时,连接器自动生成ILK文件,记录链接信息 `.obj`:目标文件,obj文件与cpp文件名字一一对应 `.pch`:prcompiled-header,预编译头文件 `.idb`:文件保存的信息,使编译器在重新编译的时候只重新编译改动过的函数和最新类定义改动过的源文件,这样可提高变异速度 `.pdb`:Program Database,即程序数据库文件,用来记录调试信息 `.dsp`:Developer Studio Project,配置文件 `.ncb`:No Compile Browser,其中存放了供ClassView、WizardBar和Component Gallery使用的信息,由VC开发环境自动生成 `.plg`:超文本文件,可以用IE打开,记录build的过程 `.cpp`:C++源文件 参考:https://zhuanlan.zhihu.com/p/45402323 ##(6)相关参数 gcc最基本的用法是:`gcc [options] [filenames]` 其中,options就是编译器所需要的参数,filenames给出相关的文件名称,不同参数的先后顺序对执行结果没有影响,只有在使用同类参数时的先后顺序才需要考虑。 最常用的有以下参数: `-E`:预编译后停下来,**生成后缀为 .i 的预编译文件**。 `-S`:编译后停下来,**生成后缀为 .s 的汇编源文件**。 `-c `: **只编译和汇编,不链接成为可执行文件**。编译器只是由输入的 .c 等源代码文件生成.o为后缀的目标文件,通常用于编译不包含主程序的子程序文件。 `-o output_filename`:确定输出文件的名称为output_filename。同时这个名称不能和源文件同名。如果不给出这个选项,gcc就给出默认的可执行文件a.out `-g`产生符号调试工具(GNU的gdb)所必要的符号信息。想要对源代码进行调试,就必须加入这个选项。 `-O`:对程序进行优化编译、链接。采用这个选项,整个源代码会在编译、链接过程中进行优化处理,这样产生的可执行文件的执行效率可以提高,但是编译、链接的速度就相应地要慢一些,而且对执行文件的调试会产生一定的影响,造成一些执行效果与对应源文件代码不一致等一些令人“困惑”的情况。因此,**一般在编译输出软件发行版时使用此选项。** `-O2`:比 -O 更好的优化编译、链接。当然整个编译链接过程会更慢。 `-Idirname`:将 dirname所指出的目录加入到程序头文件目录列表中,是在预编译过程中使用的参数。**说明:**C程序中的头文件包含两种情况: ``` #include <stdio.h> #include "stdio.h" ``` 其中,使用尖括号(<>),预处理程序 cpp 在系统默认包含文件目录(如/usr/include)中搜索相应的文件;使用双引号,预处理程序 cpp 首先在当前目录中搜寻头文件,如果没有找到,就到指定的 dirname 目录中去寻找。在程序设计中,如果需要的这种包含文件分别分布在不同的目录中,就需要逐个使用 -I选项给出搜索路径。 `-Ldirname`将dirname所指出的目录加入到程序函数库文件的目录列表中,是在链接过程中使用的参数。在默认状态下,链接程序 ld 在系统默认路径中(如 /usr/lib)寻找所需要的库文件。这个选项告诉链接程序,首先到 -L 指定的目录中去寻找,然后到系统默认路径中寻找;如果函数库存放在多个目录下,就需要依次使用这个选项,给出相应的存放目录。 `-lname ` 链接时装载名为 libname.a 的函数库。该函数库位于系统默认的目录或者由 -L 选项确定的目录下。例如,-lm 表示链接名为 libm.a 的数学函数库。多线程编程中编译时的的-lpthread参数也是属于此类,只有这样才能调用pthread.h中声明的函数。 更多可参考: https://blog.csdn.net/delphiwcdj/article/details/6555073 #gdb基础 ##(1)基本使用 1、首先需要用gcc(g++) 对源文件进行编译生成可执行文件,并且在编译时加上选项-g,把调试信息加到目标文件中。 2、假设生成的可执行文件为test,那么`gdb test` 可以用gdb打开test文件,然后通过`break linenum`设置断点,如`break 5`,在第5行设置断点。 可以输入list查看源文件和行号,方便设置断点。断点设置好后就可以run命令运行到断点处了。可以使用`print a`,或者`watch a` 来观察变量的值,或者变量的变化情况。 ``` 预处理(pre-processing)E:插入头文件,替换宏 编译(Compiling)S:编译成汇编 汇编(Assembling) c:编译成目标文件 链接 (Linking):链接到库中,变成可执行文件 gcc -E hello.c -o hello.i gcc -S hello.i –o hello.s gcc –c hello.s –o hello.o gcc hello.s –o hello 链接,生成可执行文件 /hello 运行 也可以一次性完成: gcc hello.c –o hello ``` ##(2)初始化 假设test.out是我们使用带-g参数gcc命令生成的可执行文件,使用`gdb test.out`可以直接进入调试环境;也可以使用`gdb`先进入gdb环境,然后输入`file test.out`读入可执行文件的调试信息后,就可以正常调试了。 ##(3)开始调试 调试前,查看源代码:`list 函数名`或者`list[行数]`或者切换到更直观的tui界面查看执行代码,快捷键`ctrl+x+a` 有些代码执行时是带参数的,可以设置程序运行参数:`set args + [参数1][参数2][参数3]...`,设置完参数后,直接`run`即可;或者直接`run test.out [参数1][参数2][参数3]...`,直接`run [参数1][参数2][参数3]...`也可以 ##(4)暂停程序 安排程序的执行在某个地方暂停,以便检查变量的值,从而得到关于程序错误所在位置的线索。 ###1)设置断点: 调试时, 设置断点,暂停程序的方式,主要有下列三种方式: ####断点:通知GDB在程序中的特定位置暂停执行。 a、普通断点 ``` `break + [源代码行号][源代码函数名][内存地址]`,也可以通过`break 文件名:行号/函数名`设置断点 ``` b、条件断点 ``` `break ... if condition ` ``` ...可以是上述任一参数,condition条件。例如在循环体中可以设置`break ... if i = 100` 来设置循环次数。又例如:`break main if argc>1` 条件中断中的condition可以包含如下形式,但是必须是布尔值:  可以对正常断点设置条件以将它转变为条件断点。例如,如果设置了断点3为无条件断点,但是希望添加添加i==3,只有键入: ``` (gdb) cond 3 i==3 //或者 condition 3 i==3 ``` 如果以后要删除条件,但是保持该断点,只要键入: ``` (gdb) cond 3 //或者 condition 3 //这样做,相当于传了个空的条件,自然是删除了条件了 ``` c、临时断点 在GDB中,`tbreak`命令与break相似,但是这一命令设置的断点的有效期限只到首次到达指定行时为止 ####监视点:通知GDB当特定内存位置的值发生变化时暂停执行 ``` a、`watch + [变量][表达式]` 当变量或表达式值改变时即停住程序。 b、`rwatch + [变量][表达式]` 当变量或表达式被读时,停住程序。 c、`awatch + [变量][表达式]` 当变量或表达式被读或被写时,停住程序。 ``` ####捕获点:通知GDB当特定事件发生时暂停执行。 ``` `catch + event` 当event发生时,停住程序。event可以是下面的内容: 1)、`throw` 一个C++抛出的异常。(throw为关键字) 2)、`catch` 一个C++捕捉到的异常。(catch为关键字) 3)、`exec` 调用系统调用exec时。(exec为关键字,目前此功能只在HP-UX下有用) 4)、`fork` 调用系统调用fork时。(fork为关键字,目前此功能只在HP-UX下有用) 5)、`vfork` 调用系统调用vfork时。(vfork为关键字,目前此功能只在HP-UX下有用) 6)、`load` 或 `load` 载入共享库(动态链接库)时。(load为关键字, 目前此功能只在HP-UX下有用) 7)、`unload` 或 `unload` 卸载共享库(动态链接库)时。(unload为关 键字,目前此功能只在HP-UX下有用) ``` 当然,还有比如捕捉信号,线程中断等其他方式,可参考链接等相关资料。 ###2)查看断点 程序员创建的每个断点(包括断点、监视点和捕获点)都被标识为从1开始的唯一整数标识符。这个标识符用来执行该断点上的各种操作。使用下列指令查看所有断点,监视点,捕获点: ``` info break info breakpoint info breakpoints i b //效果完全一致 ``` 使用以上指令查看断点时,所有断点的选项为: 序号|选项|释义 :---:|:---:| 1|标识符(num)|断点的唯一标识符 2|类型(type)|这个字段指出该断点是断点、监视点还是捕获点 3|部署(disp)|每个断点都有一个部署,指示断点下次引起GDB暂停程序的执行后该断点上会发生什么事情。 4|启用状态(enb)|这个字段说明断点当前是启用还是禁用的 5|地址(Address)|这是内存中设置断点的位置。 6|位置(what)|what字段显示了断点所在的位置的行号和文件名 7||每个断点信息最下方,将显示(如果通过`command[s] +断点号` 设置了的话)自定义的调试脚本 **附:部署(disp)三种类型**: (1)`保持(keep)`,下次到达断点后不改变断点;(2)`删除(del)`,下次到达断点后删除断点,临时断点(tbreak设置);(3)`禁用(dis)`,下次到达后会禁用断点,使用enable once命令设置的 ###3)删除断点 如果想要删除断点,可以通过delete命令以及断点标识符,例如 `(gdb) delete 1 2 3`将删除断点1,断点2和断点3,如果直接`delete`,则会直接删除所有断点; 如果有些断点目前暂时不使用,不想删除,则可以使之失效即可,指令为`disable 断点号`,与之对应的,使断点重新有效则为:`enable+断点号` 每个GDB都有一个焦点,可以将它看作当前“活动”文件。这意味着除非对命令做了限定,否则都是在具有GDB的焦点的文件上执行命令。默认情况下,具有GDB的初始焦点的文件是包含main()函数的文件,但是当发生如下任一动作时,焦点会转移到不同的文件上。 1)向不同的源文件应用list命令 2)进入位于不同的源代码文件中的代码 3)当在不同的源代码文件中执行代码时GDB遇到断点 `clear` 指令,清除GDB将执行的下一个指令处的断点。这种方法适用于要删除GDB已经到达的断点额情况。 ###4)恢复执行 恢复执行的方法有3类。 第一类是使用`step`和`next`“单步”调试程序(简写为`s`和`n`),仅执行代码的下一行然后再次暂停。二者区别在于,step将进入函数内部单步执行,next不会。二者都可以 `step/next+n`,代表向下执行n步。 第二类由使用`continue`组成,简写为`c`,使GDB无条件地恢复程序的执行,直到遇到另一个断点或程序结束。 第三类方法涉及条件:用`finish`或`until`命令恢复。在这种情况下,GDB会恢复执行;程序继续运行直到遇到某个预先确定的条件(比如,到达函数的末尾),到达另一个断点,或者程序完成。 如果你在一个不是`main()`的函数中,`finish`命令会导致GDB恢复执行,直到恰好在函数返回之后为止。finish的另一个常见用途是当不小心单步进入原本希望单步越过的函数时(换言之,当需要使用next时使用了step)。在这种情况下,使用finish可以讲你正好放回到使用next会位于的位置。如果在一个递归函数中,finish只会将你带到递归的上一层。 `finish`命令在不进一步在函数中暂停(除了中间断点)的情况下,完成当前函数的执行。类似地,`until`命令(简写为`u`)通常用来在不进一步在循环中暂停(除了循环中的中间断点)的情况下,完成正在执行的循环。也就是说,`finish将直接执行到当前函数结束的位置,until将直接执行到循环完成后的位置`。 当然,二者如果中途遇到断点,均会停下来。 很好的参考资料: https://yq.aliyun.com/articles/296948 ##(5)查看调试信息 前面一部分花了很多篇幅总结断点的操作,这里我们通过断点,来获取一些调试过程中的信息。 ###1)断点命令列表 当程序停在断点时,可以通过`print 变量名`,打印出我们所关心的变量的值,但是每次都得手动print的话,效率实在低下;gdb强大的地方在于我们可以为每个 断点自定义一段命令,让程序每次停在该断点处,都自动的打印出我们所关心的信息,或者自动的完成其他的一些操作。这就涉及到`断点命令列表`。使用command[s] 命令设置断点命令列表: ``` command breakpoint-number ... command ... end ``` 其中`breakpoint-number`是要将命令添加到其上的断点的标识符,`command[s]`是用行分隔的任何有效GDB命令列表。逐条输入命令,然后键入end表示输入命令完毕。从那以后,每当GDB在这个断点处中断时,它都会执行输入的任何命令。 比如,我们想在断点2处,每次都让其打印出变量i的值,并自动continue,并且减少多余信息的输出也就是要加入silent命令,那么我们可以在gdb命令行这样输入断点命令列表: ``` commands 2 silent printf "the variable i :%d \n",i continue ``` 在断点命令行列表中,还可以使用define定义的宏(暂时不推荐): 比如我们定义宏`print_and_go`: ``` define print_and_go printf $arg0,$arg1 continue end ``` 在断点2的命令行列表就可以这么写: ``` command 2 silent print_and_go "the variable i :%d \n" i end ``` 使用宏的方式按道理来说应该效果是一样的,但是实际测试时,不知道为什么continue命令不能自动执行,哪怕continue命令不写在宏里面都不行。断点时,需要按enter才能continue,继续执行。虽然效果差不过,但是如果断点次数过多,岂不是要一直按enter??? 这个地方确实没弄明白之前,先不要使用宏定义吧。 ###2)监视点 除了可以使用print以及断点命令列表查看变量值以外,某些时候还可以利用监视点的特性,来查看变量的值。前面也有讲到,监视点是一种特殊类型的断点,它类似于正常断点,是要求GDB暂停程序执行的指令。监视点是指示GDB每当某个表达式改变了值就暂停执行的指令。 例如: ``` (gdb) watch i ``` 它会使得每当i改变值时GDB就暂停。 再比如: ``` #include<stdio.h> int i=0; int main() { i=3; printf("i is %d.\n",i); i=5; printf("i is %d.\n",i); return 0; } ``` 针对以上代码,如果要监视i的值,那么先通过`break main`在main处设置断点,然后`run`,停在断点处,现在i已经在作用域中了,现在通过`watch i>4`设置监视点,并`continue`通知GDB继续执行程序。 我们每当i大于4时得到通知。因此在main()的入口处放一个断点,以便让i在作用域中,并设置一个监视点以指出i何时大于4.不能在i上设置监视点,因为在程序运行之前,i不存在。因此必须现在main()上设置断点,然后在i上设置监视点,以便于监视i的值。 ###3)显示数组中的值 比如声明数组:`int x[25];` 方法是通过键入:`p x`或者`print x` 但是,如果是动态创建的数组会是什么样呢?比如: ``` int *x ... x=(int *)malloc(25*sizeof(int)); ``` 如果要在GDB中输出数组,就不能输入:`(gdb) p x` 可以简单打印数组地址。或者键入:`(gdb) p *x` 这样只会输出数组的一个元素——x[0]。仍然可以像在命令 p x[5]中那样输入单个元素,但是不能简单地在x上使用print命令输出整个数组。 在GDB中,可以通过创建一个人工数组来解决这个问题。如下: ``` #include<stdio.h> #include<stdlib.h> int *x; void main() { x=(int*)malloc(25*sizeof(int)); x[3]=12; } ``` 先在`break main`,main处设断点; 然后单步执行到`x[3] =12`处结束,通过指令`p *x@25`构造人工数组来查看整个数组的值,最后显示为`{0, 0, 0, 12, 0 <repeats 21 times>}`,人工数组的长度可以是任意的,不会报错。 我们可以看到,一般形式为: ``` *pointer@number_of_elements ``` GDB还允许在适当的时候使用类型强制转换,比如: ``` (gdb) p (int [25])*x ``` 结果显示为: ``` $2={0,0,0,12,0 <repeats 21 times>} ``` ##(6)退出gdb 命令行输入`quit`或者快捷键`ctrl d`, 也可以直接使用结束进程的快捷键:`ctrl z` ##(7)指令总结 |序号|单步调试指令|简写|释义| :---:|:---| |1|run|r|运行程序| |2|stop|stop|暂停程序执行 |3|break|b|设置断点,可用形式(b文件名:行号/b 行号) |4|condition|cond|可以对正常断点设置条件以将它转变为条件断点,参考之前的断点讲解部分 |5|continue|c|继续,直至下一个断点 |6|step|s|在某个函数断点处,执行s可以步入该函数单步调试 |7|next|n|执行到当前模块下的下一条指令 |8|enable||使得断点有效(enable断点号) |9|disable||使得断点无效(disable断点号) |10|delete||删除断点(delete断点号),直接detele将删除所有断点 |11|clear||清除断点 |12|ignore||忽略断点(ignore断点号忽略断点次数) 13|list|l|显示当前执行程序的代码行,默认10行,可手动修改 14|info||比如: info break或者info breakpoint[s]可以查看当前所有断点信息 15|print|p|打印参数信息(p参数/变量名/数组成员/结构体成员) 16|ptype/whatis||显示数据类型 17|backtrace|bt|查看调用堆栈信息 18 |frame||配合backtrace使用,定位调用栈帧 19|command[s]||在break后加command,会提示输入信息:此时类似一个脚本程序,你可以输入想要在断点处打印的信息,输入quit退出。(非常适合看循环单步调试的信息,不用每次手动输入) 20|search text||至上往下搜索,显示在当前文件中包含text的代码行 21|reverse-search text||至下往上搜索,显示包含text的代码行 22|attach||attach加进程号,进程相关调试 23|kill||结束当前的调试 24|quit||退出debug ## 参考链接: 较为详细:https://blog.csdn.net/wojiuguowei/article/details/50920746 # 拓展(lldb) LLDB是个开源的内置于XCode的具有REPL(read-eval-print-loop)特征的Debugger,其可以安装C++或者Python插件,是LLVM项目的调试器部分,其创建是为了与LLVM编译器一起使用。[GDB与LLDB命令对照](https://lldb.llvm.org/use/map.html)
上一篇:
linux可执行文件没有扩展名
下一篇:
vim部分指令使用
0
赞
106 人读过
新浪微博
微信
腾讯微博
QQ空间
人人网
提交评论
立即登录
, 发表评论.
没有帐号?
立即注册
0
条评论
More...
文档导航
没有帐号? 立即注册