嵌入式Linux系统的GDB调试

时间:2021-09-27 19:58:59
标题:嵌入式Linux的GDB远程调试的实现
2008-01-11 11:22:08
嵌入式Linux的GDB远程调试的实现

Author: Vicky 

远程调试环境由宿主机GDB和目标机调试stub共同构成,两者通过串口或TCP连接。使用GDB标准远程串行协议协同工作,实现对目标机上的系统内核和上层应用的监控和调试功能。调试stub是嵌入式系统中的一段代码,作为宿主机GDB和目标机调试程序间的一个媒介而存在。

就目前而言,嵌入式Linux系统中,主要有三种远程调试方法,分别适用于不同场合的调试工作:用ROM Monitor调试目标机程序、用KGDB调试系统内核和用gdbserver调试用户空间程序。这三种调试方法的区别主要在于,目标机远程调试stub的存在形式的不同,而其设计思路和实现方法则是大致相同的。
而我们最常用的是调试应用程序。就是采用gdb+gdbserver的方式进行调试。在很多情况下,用户需要对一个应用程序进行反复调试,特别是复杂的程序。采用GDB方法调试,由于嵌入式系统资源有限性,一般不能直接在目标系统上进行调试,通常采用gdb+gdbserver的方式进行调试。Gdbserver在目标系统中运行,gdb则在宿主机上运行。

要进行GDB调试,目标系统必须包括gdbserver程序,宿主机也必须安装gdb程序。一般linux发行版中都有一个可以运行的gdb,但开发人员不能直接使用该发行版中的gdb来做远程调试,而要获取gdb的源代码包,针对arm平台作一个简单配置,重新编译得到相应gdb。gdb的源代码包可以从

http://ftp.cs.pu.edu.tw/Linux/sourceware/gdb/releases/下载,最新版本为gdb-6.4。下载到某个目录,笔者下载到自己的用户目录:/home/vicky。
下载完后,进入/home/vicky目录,配置编译步骤如下:
#tar jxvf gdb-6.4-tar-bz2
#cd gdb-6.4
#./configure --target=arm-linux --prefix=/usr/local/arm-gdb -v
#make
(这一步的时候可能会有问题,提示一个函数中(具体函数名不记得了)parse error,就是unsigned前边多了一个”}”,你用vi进入那一行把它删掉就行了。)
#make install
#export PATH=$PATH:/usr/local/arm-gdb
进入gdbserver目录:
#./configure --target=arm-linux –host=arm-linux
#make CC=/usr/local/arm/2.95.3/bin/arm-linux-gcc
(这一步要指定arm-linux-gcc的位置,可能跟你的不一样)
没有错误的话就在gdbserver目录下生成gdbserver可执行文件,把它烧写到flash的根文件系统分区,或通过nfs mount的方式都可以。只要保证gdbserver能在开发板上运行就行。
下面就可以用gdb+gdbserver调试我们开发板上的程序了。在目标板上运行gdbserver,其实就是在宿主机的minicom下,我的red hat linux装在vmware下的。我是在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端口开启了一个调试进程,hello为要调试的程序。
出现提示:
Process /tmp/hello created: pid=80
Listening on port 2345

(另一个终端下)
#cd /
#export PATH=$PATH:/usr/local/arm-gdb/bin
#arm-linux-gdb hello
(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)
连接成功,这时候就可以输入各种gdb命令如list、run、next、step、break等进行程序调试了。

以上针对通过nfs mount和tftp的方式,只能在主机上调试好后下载到开发板上运行,如果有错误要反复这个过程,繁琐不说,有些程序只能在开发板上调试。所以笔者采用了gdbserver的远程调试方式。希望对大家调试程序有用!

摘自华恒技术支持论坛嵌入式技术园地

本文引用通告地址:http://lionwq.spaces.eepw.com.cn/articles/trackback/item/16810


CROSS-GDB 嵌入式linux的 交叉调试
2009-12-02 0:11
从http://ftp.gnu.org/gnu/gdb下载GDB源代码--gdb-6.2.1.tar.bz2
1-编译GDB客户端:
#cd gdb-6.2.1
#./configure --target=arm-linux --prefix=/root/arm-gdb -v
#make                          //期间有可能会有错误,见上面

Iwmmxt.c: 在函数 ‘WMAC’ 中:
iwmmxt.c:2117: 错误:赋值运算中的左值无效
iwmmxt.c:2133: 错误:赋值运算中的左值无效
iwmmxt.c: 在函数 ‘WMADD’ 中:
iwmmxt.c:2169: 错误:赋值运算中的左值无效
iwmmxt.c:2177: 错误:赋值运算中的左值无效
iwmmxt.c:2186: 错误:赋值运算中的左值无效
iwmmxt.c:2191: 错误:赋值运算中的左值无效
iwmmxt.c: 在函数 ‘WSLL’ 中:
iwmmxt.c:2840: 警告:对 ‘long’ 类型而言整数常量太大
iwmmxt.c: 在函数 ‘WSRA’ 中:
iwmmxt.c:2917: 警告:对 ‘long’ 类型而言整数常量太大
iwmmxt.c:2917: 警告:对 ‘long’ 类型而言整数常量太大
iwmmxt.c:2919: 警告:对 ‘long’ 类型而言整数常量太大
iwmmxt.c: 在函数 ‘WSRL’ 中:
iwmmxt.c:2988: 警告:对 ‘long’ 类型而言整数常量太大
iwmmxt.c: 在函数 ‘WUNPCKEH’ 中:
iwmmxt.c:3290: 警告:对 ‘long’ 类型而言整数常量太大
iwmmxt.c: 在函数 ‘WUNPCKEL’ 中:
iwmmxt.c:3357: 警告:对 ‘long’ 类型而言整数常量太大
iwmmxt.c: 在函数 ‘Fetch_Iwmmxt_Register’ 中:
iwmmxt.c:3707: 警告:隐式声明与内建函数 ‘memcpy’ 不兼容
iwmmxt.c:3712: 警告:隐式声明与内建函数 ‘memcpy’ 不兼容
iwmmxt.c: 在函数 ‘Store_Iwmmxt_Register’ 中:
iwmmxt.c:3722: 警告:隐式声明与内建函数 ‘memcpy’ 不兼容
iwmmxt.c:3727: 警告:隐式声明与内建函数 ‘memcpy’ 不兼容
make[2]: *** [iwmmxt.o] 错误 1
make[2]: Leaving directory `/root/Documents/gdb-6.2.1/sim/arm'
make[1]: *** [all] 错误 2
make[1]: Leaving directory `/root/Documents/gdb-6.2.1/sim'
make: *** [all-sim] 错误 2


根据上面的提示找到错误的文件和在文件的那一行:
c:2117: error: invalid lvalue in assignment
c:2133: error: invalid lvalue in assignment
c:2169: error: invalid lvalue in assignment
c:2177: error: invalid lvalue in assignment
c:2186: error: invalid lvalue in assignment
c:2191: error: invalid lvalue in assignment

方法同上,都是改gdb压缩包的文件,这次改的是
gdb-6.2.1/sim/arm/iwmmxt.c文件里的:

第2117、2133、2169、2177、2186、2191行;

方法如下:

将2117行的    (signed long long) t += s;   
改成        (signed long long)t;    t = (signed long long)(t + s);


将2133行的    (signed long long) wR[BITS (12, 15)] += (signed long long) t;   
改成    {(signed long long) wR[BITS (12, 15)];    wR[BITS (12, 15)] = (signed long long)(wR[BITS (12, 15)] + t);}


将2169行的    (signed long) s1 = a * b;   
改成    (signed long) s1;    s1 =(signed long)(a * b);


将2177行的    (signed long) s2 = a * b;
改成    (signed long) s2;    s2 =(signed long)(a * b);


将2186行的    (unsigned long) s1 = a * b;
改成    (unsigned long) s1;    s1 =(unsigned long)(a * b);


将2191行的    (signed long) s2 = a * b;
改成    (unsigned long) s2;    s2 =(unsigned long)(a * b);



编译Gdbserver:

linux-arm-low.c:26:21: error: sys/reg.h: No such file or directory
make: *** [linux-arm-low.o] 错误 1

根据在linux-arm-low.c中:
#ifdef HAVE_SYS_REG_H
#include <sys/reg.h>
#endif

在gdb/gdbserver/config.h修改如下:
/* Define if you have the <sys/reg.h> header file.  */
#define HAVE_SYS_REG_H 1

/* Define if you have the <sys/reg.h> header file.  */
//#define HAVE_SYS_REG_H 1 省略


#make install
#vi ~/.bash_profile        修改:PATH=$PATH:/root/arm-gdb/bin
#source ~/.bash_profile
#arm-linux-gdb -v

2-编译Gdbserver
#cd gdb-6.2.1
#./configure --target=arm-linux --host=arm-linux
#cd gdb/server
#./configure --target=arm-linux --host=arm-linux
修改 gdb/gdbserver/config.h   :      #define HAVE_SYS_REG_H 1    --> //#define HAVE_SYS_REG_H 1
#make CC=arm-linux-gcc
把gdb/gdbserver目录下的gdbserver复制到开发板系统(嵌入式linux)的/bin下
实例:
//hello.c
#include<stdio.h>
#include<string.h>
int main()
{
char *str=NULL;
strcpy(str,"hello");
printf("str is %s\n",str);
return 0;
}    

#arm-linux-gcc -g hello.c -o hello
设置主机IP地址为:192.168.1.10,开发板系统IP地址为:192.168.1.230(一般只需设置成同一个网段就行),
在开发板中:
#gdbserver 192.168.1.230:1234 hello
Process test created:pid=80   //使gdbserver在1234端口监听。
在主机中:
#arm-linux-gdb
(gdb) target remote 192.168.1.230:1234    //若链接成功,开发板的串口终端会显示如下:
Remote debugging from host 192.168.1.10
(gdb) symbol file  hello                               //此处的hello是PC机上的所在路径的hello
(gdb) list
(gdb) break 5
(gdb) continuing
(gdb) step                //会提示出现段错误(通常有内存的非法访问引起的)


常用的gdb调试命令:(某些唯一开头字母的命令可用开头字母直接替代,也可用Tab来显示其完整的命令名)
file 文件名        在gdb中载入某可执行文件                    symbol file hello
break n            设置断点                                break 5
info                 查看和可执行程序相关的各种信息      info breakpoint   delete/disable/enable breakpoint num
kill                终止正在调试的程序
print                显示变量或者表达式的值
set args         设置调试程序的运行参数
watch                在程序中设置观测点(如果数据改变,将给出变化前后的情况)
delete            删除设置的某个断点或观测点
clear                删除设置的某个断点或观测点
continue            从断点处继续执行程序
list                列出gdb中加载的可执行程序的代码
run                运行在gdb中加载的可执行程序
next                单步执行所加载的程序
step                单步,可进入函数内,查看执行情况(用finish回到调用处)
whatis            查看变量或函数类型
pype                显示数据结构定义情况
make                在不退出gdb情况下,编译程序
quit                退出gdb

gdb调试带参数的程序

gdb --args ./testprg arg1 arg2