Gdb 的简单使用
gdb+gdbserver 方式进行ARM 程序调试
1: gdb 的简单使用
GDB 是GNU 开源组织发布的一个强大的UNIX 下的程序调试工具。或许, 各位比较喜欢那
种图形界面方式的, 像VC、BCB 等IDE 的调试, 但如果你是在UNIX 平台下做软件, 你
会发现GDB 这个调试工具有比VC、BCB 的图形化调试器更强大的功能。所谓“ 寸有所
长, 尺有所短” 就是这个道理。一般来说, GDB 主要帮忙你完成下面四个方面的功
能:
1、启动你的程序, 可以按照你的自定义的要求随心所欲的运行程序。
2、可让被调试的程序在你所指定的调置的断点处停住。(断点可以是条件表达式)
3、当程序被停住时, 可以检查此时你的程序中所发生的事。
4、动态的改变你程序的执行环境。从上面看来, GDB 和一般的调试工具没有什么两
样, 基本上也是完成这些功能, 不过在细节上, 你会发现GDB 这个调试工具的强大, 大
家可能比较习惯了图形化的调试工具, 但有时候, 命令行的调试工具却有着图形化工具所
不能完成的功能。让我们一一看来。
一个调试示例
—————— 源程序: tst.c
#include
int func(int n)
{
int sum=0,i;
for(i=0; i
编译生成执行文件: (Linux 下)
hchen/test> cc -g tst.c -o tst
使用GDB 调试:
hchen/test> gdb tst <--启动GDB
GNU gdb 5.1.1
Copyright 2002 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License,
and you are welcome to change it and/or distribute copies of it
under certain conditions. Type "show copying" to see the conditions.
There is absolutely no warranty for GDB. Type "show warranty" for details.
www.linuxidc.com
www.linuxidc.com
This GDB was configured as "i386-suse-linux"...
(gdb) l <-- l 命令相当于list, 从第一行开始例出原码。
1 #include
2
3 int func(int n)
4 {
5 int sum=0,i;
6 for(i=0; i
8 sum+=i;
9 }
10 return sum;
(gdb) <--直接回车表示, 重复上一次命令
11 }
12
13
14 main()
15 {
16 int i;
17 long result = 0;
18 for(i=1; i<=100; i++)
19 {
20 result += i;
(gdb) break 16 <-- 设置断点, 在源程序第16 行处。
Breakpoint 1 at 0x8048496: file tst.c, line 16.
(gdb) break func <-- 设置断点, 在函数func()入口处。
Breakpoint 2 at 0x8048456: file tst.c, line 5.
(gdb) info break <-- 查看断点信息。
Num Type Disp Enb Address What
1 breakpoint keep y 0x08048496 in main at
2 breakpoint keep y t0sx0t.8c0:4186456 in func at
(gdb) r <-- 运行程t序st.,c:5run 命令简写
Starting program: /home/hchen/test/tst Breakpoint 1, main () at tst.c:17 <-- 在断点处
停住。
17 long result = 0;
(gdb) n <-- 单条语句执行, next 命令简写。
18 for(i=1; i<=100; i++)
(gdb) n
20 result += i;
(gdb) n
18 for(i=1; i<=100; i++)
(gdb) n
20 result += i;
(gdb) c <-- 继续运行程序, continue 命令简写。
Continuing.
result[1-100] = 5050 <--程序输出。
Breakpoint 2, func (n=250) at tst.c:5
5 int sum=0,i;
(gdb) n
6 for(i=1; i<=n; i++)
(gdb) p i <-- 打印变量i 的值, print 命令简写。
$1 = 134513808
(gdb) n
8 sum+=i;
(gdb) n
6 for(i=1; i<=n; i++)
(gdb) p sum
$2 = 1
www.linuxidc.com
www.linuxidc.com
(gdb) n
8 sum+=i;
(gdb) p i
$3 = 2
(gdb) n
6 for(i=1; i<=n; i++)
(gdb) p sum
$4 = 3
(gdb) bt <-- 查看函数堆栈。
#0 func (n=250) at tst.c:5
#1 0x080484e4 in main () at tst.c:24
#2 0x400409ed in __libc_start_main () from /lib/libc.so.6
(gdb) finish <-- 退出函数。
Run till exit from #0 func (n=250) at tst.c:5
0x080484e4 in main () at tst.c:24
24 printf("result[1-250] = %d \n",
func(250) ); Value returned is $6 = 31375
(gdb) c <-- 继续运行。
Continuing.
result[1-250] = 31375 <--程序输出。
Program exited with code 027. <-------程序退出, 调试结束。
(gdb) q <-- 退出gdb。
hchen/test>
2: gdb+gdbserver 方式进行ARM 程序调试
【摘要】:本文首先介绍了gdb+gdbserver 相关的概念,然后介绍了其下载、编译、安装等过
程; 接着介绍了利用gdb+gdbserver 调试应用程序的流程及实例等; 最后分析了下
gdb+gdbserver 安装过程中的常见问题。
【关键词】:gdb,gdbserver,远程调试
目录
一、gdb+gdbserver 总体介绍... 1
二、源代码下载... 1 三、配置编译及
安装下载... 1 四、gdb+gdbserver
nfs 调试流程... 2 五、如何利用串口
调试... 3 六、实战调试... 3
七、linux 下安装gdbserver 问题... 5
一、gdb+gdbserver 总体介绍
远程调试环境由宿主机GDB 和目标机调试stub 共同构成,两者通过串口或TCP 连接。使用GDB 标
准程串行协议协同工作,实现对目标机上的系统内核和上层应用的监控和调试功能。调试stub 是嵌
入式系统中的一段代码,作为宿主机GDB 和目标机调试程序间的一个媒介而存在。就目前而言,嵌
入式Linux 系统中,主要有三种远程调试方法,分别适用于不同场合的调试工作:
用ROM Monitor 调试目标机程序、用KGDB 调试系统内核和用gdbserver 调试用户空间程序。
这三种调试方法的区别主要在于,目标机远程调试stub 的存在形式的不同,而其设计思路和
实现方法则是大致相同的。
而我们最常用的是调试应用程序。就是采用gdb+gdbserver 的方式进行调试。在很多情况下,用
户需要对一个应用程序进行反复调试,特别是复杂的程序。采用GDB 方法调试,由于嵌入式系统
资源有限性,一般不能直接在目标系统上进行调试,通常采用gdb+gdbserver 的方式进行调试。
二、源代码下载
嵌入式Linux 的GDB 调试环境由Host 和Target 两部分组成,Host 端使用arm-linuxgdb,
Target Board 端使用gdbserver。这样,应用程序在嵌入式目标系统上运行,而gdb 调试在
Host 端,所以要采用远程调试(remote)的方法。进行GDB 调试,目标系统必须包括gdbserver 程序
www.linuxidc.com
www.linuxidc.com
(在主机上正对硬件平台编译成功后下载到目标机上),宿主机也必须安装GDB 程序。一般Linux 发
行版中都有一个可以运行的GDB,但开发人员不能直接使用该发行版中的GDB 来做远程调试,而要获
取GDB 的源代码包,针对arm 平台作一个简单配置,重新编译得到相应GDB。GDB 的源代码包可
以从
http://www.gnu.org/software/gdb/download/
http://ftp.gnu.org/gnu/gdb/ 211.95.105.202:3128 可以上去的,所有的版本都有啊
http: //ftp.cs.pu.edu.tw/linux/sourceware/gdb/releases/下载
ftp://ftp.gnu.org/gnu/gdb
外网的ftp 我经常上不去,国内常见的开源社区的下载频道通常都有下载的
http://download.chinaunix.net/download/0004000/3482.shtml,最新版本为gdb-6.5.tar.bz2
。下载到某个目录,笔者下载到/opt/。但要注意,gdb 的版本需要和croostool 相匹配。
三、配置编译及安装下载
下载完后, 进入/opt/目录, 配置编译步骤如下:
#tar jxvf gdb-6.5-tar-bz2
#cd gdb-6.5
#./configure --target=arm-linux --prefix=/usr/local/arm-gdb – v
(--target 配置gdb 的目标平台, --prefix 配置安装路径, 当然其他路径也可以, .跟下面配置一致
即可,
须在环境变量中声明, 启动arm-linux-gdb 需要, 可更改/etc/profile 或~/.bash_profile 或~/.bashrc, 添
加
export PATH=$PATH:/usr/local/arm-gdb/bin, 这样可以找到路径)
#make
#make install
(生成arm-linux-gdb,并存入/usr/local/arm-gdb /bin/, 查询确认下)也可以启动arm-linux-gdb, 若成
功,
则证明安装无误并进入gdb/gdbserver 目录)
[root@dding gdbserver]# pwd
/opt/gdb-6.5/gdb/gdbserver
[root@dding gdbserver]##./configure --target=arm-linux – host=arm-linux
(必须在gdbserver 目录下运行配置命令, 此时才能用相对路径
--target=arm-linux 表示目标平台, --host 表示主机端运行的是arm-linux-gdb, 不需要配置— prefix,
因为
gdbserver 不在主机端安装运行)
#make CC=/usr/local/arm/2.95.3/bin/arm-linux-gcc
(这一步要指定你自己的arm-linux-gcc 的绝对位置, 我试过相对的不行, 提示make: arm-linux-gcc:
Command not found, 可好多人都用的相对路径, 即直接赋值arm-linux-gcc, 可采取make 时传递参
数, 也可以直接修改gdbserver 目录下的Makefile 文件中的环境变量CC)
没有错误的话就在gdbserver 目录下生成gdbserver 可执行文件,注意此时要更改其属性,否则可
能会出现无法访问的情况,chmod 777 gdbserver 将其更改为任何人都可以读写执行;使用armlinux-
strip 命令处理一下gdbserver,将多余的符号信息删除,可让elf 文件更精简,通常在
应用程序的最后发布时使用;然后把它烧写到flash 的根文件系统分区的/usr/bin(在此目录
下,系统可以自动找到应用程序,否则必须到gdbserver 所在目录下运行之),或通过nfs
mount 的方式都可以。只要保证gdbserver 能在开发板上运行就行。
四、gdb+gdbserver nfs 调试流程
下面就可以用gdb+gdbserver 调试我们开发板上的程序了。在目标板上运行gdbserver,其实就是
在宿主机的minicom 下。我是在minicom 下#mount 192.168.2.100:/ /tmp 后做的(这里参数-o
nolock 可以不加,不加这一步执行得反而更快些),hello 和gdbserver 都是位于Linux 根目录下,
把主机根目录挂在到开发板的/tmp 目录下。
要进行gdb 调试, 首先要在目标系统上启动gdbserver 服务。在gdbserver 所在目录下输
入命令: (minicom 下)
#cd /tmp
#./gdbserver 192.168.2.100:2345 hello
(192.168.2.100 为宿主机IP,在目标系统的2345 端口(你也可以设其他可用的值,当然必须跟主机的gdb 一致)开
启了一个调试进程,hello 为要调试的程序(必须-g 加入调试信息))。
出现提示:
Process /tmp/hello created: pid=80
www.linuxidc.com
www.linuxidc.com
Listening on port 2345
(另一个终端下)
#cd /
#export PATH=$PATH:/usr/local/arm-gdb/bin
#arm-linux-gdb hello
最后一行显示:This GDB was configured as “ --host=i686-pc-linux-gnu,--target=armlinux”
...,如果不一致说明arm-linux-gdb 有问题, 说明此gdb 在X86 的Host 上运行,但是调试目标是ARM
代码。
(gdb) target remote 192.168.2.223:2345
(192.168.2.223 为开发板IP)
Remote debugging using 192.168.2.223:2345
[New thread 80]
[Switching to thread 80]
0x40002a90 in ??()
同时在minicom 下提示:
Remote debugging from host 192.168.2.100
(gdb)
注意:你的端口号必须与gdbserver 开启的端口号一致,这样才能进行通信。建立链接后,就可以
进行调试了。调试在Host 端,跟gdb 调试方法相同。注意的是要用“ c” 来执行命令,不能用
“ r” 。因为程序已经在Target Board 上面由gdbserver 启动了。结果输出是在Target
Board 端, 用超级终端查看。连接成功,这时候就可以输入各种GDB 命令如list、run、next、
step、break 等进行程序调试了。
以上针对通过nfs mount 和tftp 的方式,只能在主机上调试好后下载到开发板上运行,如果有
错误要反复这个过程,繁琐不说,有些程序只能在开发板上调试。所以笔者采用了gdbserver 的
远程调试方式。希望对大家调试程序有用!
五、如何利用串口调试
如果你用串口1 调试hello 的话,你就要现在板子上运行命令:
gdbserver hello /dev/ttyS0
(详情可以参考gdbserver 目录下的readme 文件),这时gdbserver 就在等待gdb 的应答信号了。)
然后在pc 机上运行命令:
xxx-linux-gdb hello
在xxx-linux-gdb 里敲入入下命令:
(gdb) set remotedevice /dev/ttyS0
(这里设置串口1)
(gdb) set remote baud 9600
(这里设置串口波特率)
(gdb) set debug remote
(可选)
(gdb) target remote /dev/ttyS0
操作到这儿,gdb 就应该和gdbserver 联系上了。
六、实战调试
1.编辑文件
# vi gdbtest.c
1 #include
2
3 int
4 func(int n){
5 int sum=0, i;
6 for (i=0; i
8 }
9 return sum;
10 }
11
www.linuxidc.com
www.linuxidc.com
12 int
13 main(void)
14 {
15 int i;
16 long result = 0;
17 for (i=0; i<=100; i++){
18 result += i;
19 }
20
21 printf("result[1-100] = %d \n", result);
22 printf("resutl[1-225] = %d \n", func(255));
23
24 return 0;
25 }
# arm-linux-gcc -g gdbtest.c -o gdbtest // 交叉编译
2.下载文件到目标板: gdbtest 和gdbserver
假设host pc ip: 192.168.1.45
board ip: 192.168.1.180
A: 将文件拷贝到目标板上:
(先将gdbtest 和gdbserver 两个文件拷贝到主机的/tftpboot 目录下,此时系统主机和目标机都必须能够支持nfs)
在目标板的Linux 中运行:
#mount 192.168.1.108:/tftpboot /mnt/nfs
#cd /mnt/nfs
#ls
看是否有gdbtest 和gdbserver 两个文件。
3.运行调试
client board:
#./gdbserver 192.168.1.45:1234 gdbtest
(目标板上运行gdbtest 监听端口1234)
[root@AT91RM9200DK arm]$./gdbserver 192.168.0.12:2345 mainparacarm
./gdbserver: error in loading shared libraries: libthread_db.so.1: cannot open
[root@AT91RM9200DK arm]$
host pc:
#cd /usr/local/arm-gdb/bin/
(以便能够运行arm-linux-gdb,但是无此必要,可在环境变量中设置此路径即可。)
#copy gdbtest /usr/local/arm-gdb/bin/
(将前面编译的文件gdbtest 拷贝到此目录)
#./arm-linux-gdb gdbtest
(gdb)target remote 192.168.1.180:1234
(连接到开发板成功后就可以)
进行调试
(gdb)list or l
(gdb)break func
(gdb)break 22
(gdb)info br
(gdb)continue or c
(这里不能用run)
(gdb)next or n
(gdb)print or p result
(gdb) finish
(跳出func 函数)
(gdb) next
(gdb) quit
建立连接后进行gdb 远程调试和gdb 本地调试方法相同
www.linuxidc.com
www.linuxidc.com
七、linux 下安装gdbserver 问题
toolchain version: gdb 的版本可能和交叉编译器有很大的关系
gccglibcbinutils-
2.15 此为croostool
安装步骤: 下载
解压gdb-6.6
#cd gdb-6.6
#./configure --target=arm-linux --prefix=/usr/local/arm-gdb – v
#make & make install
OK,然后:
#export PATH=$PATH:/usr/local/arm-gdb
进入gdbserver 目录:
#./configure --target=arm-linux --host=arm-linux
#make CC=/usr/local/armv5l//bin/armv5l -linux-gcc
出错:
/usr/local/armv5l//bin/armv5l-linux-gcc -c -Wall -g -O2 -I. - -I./../regformats -
I./../../include
-I../../bfd -I./../../bfd linux-arm-low.c
linux-arm-low.c:35:21: sys/reg.h: 没有那个文件或目录
make: *** [linux-arm-low.o] 错误1
然后把/usr/include/sys/reg.h copy 到
/usr/local/armv5l-2.6.x//armv5l-linux/include/sys/reg.h,
即将该文件拷贝到交叉编译器的include 目录下,再make,显示错误:
/usr/local/armv5l//bin/armv5l-linux-gcc -c -Wall -g -O2 -I. - -I./../regformats -
I./../../include
-I../../bfd -I./../../bfd thread-db.c
thread-db.c: In function `thread_db_err_str':
thread-db.c:95: error: `TD_VERSION' undeclared (first use in this
function) thread-db.c:95: error: (Each undeclared identifier is reported
only once thread-db.c:95: error: for each function it appears in.)
thread-db.c: In function `thread_db_get_tls_address':
thread-db.c:336: warning: implicit declaration of function `td_thr_tls_get_addr'
thread-db.c:336: warning: cast to pointer from integer of
different size thread-db.c:340: warning: cast from pointer to
integer of different size make: *** [thread-db.o] 错误1
本想继续fix error,但是感觉不太对,请问各位,是什么原因呢?
是不是CC 的target 写错了?应该是arm-linux 还是armv5l-linux?
1.make: *** [linux-arm-low.o] Error 1
[root@dding gdbserver]#
[root@dding gdbserver]# gedit config.h
/* Define to 1 if you have the
/*define HAVE_SYS_REG_H 1 */
/*have no
2.
thread-db.c: In function `thread_db_err_str': gdb6.5
thread-db.c:95: `TD_VERSION' undeclared (first use in this function)
[root@dding gdbserver]# gedit config.h
94 #ifdef HAVE_TD_VERSION
95 case TD_VERSION:
96 return "version mismatch between libthread_db and libpthread";
97 #endif
/* Define if TD_VERSION is available. */
/*#define HAVE_TD_VERSION 1 */
/*have no TD_VERSION. so undefine 20070402 dding */
gdb6.1 没有此问题
3.
[root@AT91RM9200DK arm]$./gdbserver 192.168.0.12:2345 mainparacarm gdb6.5
./gdbserver: error in loading shared libraries: libthread_db.so.1: cannot open
www.linuxidc.com
www.linuxidc.com
[root@AT91RM9200DK arm]$./gdbserver 192.168.0.14:2345 mainparacarm gdb6.1
./gdbserver: error in loading shared libraries: libthread_db.so.1: cannot open shared object
file:
No such file or directory
我已经加了libthread_db.so.1 共享库为什么还打不开呢????共享库和cpu 类型有关吗?
gdbserver: error while loading shared libraries: libthread_db.so.1: cannot open
shared object file: No such file or director
****编译GDB 的时候搞成静态的就好了.我想编译选项里应该有. 要不你就在Makefile 里加上CFLAGS
+=
-static
LDFLAGS += -static
这两个的其中一个应该就可以了,不过还是两个都加上吧.
***/lib there is no libthread_db.so.1 Can i
use nfs to copy libthread_db.so.1 to
/lib? But now i cannot find this file, and is there any for cross ?
libpthread-0.8.so
libpthread.so libpthread.so.0libresolv-.so
libresolv.so.2 libstdc++. libtermcap.so.2
[root@AT91RM9200DK arm]$cp libthread_db-1.0.so libthread_db.so.1
[root@AT91RM9200DK arm]$cp libthread_db.so.1 /lib/
[root@AT91RM9200DK arm]$./gdbserver 192.168.0.12:2345 mainparacarm
./gdbserver: /lib/libc.so.6: version `GLIBC_2.2' not found (required by
/lib/li) 难道目前的gdb 6.5 版本太高,需要内核版本和交叉编译器与之匹配?实在不行,就试试
低版本的gdb 参考文档
www.linuxidc.com
www.linuxidc.com