嵌入式系统及应用Linux学习笔记(三)— GCC/G++编译器与调试器

Linux GCC/G++编译器与调试器

  • Linux GCC/G++编译器与调试器
    • GCC/G++编译器
    • GCC简介
    • GCC的使用
    • 程序调试工具gdb
      • GDB常用调试命令
        • 显示调试程序的源代码
        • 监视及更改变量值
        • 控制程序的执行
        • 设置和显示断点(BreakPoint)
        • 设置和显示观察点(WatchPoint)
        • 设置捕捉点(CatchPoint)
        • 维护停止点
        • 为停止点设定运行命令
        • 程序运行和单步调试

Linux GCC/G++编译器与调试器

GCC/G++编译器

  • 目前Linux下最常用的C语言编译器是GCC(GNU Compiler Collection),它是Linux平台编译器的事实标准。
  • GCC是GNU项目中符合ANSI C标准的编译系统,能够编译用C、C++和Object C等语言编写的程序。
  • GCC之所以被广泛采用,还因为它能支持各种不同的目标体系结构。它既支持基于宿主的开发,也支持交叉编译。
  • 目前,GCC支持的体系结构有四十余种,常见的有X86系列、ARM、PowerPC等。同时,GCC还能运行在不同的操作系统上,如Linux、Solaris、Windows等。
  • 开发者通常选择GCC来编译C语言编写的源代码,选择G++来编译C++源代码。

GCC简介

使用GCC编译程序时,编译过程可以被细分为四个阶段:

  • (1)预处理(Pre-Processing)
  • (2)编译(Compiling)
  • (3)汇编(Assembling)
  • (4)链接(Linking)

在这四个阶段中可以设置选项分别生成扩展名分别为“.i”、“.s”、“.o”的文件,以及最终可执行文件,各扩展名文件含义如下:

  • .c:最初的c源代码文件。
  • .i:经过编译预处理的源代码。
  • .s:汇编处理后的汇编代码。
  • .o:编译后的目标文件,含有最终编译出的机器码,但它里面所引用的其他文件中函数的内存位置尚未定义

下面以程序hello.c为例具体看一下GCC是如何完成以上四个步骤的,程序hello.c源代码如下所示。

#include
int main(void)
{
   printf("Hello World!/n");
   return 0;
}

(1)预处理阶段

  • 在该阶段,编译器将上述代码中的stdio.h编译进来。GCC首先调用cpp进行预处理,根据以字符#开头的命令修改原始的C程序。
  • 如hello.c中的指令#include
    告诉预处理器读系统头文件stdio.h的内容,并把它直接插入到程序文本中去,结果就得到经过编译预处理的源代码hello.i。

(2)编译阶段

  • GCC调用ccl检查代码的规范性,是否有语法错误等,以确定代码实际要做的工作,在检查无误后,把代码翻译成汇编语言,生成汇编处理后的汇编代码hello.s。这个阶段对应的GCC命令如下所示。
$gcc -S  hello.i  -o  hello.s  

(3)汇编阶段

  • GCC调用as把编译阶段生成的hello.s文件转成编译后的目标文件hello.o,但hello.c中所引用的其他文件中函数(如printf)的内存位置尚未定义。这个阶段对应的GCC命令如下所示:
  $gcc  -c  hello.s  -o  hello.o

(4)链接阶段

  • GCC调用ld将程序的目标文件与所需的所有附加的目标文件连接起来,最终生成可执行文件。如GCC找到hello.c所调用的函数printf函数库所在位置/user/lib,把函数的实现链接进来,生成最终的可执行文件hello,可以利用下面的命令完成。
 $gcc hello.o -o hello 

GCC的使用

格式:gcc [选项|文件]…
(1)总体选项
嵌入式系统及应用Linux学习笔记(三)— GCC/G++编译器与调试器_第1张图片
(2)链接选项
嵌入式系统及应用Linux学习笔记(三)— GCC/G++编译器与调试器_第2张图片
(3)警告选项
嵌入式系统及应用Linux学习笔记(三)— GCC/G++编译器与调试器_第3张图片
示例:
(1)编译当前目录下的文件helloworld.c。

  $gcc  helloworld.c

该命令将helloworld.c文件预处理、汇编、编译并链接形成可执行文件。这里未指定输出文件,默认输出为a.out,a.out为可执行程序文件名。

(2)将当前目录下的文件helloworld.c编译成名为helloworld的可执行文件。

  $gcc  –o  helloworld  helloworld.c

(3)将当前目录下的文件helloworld.c编译为汇编语言文件。

   $gcc  –S  helloworld.c 

该命令生成helloworld.c的汇编文件helloworld.s,使用的是AT&T汇编。

(4)将文件testfun.c 和文件test.c 编译成目标文件test
方法1:

 $gcc  testfun.c  test.c  -o  test

方法2:

 $gcc  -c  testfun.c         //将testfun.c编译成testfun.o
    $gcc  -c  test.c             //将test.c编译成test.o
    $gcc    testfun.o  test.o  -o  test     //将testfun.o和test.o链接成test

(5)编译当前目录下的程序bad.c,同时查看编译过程中所有报警信息。
程序bad.c的源码如下所示。

#include  
int main (void) 
{ 
   printf ("Two plus two is %f\n", 4); 
   return 0; 
}

程序调试工具gdb

gdb是Linux系统中一个功能强大的GNU调试程序,它可以调试C和C++程序,使程序开发者在程序运行时观察程序的内部结构和内存的使用情况。gdb提供如下功能:

  • (1)运行程序,设置所有的能影响程序运行的参数和环境。
  • (2)控制程序在指定的条件下停止运行。
  • (3)当程序停止时,可以检查程序的状态。
  • (4)修改程序的错误,并重新运行程序。
  • (5)动态监视程序中变量的值。
  • (6)可以单步逐行执行代码,观察程序的运行状态。
  • (7)分析崩溃程序产生的core文件。

1.启动gdb
要使用gdb调试程序,首先在编译时,必须把调试信息加到可执行文件中,可通过使用编译器gcc的 -g 参数完成。
如:$gcc -g hello.c -o hello

GDB常用调试命令

嵌入式系统及应用Linux学习笔记(三)— GCC/G++编译器与调试器_第4张图片
启动gdb的方法有以下四种:
(1)gdb
(2)gdb
(3)gdb core
gdb同时调试可执行程序program和文件core,文件core是程序崩溃时产生的文件,仅仅是一个内存映象(加上调试信息),主要是用来调试的。
(4)gdb
PID是程序运行时的进程号,gdb会自动绑定到该进程上,并调试。

显示调试程序的源代码

gdb可以用list(list指令可简写为l)命令来显示程序的源代码,其方法有如下几种:
(1)格式:list [file:]linenum
说明:显示程序file中的第linenum行周围的源代码。
(2)格式:list [file:]function
说明:显示程序file中的函数名为function的函数的源代码。
(3)格式:list
说明:显示当前行后面的源代码。
(4)格式:list –
说明:显示当前行前面的源代码。
(5)格式:list start,end
说明:显示从行号start到end之间的代码行。默认情况下,list命令显示10行代码。

监视及更改变量值

gdb可用print (print 指令可简写为 p)命令来监视及更改变量值。
格式:

  • print exp
    说明:显示或改变表达式exp的值,exp是符合所用编程语言语法规则的表达式,如调试c语言编写的程序,则exp符合c语言的语法规则。

示例:
(1)显示变量a的内容。
(gdb) print a
(2)显示变量a的长度。
(gdb) print sizeof(a)
(3)将变量a的值设定为 10。
(gdb) print (a=10)

控制程序的执行

调试程序过程中,经常需要暂停程序的运行,以便查看某些变量值的变化,及程序运行的流程。gdb可以方便地暂停程序的运行,常用的有以下几种暂停方式:断点(BreakPoint)、观察点(WatchPoint)、捕捉点(CatchPoint)。如要恢复程序运行,可以使用c命令或是continue命令。

设置和显示断点(BreakPoint)

gdb用break命令来设置断点,设置断点的方法有如下几种:
(1)格式:break
说明:将程序在进入指定函数function时停住,function为函数名。
(2)格式:break
说明:将程序在指定行号linenum停住,linenum为行号。
(3)格式:break +offsetbreak -offset
说明:将程序在当前行号的前面或后面的offset行停住,offiset为自然数。
(4)格式:break filename:linenum
说明:将程序在源文件filename的行号linenum处停住,filename为源文件名,linenum 为行号。
(5)格式:break filename:function
说明:将程序在源文件filename的function函数的入口处停住,filename为源文件名,function为函数名。
(6)格式:break *address
说明:将程序在运行的内存地址address处停住,address为程序运行的内存地址。
(7)格式:break
说明:将程序在下一条指令处停住。
(8)格式:break ... if
说明:将程序在条件condition成立停住,…表示上述的参数。
(9)格式:info breakpoints 说明:显示程序设置的所有断点。

设置和显示观察点(WatchPoint)

观察点一般来观察某个表达式(变量也是一种表达式)的值是否有变化,如果有变化,马上停住程序。设置观察点的方法有以下几种:
(1)格式:watch
说明:为表达式expr设置一个观察点,当表达式的值有变化时,停住程序。
( 2)格式:rwatch
说明:为表达式expr设置一个观察点,当表达式的值被读时,停住程序。
(3)格式:awatch
说明:为表达式expr设置一个观察点,当表达式的值被读或被写时,停住程序。

可使用info命令显示程序中设置了哪些观察点,命令如下所示。
格式:info watchpoints
说明:显示程序设置的所有观察点。

设置捕捉点(CatchPoint)

在程序运行过程中,当发生了某些事件,如动态链接库加载、暂停程序运行等,可设置捕捉点来捕捉这些事件,暂停程序运行。用户可对事件作出分析判断,并采取相应措施。设置捕捉点命令如下所示。
格式:catch
说明:将程序在事件event发生时停住
嵌入式系统及应用Linux学习笔记(三)— GCC/G++编译器与调试器_第5张图片

维护停止点
 在gdb中,可以使用delete、clear、disable、enable这几个命令进行维护。

(1)格式:clear
说明:清除所有的已定义的停止点。
(2)格式:clear clear
说明:清除所有设置在函数function上的停止点,或清除所有设置在源文件filename中函数function上的停止点。
(3)格式:clear clear
说明:清除所有设置在指定行linenum上的停止点,或清除所有设置在源文件filename中指定行linenum上的停止点。
linenum为行号,filename为源文件名。
(4)格式:delete [breakpoints] [range...]
说明:删除指定的断点。breakpoints为断点号,如果不指定断点号,表示删除所有的断点
(5)格式:disable [breakpoints] [range...]
说明:停用指定的断点。breakpoints为断点号,如果不指定断点号,表示停用所有的断点。停用的断点,gdb不会删除,需要时利用enable命令激活即可,简写命令是dis。
(6)格式:enable [breakpoints] [range...]
说明:激活指定的断点,breakpoints为断点号,如果不指定断点号,表示激活所有的断点。range 表示断点号的范围

为停止点设定运行命令

使用gdb提供的command命令可设置停止点的运行命令。
格式:

commands [bnum] ... command-list ... end

说明:为断点号bnum指写一个命令列表command-list,当程序停在该断点时,gdb会依次运行命令列表中的命令。bnum为断点号,command-list为执行的命令列表。

程序运行和单步调试

设置好停止点后,就可以使用run命令运行程序。
格式:(gdb) run
说明:该命令表示从程序开头执行程序,直到遇到断点或是程序执行完毕为止。
程序被停住,可用continue命令恢复程序的运行直到程序结束,或下一个断点到来。
(1)格式:

continue  [ignore-count]
c        [ignore-count]
fg       [ignore-count]

说明:continue,c,fg三个命令功能基本相同。恢复程序运行,直到程序结束,或是下一个断点到来。ignore-count表示忽略其后的断点次数。

(2)格式:step
说明:单步跟踪,count表示执行后面的count条指令后再停住,省略表示一条条执行。如果有函数调用,会进入该函数。进入函数的前提是,此函数被编译有debug信息。
(3)格式:next
说明:单步跟踪,count表示执行后面的count条指令后再停住,省略表示一条条执行。如果有函数调用,不会进入该函数。
(4)格式:set step-modeset step-mode on
说明:打开step-mode模式,进行单步跟踪时,程序不会因为没有debug信息而不停住。
(5)格式:set step-mod off
说明:关闭step-mode模式。
(6)格式:finish
说明:运行程序,直到当前函数完成返回。打印函数返回时的堆栈地址和返回值及参数值等信息。
(7)格式:until 或 u
说明:运行程序直到退出循环体,即取消在一个循环体内单步跟踪。
(8)格式:stepisi
nextini
说明:单步跟踪一条机器指令i,一条程序代码有可能由数条机器指令完成,step i和nexti可以单步执行机器指令。

打开可执行文件后,可根据需要在程序中加入断点或观察点,并运行程序。以helloworld程序为例,可在为变量赋值前加入断点,并运行程序。方法如下:

(gdb) break 5	// 在源代码第5行,即变量c赋值处加入断点
(gdb) run	// 运行程序

你可能感兴趣的:(Linux学习,编译器,linux)