Linux下的编译与调试工具

Linux下的编译与调试工具

 

1.GCC

(1)介绍

       GCC是Linux环境下的一套工具集,全称是“GNU Compiler Collection”,它是Linux下的标准工具集,有三大特点:

l  支持多种编程语言,如Ada,C++等;

l  支持多个硬件平台,可交叉编译;

l  支持多种操作系统,如Linux,Solaris和Windows等。

(2)编译四个步骤的简单回顾

1)预处理(Preprocessing)阶段

       将源代码文件(*.c)与头文件(*.h)进行预处理,其中诸如#include、#ifdef等指令都被处理了。

2)编译(Compiling)阶段

       将预处理后的文件编译成汇编语言文件(*.s)。

3)汇编(Assembling)阶段

       将汇编语言文件汇编成目标文件。

4)链接(Linking)阶段

       将目标文件连同可能存在的库链接成可执行文件。

       简短回顾之后,下面我们来看看基本的使用方式。

(3)基本使用方式(编译、编译并链接)

l  将一个源文件编译成对应的目标文件,可以使用:

              gcc -c test.c

              生成对应的test.o文件

l  如果想给生成的目标文件取个新名字,可以这样:

              gcc -c test.c -o newtest.o

l  将源代码文件连同头文件编译并链接成可执行文件:

gcc -o program source1.c source2.c source3.c a.h b.h

(4)复杂使用方法

       现实情况中我们可能会对一个项目的源代码目录进行归类和整理,比如典型地,将源代码文件放在目录的src目录下,把头文件放在headers下,那么在编译连接生成可执行文件时怎么告诉gcc去哪搜索头文件呢,答案是使用“-I”选项:

l  比如,在src目录下,可以这样使用gcc命令:

              gcc  -o program source1.c source2.c -I../headers

              该选项可以使用多次,指定多个目录:

              gcc  -o program source1.c source2.c -I../headers -I../inc

l  另一个比较有用的选项是“-D”选项,它类似于预处理命令“#define”,它的好处就在于,每次定义宏不用修改源代码,此选项常用于DEBUG宏,比如我们在代码中写入“#ifdef DEBUG”,当此宏定义时执行一些调试语句,那么在测试阶段我们可以在命令行上使用“-DDEBUG”来开启,测试完毕去掉这个选项取消宏定义即可。

(5)警告选项

l  可以使用“-Wall”开启全部警告选项,这样gcc在处理过程中会尽量挖掘代码中的错误并提出警告或者提示,这样方便程序员编写出高质量的软件,另一个常用的选项是“-Werror”,该选项将警告也当成错误,除非解决掉所有的错误和警告,否则无法编译通过。

其他常用警告选项:

cast-align

      Warn whenever a pointer is cast and the required alignment is increased.

sign-compare

      Warn if a signed vs. unsigned compare could yield an incorrect result.

missing-prototypes

      Warn if a global function is used without a previous prototype definition.

packed

      Warn if a structure is provided with the packed attribute and no packing occurs.

padded

      Warn if a structure is padded to align it (resulting in a larger structure).

unreachable-code

      Warn if code is found that can never be executed.

inline

      Warn if a function marked as inline could not be inlined.

disabled-optimization

Warn that the optimizer was not able to perform a given optimization (required too much

time or resources to perform).

(6)优化选项

       GCC提供五个优化级别,如下:

-O0

No optimization (the default level).

-O, -O1

      Tries to reduce both compilation time and image size.

-O2

More optimizations than -O1, but only those that don’t increase size over speed (or vice-versa).

-Os

      Optimize for resulting image size (all -O2, except for those that increase size).

-O3

      Even more optimizations (-O2, plus a couple more)

       现在我们来深入优化级别内部看看其有什么本质区别。

1)-O0优化级别

       不做任何优化,方便调试程序,在调试阶段推荐使用这个级别。

2)-O1优化级别

       这一级别的主要目标是尽可能快地编译并减少最终生成代码的大小和生成程序的执行时间。这一级别包含如下优化选项:

defer-pop

      Defer popping function args from stack until necessary.

thread-jumps

      Perform jump threading optimizations (to avoid jumps to jumps).

branch-probabilities

      Use branch profiling to optimize branches.

cprop-registers

      Perform a register copy-propagation optimization pass.

guess-branch-probability

      Enable guessing of branch probabilities.

omit-frame-pointer

      Do not generate stack-frames (if possible).

       注意:如果你想单独使用其中某个选项,可以使用“-f”,如“-fdefer-pop”,如果在这一优化级别中你想关闭某个特殊的选项,就使用相反形式,如“-fno-defer-pop”,请注意举一反三。

3)-O2优化级别

       该级别包含了O1的全部优化,但不会牺牲速度来换取空间或牺牲空间来换取时间,它主要包含以下优化选项:

align-loops

Align the start of loops.

align-jumps

      Align the labels that are only reachable by jumps.

align-labels

      Align all labels.

align-functions

      Align the beginning of functions.

optimize-sibling-calls

      Optimize sibling and tail recursive calls.

cse-follow-jumps

      When performing CSE, follow jumps to their targets.

cse-skip-blocks

      When performing CSE, follow conditional jumps.

gcse

      Perform global common subexpression elimination.

expensive-optimizations

      Perform a set of expensive optimizations.

strength-reduce

      Perform strength reduction optimizations.

rerun-cse-after-loop

      Rerun CSE after loop optimizations.

rerun-loop-opt

      Rerun the loop optimizer twice.

caller-saves

      Enable register saving around function calls.

force-mem

      Copy memory operands into registers before using.

peephole2

      Enable an rtl peephole pass before sched2.

regmove

      Enable register move optimizations.

strict-aliasing

      Assume that strict aliasing rules apply.

delete-null-pointer-checks

      Delete useless null pointer checks.

reorder-blocks

      Reorder basic blocks to improve code placement.

schedule-insns

      Reschedule instructions before register allocation.

schedule-insns2

      Reschedule instructions after register allocation

4)-Os优化级别

       这一优化级别取消了O2中会导致最终生成的可执行程序变大的选项,如“-falign-labels”,“-falign-jumps”,“-falign-labels”和“-falign-functions”。这些都可能潜在地导致最终生成的程序变大,因此取消这些选项以便于生成较小的可执行程序。

5)-O3优化级别

       是GCC提供的最高优化级别了,除了O2中包含的那些以外,这一级还包含如下优化选项:

-finline-functions

      Inline simple functions into the calling function.

 -frename-registers

      Optimize register allocation for architectures with large numbers of registers (makes debugging difficult).

6)针对某一CPU架构的优化

       除以上种种之外,我们还可以使用“-mcpu”选项来针对特定CPU提供优化,如下表所示:

i386 DX/SX/CX/EX/SO                                                        i386

i486 DX/SX/DX2/SL/SX2/DX4                                       i486

487                                                                               i486

Pentium                                                                        pentium

Pentium MMX                                                               pentium-mmx

Pentium Pro                                                                  pentiumpro

Pentium II                                                                     pentium2

Celeron                                                                         pentium2

Pentium III                                                                    pentium3

Pentium IV                                                                    pentium4

Via C3                                                                          c3

Winchip 2                                                                      winchip2

Winchip C6-2                                                                winchip-c6

AMD K5                                                                       i586

AMD K6                                                                       k6

AMD K6 II                                                                   k6-2

AMD K6 III                                                                  k6-3

AMD Athlon                                                                  athlon

AMD Athlon 4                                                               athlon

AMD Athlon XP/MP                                                      athlon

AMD Duron                                                                  athlon

AMD Tbird                                                                   athlon-tbird

       于是,如果我们编译的程序需要对Celeron处理器进行特殊优化,那么可以使用如下命令:

       gcc -mcpu=pentium2 test.c -o test

(7)调试选项

       如果我们编译生成的可执行文件要用gdb调试器来调试,那么我们在编译时可以指定“-g”参数,这样编译生成的可执行文件稍微大一点,但是带有必要的调试信息,可以使用在随后的gdb控制台中。实际使用中,我们最好加上“-O0”参数,取消所有的编译器优化,这样可以避免一些不必要的问题。

       一般来说,使用“-g”将产生操作系统本地的调试信息,这也就足够了,但是gcc提供了相当多的相关调试选项来精确控制,常用的选项如下所示:

-ggdb

Produce debugging information for use by GDB.

-gstabs

Produce debugging information in stabs format (if that is supported), without GDB extensions. This is the format used by DBX on most BSD systems.

-gstabs+

Produce debugging information in stabs format (if that is supported), using GNU extensions understood only by the GNU debugger (GDB)

-gcoff

Produce debugging information in COFF format (if that is supported).

-gxcoff

Produce debugging information in XCOFF format (if that is supported).

-gxcoff+

Produce debugging information in XCOFF format (if that is supported), using GNU extensions understood only by the GNU debugger (GDB).

-gdwarf-version

Produce debugging information in DWARF format (if that is supported). The value of version may be either 2 or 3; the default version is 3.

-gvms

Produce debugging information in VMS debug format (if that is supported). This is the format used by DEBUG on VMS systems.

-glevel

-ggdblevel

-gstabslevel

-gcofflevel

-gxcofflevel

-gvmslevel

Request debugging information and also use level to specify how much information. The default level is 2. Level 0 produces no debug information at all. Thus, -g0 negates -g. Level 1 produces minimal information, enough for making backtraces in parts of the program that you don’t plan to debug. This includes descriptions of functions and external variables, but no information about local variables and no line numbers. Level 3 includes extra information, such as all the macro definitions present in the program. Some debuggers support macro expansion when you use -g3.

(8)常用工具——objdump,nm和size

       objdump可用于查看目标文件或者可执行文件的逆向工程信息,比如,我们可以使用“--syms”选项查看符号表,或者“-d”选项反汇编代码。

       nm命令也用于查看可执行文件或目标文件的符号信息。size命令可用于查看某文件的大小,包括text、bss和data等各段的大小。

 

2.GDB

       GDB——the GNU Debugger,是Linux下强大的源代码级调试器,可以在源代码级或机器级(汇编语言)调试程序,是软件开发必不可少的Debug工具,下面介绍其基本使用。

       这里给出一个简单的示例程序作为实验对象:

#include <stdio.h>

 

int main(void){

        int i;

        for(i = 0; i < 10; i++){

                printf("i = %d\n",i);

        }

        return 0;

}

(1)调试前的准备工作

       首先,以调试模式编译生成可执行文件:

       gcc -o test test.c -Wall -O0 -g

(2)启动GDB

       执行命令“gdb test”即将test程序置于GDB的控制环境中执行,当看到“<gdb>”的提示符就说明已经成功运行在GDB调试器的控制下。使用“run”命令即可启动程序执行,“run”命令后还可以带命令行参数。

(3)查看源码

       使用“list”命令列出源代码,常用的有:

       list LINENUM                            列出LINENUM附近的代码

       list FILE:LINENUM                 列出FILE文件中LINENUM附近的代码

       list FUNCTION                          列出FUNCTION函数

       list FILE:FUNCTION               列出FILE文件中的FUNCTION函数

       list *ADDRESS                                 列出地址ADDRESS附近的代码

       说明:若直接按回车键,则重复执行最近一次执行的命令。

(4)设置断点

       使用“break(b)”命令来设置断点,可以按照文件名、函数名和行号等设置断点,并可设置“条件断点”——当某条件满足时才触发该断点,命令“info breakpoints”可以列出所有的断点并编号,使用“delete”命令加上编号可删除断点,常见用法:

break foo                                    在foo函数处设置断点

break foobar.c:foo                       在foobar.c的foo函数处设置断点

break 10                                     在当前模块的第10行设置断点

break foobar.c:10                        在foobar.c的第10行设置断点

break 20 if setlen>buflen              当setlen大于buflen时触发当前模块的第20行断点

break 0x345a345d                       在该物理地址处设置断点

(5)设置观察点

       除了基本的断点设置功能外,GDB还提供观察点设置命令——watch,rwatch,awatch。当目标变量被写入并改变值时触发watch观察点;当目标变量被读取时触发rwatch观察点;当目标变量被访问(读或写)时触发awatch观察点。注意,如果需要,观察点也可以设置成条件的。

(6)单步调试

       与单步调试相关的有以下三个命令:

l  step

l  next

l  cont

当程序因为触发断点或者观察点而中止时,可以使用step或者next命令单步执行下一行代码,不同的是,若下一行是对某函数的调用,那么step会“跳入”(step into)这个函数中单步执行;而next不会跳进去执行。使用“cont”命令可以恢复程序的运行。

(7)查看数据

1)print命令

       print命令是最基本的查看命令,可以查看单个变量、结构体变量或数组的内容,并可以使用不同的进制来显示目标变量,常用命令如下所示:

查看单个变量的内容:

      print  i

以十六进制/十进制/无符号十进制/八进制/二进制/地址/字符/浮点数形式查看变量的值:

      print/x/d/u/o/t/a/c/f  var

查看结构体变量:

print       strucVar/strucVar.member

查看数组成员:

print  array[5]

查看从array[3]开始的6个数组成员值

print  array[3]@6

2)高级使用方法——display命令

       当我们调试循环结构的时候,每当程序中止我们都要查看变量的值,若每次都使用print命令有点麻烦,这时我们可以使用display命令,该命令的用法和print完全一样,唯一不同的地方是你每次键入的display命令都被GDB记录在一张表格中,通过“info display”命令可以查看,下次触发断点时将自动执行此display命令。当我们不需要时,可通过“delete display num”命令删除num对应的display项。

       还有更高级的用法,使用commands命令可以指定每次程序中止时要执行的命令,这样可以更进一步地实现复杂的断点调试模式。

(8)改变数据

       GDB最让程序员感到愉快的是它提供了运行时变量修改功能,当我们在调试有bug的程序时,我们可以动态修改相关变量的值,然后让程序继续运行来验证我们的猜想,如果修改之后程序执行正确了,那么如何修复程序的缺陷也就明确了,这个过程被称作给程序“打补丁”,使用以下命令来修改var的值:

       set var=value

(9)检查堆栈

       backtrace(bt)命令可以追踪函数调用栈,也就是查看经过了什么样的函数调用路径走到当前这个位置,该命令可用于快速定位段错误(Segmentation Fault)。

       若程序对指针使用不当(如使用了未初始化的指针),就会产生段错误,解决方法是:将有问题的程序放在gdb下,使用“run”命令执行,当出现段错误时GDB会提示并中止,此时键入bt命令即可查看函数调用栈并定位问题出现的地方。

(10)调试多进程程序

       在调试多进程的程序时,一个关键的问题是当创建新进程时该跟踪哪个进程,我们可以使用“follow-fork-mode”命令来告诉GDB该跟踪哪个进程,若要跟踪子进程,那么使用:

       set follow-fork-mode child

反之“set follow-fork-mod parent”设定跟踪父进程,也可以使用“set follow-fork-mod ask”告诉GDB当fork调用发生时询问我们该跟踪哪个进程。注意,在跟踪某个进程时,其他进程是不受GDB影响的。

(11)调试多线程程序

       调试多线程程序是比较有挑战性的,不过GDB对多线程程序的调试还是提供了支持。多线程程序中的断点和单线程程序不太相同,举个例子,如果设置的断点位置会被多个线程访问到的话,那么每个线程都会受到该断点的影响,这时我们可以通过指定线程号来限制这种影响,如:

       <gdb> break pos.c:17 thread 5 if ret>0

       设置当ret大于0时,该断点被触发并只影响5号线程。命令“info threads”可用于显示当前活跃的进程及它们的状态,带“*”号的线程是GDB当前控制的焦点。可使用“thread num”来切换到对应的线程。

在调试多线程程序时,有时候GDB的焦点会在多个线程之间跳来跳去,我们可以使用命令“set scheduler-locking on”设置GDB进行锁定,通过“show scheduler-locking”来查看锁定状态。

最后一个对多线程调试有用的命令是“thread apply”命令,它可用于将单个的命令应用给多个线程,如“thread apply all backtrace”将backtrace命令应用于所有线程,而“thread apply 1,4,9 backtrace”将backtrace命令应用于1,4和9号线程。

(12)调试正在运行的程序

       GDB还可以用于调试正在运行的程序,这有时是十分有用的,比如,当某个程序在运行一段时间后突然被挂起停止响应了,这时候我们就可以使用GDB来调试它。换句话说,对于某些运行一段时间后会出问题的程序,可以使用GDB进行调试。

       调试的方法是启动GDB,并使用“attach PID”命令并指定进程PID,调试完成后使用“detach”命令解除控制。

(13)调试崩溃了的程序

       有时候程序因为致命错误崩溃后会产生核心转储文件(core-dump),这时候我们通过向GDB提供该可执行文件和核心转储文件可以根据核心转储文件中的信息调试程序崩溃时的代码异常,比如test程序崩溃并产生core.123文件,那么可以使用“gdb test core.123”启动gdb调试该程序。

最后,给出一个常用GDB命令的快速参考

(14)GDB常用命令快速参考

Summary of GDB commands for IA32 Systems

Starting:

gdb

gdb <file>

Running and stopping

quit :Exit gdb

run :Run program

run 1 2 3: Run program with command-line arguments 1 2 3

kill :Stop the program

quit :Exit gdb

Ctrl-d :Exit gdb     

Note: Ctrl-C does not exit from gdb, but halts the current gdb command

Breakpoints

break sum :Set breakpoint at the entry to function sum

break *0x40046b :Set breakpoint at address 0x40046b

disable 1 :Disable the breakpoint 1 (gdb numbers each breakpoint you create)

enable 1 :Enable breakpoint 1

delete 1 :Delete breakpoint 1

delete :Delete all breakpoints

clear sum :Clear any breakpoints at the entry to function sum

Execution

stepi :Execute one instruction

stepi 4 :Execute four instructions

nexti :Like stepi, but proceed through function calls without stopping

step :Execute one C statement

continue :Resume execution until the next breakpoint

until 3 :Continue executing until program hits breakpoint 3

finish :Resume execution until current function returns

call sum(1, 2): Call sum(1,2) and print return value

Examining code

disas: Disassemble current function

disas sum :Disassemble function sum

disas 0x8048335 :Disassemble function around 0x8048335

disas 0x8048335 0x8048343 :Disassemble code within specified address range

print /x $eip :Print program counter in hex

print /d $eip :Print program counter in decimal

print /t $eip :Print program counter in binary

Examining data

print /d $eax: Print contents of %eax in decimal

print /x $eax :Print contents of %eax in hex

print /t $eax :Print contents of %eax in binary

print 0x100 :Print decimal representation of 0x100

print /x 555 :Print hex representation of 555

print /x ($esp+8): Print (contents of %esp) + 8 in hex

print *(int *) 0xffffcca8: Print integer at address 0xffffcca8

print *(int *) ($esp+8) :Print integer at address %esp + 8

print (char *) 0xbfff890 :Examine a string stored at 0xffffcca8

x/w 0xffffcca8 :Examine (4-byte) word starting at address 0xffffcca8

x/w $esp: Examine (4-byte) word starting at address in $esp

x/wd $esp: Examine (4-byte) word starting at address in $esp.Print in decimal

x/2w $esp: Examine two (4-byte) words starting at address in $esp

x/2wd $esp :Examine two (4-byte) words starting at address in $esp. Print in decimal

x/g $esp :Examine (8-byte) word starting at address in $esp.

x/gd $esp :Examine (8-byte) word starting at address in $esp. Print in decimal

x/a $esp :Examine address in $esp. Print as offset from previous global symbol.

x/s 0xffffcca8 :Examine a string stored at 0xffffcca8

x/20b sum: Examine first 20 opcode bytes of function sum

x/10i sum :Examine first 10 instructions of function sum

(Note: the format string for the ‘x’ command has the general form

x/[NUM][SIZE][FORMAT] where

NUM = number of objects to display

SIZE = size of each object (b=byte, h=half-word, w=word, g=giant (quad-word))

FORMAT = how to display each object (d=decimal, x=hex, o=octal, etc.)

If you don’t specify SIZE or FORMAT, either a default value, or the last

value you specified in a previous ‘print’ or ‘x’ command is used.)

Useful information

backtrace: Print the current address and stack backtrace

where :Print the current address and stack backtrace

info program :Print current status of the program

info functions :Print functions in program

info stack :Print backtrace of the stack

info frame :Print information about the current stack frame

info registers :Print registers and their contents

info breakpoints: Print status of user-settable breakpoints

display /FMT EXPR: Print expression EXPR using format FMT every time GDB stops

undisplay :Turn off display mode

help: Get information about gdb

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值