內容摘要 遠程調試環境由宿主機GDB和目標機調試stub共同構成,兩者通過串口或TCP連接。使用 GDB標准程串行協議協同工作,實現對目標機上的系統內核和上層應用的監控和調試功能。調試stub是嵌入式系統中的一段代碼,作為宿主機GDB和目標機調試程序間的一個媒介而存在。 就目前而言,嵌入式Linux系統中,主要有三種遠程調試方法,分別適用於不同場合的調試工作:用ROM Monitor調試目標機程序、用KGDB調試系統內核和用gdbserver調試用戶空間程序。這三種調試方法的區別主要在於,目標機遠程調試stub 的存在形式的不同,而其設計思路和實現方法則是大致相同的。 而我們最常用的是調試應用程序。就是采用gdb+gdbserver的方式進行調試。在很多情況下,用戶需要對一個應用程序進行反復調試,特別是復雜的程序。采用GDB方法調試,由於嵌入式系統資源有限性,一般不能直接在目標系統上進行調試,通常采用gdb+gdbserver的方式進行調試。
gdb的簡單使用
GDB是GNU開源組織發布的一個強大的UNIX下的程序調試工具。或許,各位比較喜歡那種圖形界面方式的,像VC、BCB等IDE的調試,但如果你是在 UNIX平台下做軟件,你會發現GDB這個調試工具有比VC、BCB的圖形化調試器更強大的功能。所謂“寸有所長,尺有所短”就是這個道理。一般來說,GDB主要幫忙你完成下面四個方面的功能: 1、啟動你的程序,可以按照你的自定義的要求隨心所欲的運行程序。
2、可讓被調試的程序在你所指定的調置的斷點處停住。(斷點可以是條件表達式)
3、當程序被停住時,可以檢查此時你的程序中所發生的事。
4、動態的改變你程序的執行環境。從上面看來,GDB和一般的調試工具沒有什么兩樣,基本上也是完成這些功能,不過在細節上,你會發現GDB這個調試工具的強大,大家可能比較習慣了圖形化的調試工具,但有時候,命令行的調試工具卻有着圖形化工具所不能完成的功能。讓我們一一看來。
一個調試示例
—————— 源程序:tst.c 1 #include <stdio.h>
2
3 int func(int n)
4 {
5 int sum=0,i;
6 for(i=0; i<n; i++)
7 {
8 sum+=i;
9 }
10 return sum;
11 }
12
13
14 main()
15 {
16 int i;
17 long result = 0;
18 for(i=1; i<=100; i++)
19 {
20 result += i;
21 }
22
23 printf("result[1-100] = %d \\n", result );
24 printf("result[1-250] = %d \\n", func(250) );
25 } 編譯生成執行文件:(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.
This GDB was configured as "i386-suse-linux"...
(gdb) l <-------------------- l命令相當於list,從第一行開始例出原碼。
1 #include <stdio.h>
2
3 int func(int n)
4 {
5 int sum=0,i;
6 for(i=0; i<n; i++)
7 {
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 tst.c:16
2 breakpoint keep y 0x08048456 in func at tst.c:5
(gdb) r <--------------------- 運行程序,run命令簡寫
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
(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>
gdb+gdbserver方式進行ARM程序調試
【摘要】:本文首先介紹了gdb+gdbserver相關的概念,然后介紹了其下載、編譯、安裝等過程;接着介紹了利用gdb+gdbserver調試應用程序的流程及實例等;最后分析了下gdb+gdbserver安裝過程中的常見問題。
【關鍵詞】:gdb,gdbserver,遠程調試
目錄
一、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-linux-gdb,Target Board端使用gdbserver。這樣,應用程序在嵌入式目標系統上運行,而gdb調試在Host端,所以要采用遠程調試(remote)的方法。進行GDB調試,目標系統必須包括gdbserver程序(在主機上正對硬件平台編譯成功后下載到目標機上),宿主機也必須安裝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我經常上不去,國內常見的開源社區的下載頻道通常都有下載的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]# 必須在gdbserver目錄下運行配置命令,此時才能用相對路徑 #./configure --target=arm-linux --host=arm-linux (--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將其更改為任何人都可以讀寫執行;使用arm-linux-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 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=arm-linux”...,如果不一致說明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里敲入入下命令:
set remotedevice /dev/ttyS0(這里設置串口1)
set remote baud 9600 (這里設置串口波特率)
set debug remote 1(可選)
target remote /dev/ttyS0
操作到這兒,gdb就應該和gdbserver聯系上了。
六、實戰調試
1.編輯文件
# vi gdbtest.c
1 #include <stdio.h>
2
3 int
4 func(int n){
5 int sum=0, i;
6 for (i=0; i<n; i++){
7 sum += i;
8 }
9 return sum;
10 }
11
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
將文件拷貝到目標板上:
先將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本地調試方法相同
七、 linux下安裝gdbserver問題
toolchain version: gdb的版本可能和交叉編譯器有很大的關系
gcc-3.3.2
glibc-2.2.5
binutils-2.15 此為croostool 3.3.2
安裝步驟:
下載解壓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/3.3.2/bin/armv5l-linux-gcc
出錯:
/usr/local/armv5l/3.3.2/bin/armv5l-linux-gcc -c -Wall -g -O2 -I. -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/3.3.2/armv5l-linux/include/sys/reg.h,即將該文件拷貝到交叉編譯器的include目錄下,再make,顯示錯誤:
/usr/local/armv5l/3.3.2/bin/armv5l-linux-gcc -c -Wall -g -O2 -I. -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 <sys/reg.h> header file. */
/*define HAVE_SYS_REG_H 1 */
/*have no <sys/reg.h> header file. so undefine 20070402 dding */
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
[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 3.3.2?
libpthread-0.8.so
libpthread.so libpthread.so.0 libresolv-2.1.3.so
libresolv.so.2 libstdc++.a.2.10.0 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
參考文檔
http://blog.chinaunix.net/u/27802/showart_211833.html
http://litttlebylittle.bokee.com/5803108.html
http://www.blogcn.com/u/93/99/litcatfish/index.html
內容摘要 遠程調試環境由宿主機GDB和目標機調試stub共同構成,兩者通過串口或TCP連接。使用 GDB標准程串行協議協同工作,實現對目標機上的系統內核和上層應用的監控和調試功能。調試stub是嵌入式系統中的一段代碼,作為宿主機GDB和目標機調試程序間的一個媒介而存在。 就目前而言,嵌入式Linux系統中,主要有三種遠程調試方法,分別適用於不同場合的調試工作:用ROM Monitor調試目標機程序、用KGDB調試系統內核和用gdbserver調試用戶空間程序。這三種調試方法的區別主要在於,目標機遠程調試stub 的存在形式的不同,而其設計思路和實現方法則是大致相同的。 而我們最常用的是調試應用程序。就是采用gdb+gdbserver的方式進行調試。在很多情況下,用戶需要對一個應用程序進行反復調試,特別是復雜的程序。采用GDB方法調試,由於嵌入式系統資源有限性,一般不能直接在目標系統上進行調試,通常采用gdb+gdbserver的方式進行調試。
gdb的簡單使用
GDB是GNU開源組織發布的一個強大的UNIX下的程序調試工具。或許,各位比較喜歡那種圖形界面方式的,像VC、BCB等IDE的調試,但如果你是在 UNIX平台下做軟件,你會發現GDB這個調試工具有比VC、BCB的圖形化調試器更強大的功能。所謂“寸有所長,尺有所短”就是這個道理。一般來說,GDB主要幫忙你完成下面四個方面的功能: 1、啟動你的程序,可以按照你的自定義的要求隨心所欲的運行程序。
2、可讓被調試的程序在你所指定的調置的斷點處停住。(斷點可以是條件表達式)
3、當程序被停住時,可以檢查此時你的程序中所發生的事。
4、動態的改變你程序的執行環境。從上面看來,GDB和一般的調試工具沒有什么兩樣,基本上也是完成這些功能,不過在細節上,你會發現GDB這個調試工具的強大,大家可能比較習慣了圖形化的調試工具,但有時候,命令行的調試工具卻有着圖形化工具所不能完成的功能。讓我們一一看來。
一個調試示例
—————— 源程序:tst.c 1 #include <stdio.h>
2
3 int func(int n)
4 {
5 int sum=0,i;
6 for(i=0; i<n; i++)
7 {
8 sum+=i;
9 }
10 return sum;
11 }
12
13
14 main()
15 {
16 int i;
17 long result = 0;
18 for(i=1; i<=100; i++)
19 {
20 result += i;
21 }
22
23 printf("result[1-100] = %d \\n", result );
24 printf("result[1-250] = %d \\n", func(250) );
25 } 編譯生成執行文件:(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.
This GDB was configured as "i386-suse-linux"...
(gdb) l <-------------------- l命令相當於list,從第一行開始例出原碼。
1 #include <stdio.h>
2
3 int func(int n)
4 {
5 int sum=0,i;
6 for(i=0; i<n; i++)
7 {
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 tst.c:16
2 breakpoint keep y 0x08048456 in func at tst.c:5
(gdb) r <--------------------- 運行程序,run命令簡寫
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
(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>
gdb+gdbserver方式進行ARM程序調試
【摘要】:本文首先介紹了gdb+gdbserver相關的概念,然后介紹了其下載、編譯、安裝等過程;接着介紹了利用gdb+gdbserver調試應用程序的流程及實例等;最后分析了下gdb+gdbserver安裝過程中的常見問題。
【關鍵詞】:gdb,gdbserver,遠程調試
目錄
一、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-linux-gdb,Target Board端使用gdbserver。這樣,應用程序在嵌入式目標系統上運行,而gdb調試在Host端,所以要采用遠程調試(remote)的方法。進行GDB調試,目標系統必須包括gdbserver程序(在主機上正對硬件平台編譯成功后下載到目標機上),宿主機也必須安裝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我經常上不去,國內常見的開源社區的下載頻道通常都有下載的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]# 必須在gdbserver目錄下運行配置命令,此時才能用相對路徑 #./configure --target=arm-linux --host=arm-linux (--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將其更改為任何人都可以讀寫執行;使用arm-linux-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 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=arm-linux”...,如果不一致說明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里敲入入下命令:
set remotedevice /dev/ttyS0(這里設置串口1)
set remote baud 9600 (這里設置串口波特率)
set debug remote 1(可選)
target remote /dev/ttyS0
操作到這兒,gdb就應該和gdbserver聯系上了。
六、實戰調試
1.編輯文件
# vi gdbtest.c
1 #include <stdio.h>
2
3 int
4 func(int n){
5 int sum=0, i;
6 for (i=0; i<n; i++){
7 sum += i;
8 }
9 return sum;
10 }
11
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
將文件拷貝到目標板上:
先將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本地調試方法相同
七、 linux下安裝gdbserver問題
toolchain version: gdb的版本可能和交叉編譯器有很大的關系
gcc-3.3.2
glibc-2.2.5
binutils-2.15 此為croostool 3.3.2
安裝步驟:
下載解壓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/3.3.2/bin/armv5l-linux-gcc
出錯:
/usr/local/armv5l/3.3.2/bin/armv5l-linux-gcc -c -Wall -g -O2 -I. -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/3.3.2/armv5l-linux/include/sys/reg.h,即將該文件拷貝到交叉編譯器的include目錄下,再make,顯示錯誤:
/usr/local/armv5l/3.3.2/bin/armv5l-linux-gcc -c -Wall -g -O2 -I. -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 <sys/reg.h> header file. */
/*define HAVE_SYS_REG_H 1 */
/*have no <sys/reg.h> header file. so undefine 20070402 dding */
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
[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 3.3.2?
libpthread-0.8.so
libpthread.so libpthread.so.0 libresolv-2.1.3.so
libresolv.so.2 libstdc++.a.2.10.0 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
參考文檔
http://blog.chinaunix.net/u/27802/showart_211833.html
