Linux GDB Debugging

Catalog

1. GDB Introduction
2. GDB基本命令

 

1. GDB Introduction

GDB是GNU开源组织发布的一个强大的UNIX下的程序调试工具,GDB主要可帮助工程师完成下面4个方面的功能

1. 启动程序,可以按照工程师自定义的要求随心所欲的运行程序
2. 让被调试的程序在工程师指定的断点处停住,断点可以是条件表达式
3. 当程序被停住时,可以检查此时程序中所发生的事,并追索上文
4. 动态地改变程序的执行环境 

在命令行上键入gdb并按回车键就可以运行gdb了, 如果一切正常的话, gdb 将被启动并且你将在屏幕上看到类似的内容

GNU gdb (GDB) Red Hat Enterprise Linux (7.2-75.el6)
Copyright (C) 2010 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-redhat-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
(gdb) 
//当启动gdb后,我们能在命令行上指定很多的选项: gdb -h
aliases -- Aliases of other commands
breakpoints -- Making program stop at certain points
data -- Examining data
files -- Specifying and examining files
internals -- Maintenance commands
obscure -- Obscure features
running -- Running the program
stack -- Examining the stack
status -- Status inquiries
support -- Support facilities
tracepoints -- Tracing of program execution without stopping the program
user-defined -- User-defined commands

0x1: GDB启动

//当用这种方式运行gdb,我们能直接指定想要调试的程序. 这将告诉gdb装入名为fname的可执行文件
1. gdb <program>

//我们也可以用gdb去检查一个因程序异常终止而产生的core dump文件
2. gdb <corename>

//与一个正在运行的程序相连,gdb会自动attach上去,并调试它,program应该在PATH环境变量中搜索得到
3. gdb programname pid

0x2: GDB启动参数

gdb [-help] [-nx] [-q] [-batch] [-cd=dir] [-f] [-b bps] [-tty=dev] [-s symfile] [-e prog] [-se prog] [-c core] [-x cmds] [-d dir] [prog[core|procID]]

1. -help (-h):                列出所有选项,并附简要说明 
2. -symbols=file (-s file):        读出文件(file)中的符号表 
3. -write:                开通(enable)往可执行文件和核心文件写的权限 
4. -exec=file (-e file):        在适当时候把File作为可执行的文件执行,来检测与core dump结合的数据 
5. -se File:                从File读取符号表并把它作为可执行文件 
6. -core File (-c File):        把File作为core dump来执行 
7. -directory=Directory (-d Directory):    把Dicrctory加入源文件搜索的路径中 
8. -cd=Directory:            运行GDB,使用Directory作为它的工作目录,取代当前工作目录

0x3: 为调试编译代码

为了使gdb正常工作,你必须使你的程序在编译时包含调试信息(DEBUG信息)。调试信息包含你程序里的每个变量的类型和在可执行文件里的地址映射以及源代码的行号。gdb利用这些信息使源代码和机器码相关联

//在编译时用"-g"选项打开调试选项
gcc –g gdb_example.c –o gdb_example
g++ –g gdb_example.c –o gdb_example

 

2. GDB基本命令

0x1: 测试环境准备

#include <stdio.h> 
#include <string.h>
#include <malloc.h>

void my_print (char *string) 
{ 
    printf ("The string is %s\n", string); 
}
  
void my_print2 (char *string)
{
    char *string2; 
    int size, i; 

    size = strlen (string); 
    string2 = (char *) malloc (size + 1);

    for (i = 0; i < size; i++)
    {
        string2[size - i] = string[i];
    } 
    string2[size+1] = '\0';

    printf ("The string printed backward is %s\n", string2); 
}

main ()
{ 
    char my_string[] = "hello there"; 

    my_print (my_string); 
    my_print2 (my_string); 
}
//gcc -g test.c -o test 

结果不符合预期,我们通过GDB调试这个程序来学习GDB的调试命令

0x2: list命令

在gdb中运行"list/l"命令可以列出代码

1. 显示程序第linenum行周围的源程序
list <linenum>
l <linenum>

2. 显示函数名为function的函数的源程序
list <function> 
l <function> 

3. 显示当前行后面的源程序
list

4. 显示当前行前面的源程序
list - 

0x3: run命令

在gdb中,运行程序使用run命令,run指令会重头运行一次程序。在程序运行前,我们可以设置如下4方面的工作环境

1. 程序运行参数  
    1) 指定运行时参数: set args 10 20 30 40 50
    2) 查看设置好的运行参数: show args 
2. 运行环境
    1) 设定程序的运行路径: path <dir>
    2) 查看程序的运行路径: show paths
    3) 设置环境变量: set environment varname [=value],如set env USER=baohua;
    4) 查看环境变量: show environment [varname] 
3. 工作目录
    1) cd <dir>: 相当于shell的cd命令
    2) pwd: 显示当前所在的目录。
4. 程序的输入输出
    1) info terminal: 用于显示程序用到的终端的模式
    2) gdb中也可以使用重定向控制程序输出,如: run > outfile 
    3) tty命令可以指定输入输出的终端设备,如:tty /dev/ttyS1 

在gdb提示符下按回车健将重复上一个命令

0x4: break命令

在gdb中用break命令来设置断点,设置断点的方法包括

1. 在进入指定函数时停住,C++中可以使用class::function或function(type, type)格式来指定函数名  
break <function>

2. 在指定行号停住 
break <linenum>

3. 在当前行号的前面或后面的offset行停住,offiset为自然数
break +offset
break -offset

4. 在源文件filename的linenum行处停住
break filename:linenum
 
5. 在源文件filename的function函数的入口处停住
break filename:function
 
6. 在程序运行的内存地址处停住
break *address

7. break命令没有参数时,表示在下一条指令处停住
break
 
8. 条件IF断点
break ... if <condition>
    1) "..."可以是上述的break <linenum>、break +offset / break –offset中的参数
    2) condition表示条件
在条件成立时停住。比如在循环体中,可以设置break if i=100,表示当i为100时停住程序 

9. xbreak
 在当前函数的退出的点上设置一个断点

10. txbreak
在当前函数的退出的点上设置一个临时的断点(只可使用一次)

11. 查看断点时
info
info breakpoints [n]、info break [n](n表示断点号)

0x5: 单步命令

在调试过程中

1. next/n命令: 用于单步执行,类似VC++中的step over,next单步不会进入函数的内部
2. step/s命令: 在单步执行一个函数时,会进入其内部,类似VC++中的step into

单步执行的更复杂用法包括

1. 单步跟踪
step <count>
如果有函数调用,则"进入"该函数(进入函数的前提是,此函数被编译有debug信息)。step后面不加count表示一条条地执行,加表示执行后面的count条指令,然后再停住 

2. 单步跟踪next <count>
如果有函数调用,它"不会"进入该函数。同样地,next后面不加count表示一条条地执行,加表示执行后面的count条指令,然后再停住 

3. set step-mode
    1) set step-mode on: 打开step-mode模式,这样,在进行单步跟踪时,程序不会因为没有debug信息而不停住,这个参数的设置可便于查看机器码
    2) set step-mod off: 关闭step-mode模式。

4. finish
运行程序,直到当前函数完成返回,并打印函数返回时的堆栈地址和返回值及参数值等信息 

5. until/u
一直在循环体内执行单步,退不出来是一件令人烦恼的事情,until命令可以运行程序直到退出循环体 

6. stepi/si、nexti/ni
stepi和nexti用于单步跟踪一条机器指令,一条程序代码有可能由数条机器指令完成,stepi和nexti可以单步执行机器指令

7. display/i $pc
单步跟踪会在打出程序代码的同时打出机器指令,即汇编代码

0x6: continue命令

当程序被停住后,可以使用continue/c命令,(fg命令同continue命令)恢复程序的运行直到程序结束,或到达下一个断点,命令格式为

continue [ignore-count]
c [ignore-count]
fg [ignore-count]
//ignore-count表示忽略其后多少次断点

0x7: print命令

在调试程序时,当程序被停住时,可以使用print/p命令,或是同义命令inspect来查看当前程序的运行数据。print命令的格式是

print <expr>  
//<expr>是表达式,是被调试的程序中的表达式 

print /<f> <expr>  
/*
<f>是输出的格式
1. x: 按十六进制格式显示变量 
2. d: 按十进制格式显示变量 
3. u: 按十六进制格式显示无符号整型 
4. o: 按八进制格式显示变量 
5. t: 按二进制格式显示变量 
6. a: 按十六进制格式显示变量 
7. c: 按字符格式显示变量 
8. f: 按浮点数格式显示变量 
*/

expr: 表达式
在表达式中,有几种GDB所支持的操作符,它们可以用在任何一种语言中
1. "@"是一个和数组有关的操作符
2. "::"指定一个在文件或是函数中的变量
3. "{<type>} <addr>"表示一个指向内存地址<addr>的类型为type的一个对象

0x8: watch命令

watch一般来观察某个表达式(变量也是一种表达式)的值是否有变化了,如果有变化,马上停住程序。我们有下面的几种方法来设置观察点
1. watch <expr>
为表达式(变量)expr设置一个观察点。一量表达式值有变化时,马上停住程序

2. rwatch <expr>
当表达式(变量)expr被读时,停住程序

3. awatch <expr>
当表达式(变量)的值被读或被写时,停住程序

4. info watchpoints
列出当前所设置了的所有观察点 

0x9: examine命令

我们可以使用examine/x命令,来查看内存地址中的值。examine命令的语法如下所示

x/<n/f/u> <addr> 
1. <addr>: 表示一个内存地址
2. <n/f/u>: 可选的参数
    1) n: 一个正整数,表示显示内存的长度,也就是说从当前地址向后显示几个地址的内容
    2) f: 显示的格式,如果地址所指的是字符串,那么格式可以是s,如果地址是指令地址,那么格式可以是i
    3) u: 表示从当前地址往后请求的字节数,如果不指定的话,GDB默认是4字节。u参数可以被一些字符代替
    4) b: 表示单字节
    5) h: 表示双字节
    6) w: 表示四字节
    7) g: 表示八字节
当我们指定了字节长度后,GDB会从指定的内存地址开始,读写指定字节,并把其当作一个值取出来。n、f、u这3个参数可以一起使用,例如命令
x/3uh 0x54320: 表示从内存地址0x54320开始以双字节为1个单位(h)、16进制方式(u)显示3个单位(3)的内存 

0x10: set命令

修改寄存器  
1. set $v0 = 0x004000000  
2. set $epc = 0xbfc00000   

修改内存 
1. set {unsigned int}0x8048a51=0x0 

0x11: jump命令

一般来说,被调试程序会按照程序代码的运行顺序依次执行,但是GDB也提供了乱序执行的功能,也就是说,GDB可以修改程序的执行顺序,从而让程序随意跳跃

1. jump <linespec>: 指定下一条语句的运行点
    1) <linespec>可以是文件的行号
    2) 可以是file:line格式
    3) 也可以是+num这种偏移量格式,表示下一条运行语句从哪里开始

2. jump <address>
这里的<address>是代码行的内存地址
//注意,jump命令不会改变当前的程序栈中的内容(它是纯粹的EIP修改跳转),所以,如果使用jump从一个函数跳转到另一个函数,当跳转到的函数运行完返回,进行出栈操作时必然会发生错误,这可能导致意想不到的结果,所以最好只用jump在同一个函数中进行跳转 

0x12: signal命令

使用singal命令,可以产生一个信号量给被调试的程序,如中断信号"Ctrl+C"。这非常方便于程序的调试,可以在程序运行的任意位置设置断点,并在该断点用GDB产生一个信号量,这种精确地在某处产生信号的方法非常有利于程序的调试

signal命令的语法是:signal <signal>
//UNIX的系统信号量通常从1到15,所以<signal>取值也在这个范围 

0x13: return命令

如果在函数中设置了调试断点,在断点后还有语句没有执行完,这时候我们可以使用return命令强制函数忽略还没有执行的语句并返回

return
return <expression>
//return命令用于取消当前函数的执行,并立即返回,如果指定了<expression>,那么该表达式的值会被作为函数的返回值

0x14: call命令

call命令用于强制调用某函数

call <expr> 
表达式中可以一是函数,以此达到强制调用函数的目的,它会显示函数的返回值(如果函数返回值不是void) 

0x15: info命令

info命令可以在调试时用来查看相关信息

1. 寄存器
    1) info registers: 查看除了浮点寄存器以外的寄存器
    2) info all-registers: 查看所有寄存器,包括浮点寄存器
    3) info registers <regname ...>: 查看所指定的寄存器

2. 断点
    1) info break: 查看断点信息

3. 观察点
    1) info watchpoints: 列出当前所设置的所有观察点

4. 信号
    1) info signals: 查看有哪些信号正在被GDB检测
    2) info handle: 查看有哪些信号正在被GDB检测
 
5. info line <xx>: 查看源代码在内存中的地址
info line后面可以跟行号、函数名、文件名:行号、文件名:函数名等多种形式,例如下面的命令会打印出所指定的源码在运行时的内存地址:
//info line tst.c:func  
 
6. info threads: 查看多线程

0x16: set scheduler-locking off|on|step

1. off: 不锁定任何线程,也就是所有线程都执行,这是默认值。 
2. on: 只有当前被调试程序会执行
3. step: 在单步的时候,除了next过一个函数的情况以外,只有当前线程会执行

与多线程调试相关的命令还包括

1. thread ID
切换当前调试的线程为指定ID的线程  
 
2. break thread_test.c:123 thread all
在所有线程中相应的行上设置断点
 
3. thread apply ID1 ID2 command 
让一个或者多个线程执行GDB命令command 
 
4. thread apply all command 
让所有被调试线程执行GDB命令command

0x17:disassemble

disassemble命令用于反汇编,它可被用来查看当前执行时的源代码的机器码,其实际上只是把目前内存中的指令dump出来

1. 反汇编一个函数
disass func_name

2. 反汇编一段内存地址, 第1个参数是起始地址,第2个是终止地址
disassemble 0x0 0x10
//可以使用 info line 命令来映射一个源码行到程序地址,然后使用命令disassemble显示一个地址范围的机器指令

3. disassemble 不带参数
默认的反汇编范围是 所选择帧的pc附近的函数

0x18: kill [filename]

终止正在调试的程序

0x19: clear

删除一个断点,这个命令需要指定代码行或者函数名作为参数 

0x20: bt/Backtrace

显示程序堆栈信息,即显示函数调用栈的信息

0x21: enable、enable

1. disable: 禁止断点功能,这个命令需要禁止的断点在断点列表索引值作为参数
2. enable: 允许断点功能,这个命令需要允许的断点在断点列表索引值作为参数

0x22: ignore

忽略某个断点制定的次数

ignore 4 23: 忽略断点4的23次运行,在第24次的时候中断

0x23: load

动态载入一个可执行文件到调试器

0x24: whatis

显示变量的值和类型

0x25: ptype

显示变量的类型

0x26: make

使你能不退出GDB就可以重新产生可执行文件

0x27: shell

使你能不离开GDB就执行 UNIX shell 命令

Relevant Link:

http://www.chinalinuxpub.com/doc/pro/gdb.html 
http://www.vimer.cn/2009/11/%E4%BD%BF%E7%94%A8gdb%E8%B0%83%E8%AF%95%E7%A8%8B%E5%BA%8F%E8%AF%A6%E8%A7%A3.html
https://www.ibm.com/developerworks/cn/linux/sdk/gdb/
http://blog.csdn.net/21cnbao/article/details/7385161
http://www.vimer.cn/2009/11/%E4%BD%BF%E7%94%A8gdb%E8%B0%83%E8%AF%95%E7%A8%8B%E5%BA%8F%E8%AF%A6%E8%A7%A3.html

 

Copyright (c) 2015 LittleHann All rights reserved

 

posted @ 2015-04-09 12:11  郑瀚Andrew  阅读(989)  评论(0编辑  收藏  举报