欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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

程序员文章站 2022-07-14 16:35:31
...

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<stdio.h>
int main(void)
{
   printf("Hello World!/n");
   return 0;
}

(1)预处理阶段

  • 在该阶段,编译器将上述代码中的stdio.h编译进来。GCC首先调用cpp进行预处理,根据以字符#开头的命令修改原始的C程序。
  • 如hello.c中的指令#include
    <stdio.h>告诉预处理器读系统头文件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++编译器与调试器
(2)链接选项
嵌入式系统及应用Linux学习笔记(三)— GCC/G++编译器与调试器
(3)警告选项
嵌入式系统及应用Linux学习笔记(三)— GCC/G++编译器与调试器
示例:
(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 <stdio.h> 
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++编译器与调试器
启动gdb的方法有以下四种:
(1)gdb
(2)gdb <program>
(3)gdb <program> core
gdb同时调试可执行程序program和文件core,文件core是程序崩溃时产生的文件,仅仅是一个内存映象(加上调试信息),主要是用来调试的。
(4)gdb <program> <PID>
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时停住,function为函数名。
(2)格式:break <linenum>
说明:将程序在指定行号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>
说明:将程序在条件condition成立停住,…表示上述的参数。
(9)格式:info breakpoints 说明:显示程序设置的所有断点。

设置和显示观察点(WatchPoint)

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

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

设置捕捉点(CatchPoint)

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

维护停止点

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

(1)格式:clear
说明:清除所有的已定义的停止点。
(2)格式:clear <function>clear <filename:function>
说明:清除所有设置在函数function上的停止点,或清除所有设置在源文件filename中函数function上的停止点。
(3)格式:clear <linenum>clear <filename:linenum>
说明:清除所有设置在指定行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表示执行后面的count条指令后再停住,省略表示一条条执行。如果有函数调用,会进入该函数。进入函数的前提是,此函数被编译有debug信息。
(3)格式:next <count>
说明:单步跟踪,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	// 运行程序