新聞中心

        EEPW首頁 > 嵌入式系統 > 設計應用 > GDB+GdbServer: ARM程序調試

        GDB+GdbServer: ARM程序調試

        作者: 時間:2016-11-22 來源:網絡 收藏
        內容摘要遠程調試環境由宿主機GDB和目標機調試stub共同構成,兩者通過串口或TCP連接。使用 GDB標準程串行協議協同工作,實現對目標機上的系統內核和上層應用的監控和調試功能。調試stub是嵌入式系統中的一段代碼,作為宿主機GDB和目標機調試程序間的一個媒介而存在。 就目前而言,嵌入式Linux系統中,主要有三種遠程調試方法,分別適用于不同場合的調試工作:用ROM Monitor調試目標機程序、用KGDB調試系統內核和用gdbserver調試用戶空間程序。這三種調試方法的區別主要在于,目標機遠程調試stub 的存在形式的不同,而其設計思路和實現方法則是大致相同的。 而我們最常用的是調試應用程序。就是采用gdb+gdbserver的方式進行調試。在很多情況下,用戶需要對一個應用程序進行反復調試,特別是復雜的程序。采用GDB方法調試,由于嵌入式系統資源有限性,一般不能直接在目標系統上進行調試,通常采用gdb+gdbserver的方式進行調試。

        本文引用地址:http://www.104case.com/article/201611/319975.htm

        gdb的簡單使用

        GDB是GNU開源組織發布的一個強大的UNIX下的程序調試工具。或許,各位比較喜歡那種圖形界面方式的,像VC、BCB等IDE的調試,但如果你是在 UNIX平臺下做軟件,你會發現GDB這個調試工具有比VC、BCB的圖形化調試器更強大的功能。所謂“寸有所長,尺有所短”就是這個道理。一般來說,GDB主要幫忙你完成下面四個方面的功能: 1、啟動你的程序,可以按照你的自定義的要求隨心所欲的運行程序。
        2、可讓被調試的程序在你所指定的調置的斷點處停住。(斷點可以是條件表達式)
        3、當程序被停住時,可以檢查此時你的程序中所發生的事。
        4、動態的改變你程序的執行環境。從上面看來,GDB和一般的調試工具沒有什么兩樣,基本上也是完成這些功能,不過在細節上,你會發現GDB這個調試工具的強大,大家可能比較習慣了圖形化的調試工具,但有時候,命令行的調試工具卻有著圖形化工具所不能完成的功能。讓我們一一看來。
        一個調試示例
        —————— 源程序:tst.c 1 #include
        2
        3 int func(int n)
        4 {
        5 int sum=0,i;
        6 for(i=0; 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>gdbtst <---------- 啟動GDB
        GNUgdb5.1.1
        Copyright 2002 Free Software Foundation, Inc.
        GDBis 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 forGDB. Type "show warranty" for details.
        ThisGDBwas 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; i7 {
        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總體介紹... 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-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://ftp.gnu.org/gnu/gdb

        外網的ftp我經常上不去,國內常見的開源社區的下載頻道通常都有下載的http://download.chinaunix.net/download/0004000/3482.shtml,最新版本為gdb-6.5.tar.bz2。下載到某個目錄,筆者下載到/opt/。但要注意,gdb的版本需要和croostool相匹配。

        三、配置編譯及安裝下載

        下載完后,進入/opt/目錄,配置編譯步驟如下:

        #tar jxvfgdb-6.5-tar-bz2

        #cdgdb-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+gdbservernfs調試流程

        下面就可以用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-gdbhello

        最后一行顯示:ThisGDBwas 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-gdbhello

        在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

        2

        3 int

        4 func(int n){

        5 int sum=0, i;

        6 for (i=0; 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@AT91RM9200DKarm]$./gdbserver 192.168.0.12:2345 mainparacarm

        ./gdbserver: error in loading shared libraries: libthread_db.so.1: cannot open [root@AT91RM9200DKarm]$

        host pc:

        #cd /usr/local/arm-gdb/bin/以便能夠運行arm-linux-gdb,但是無此必要,可在環境變量中設置此路徑即可。

        #copy gdbtest /usr/local/arm-gdb/bin/ //將前面編譯的文件gdbtest拷貝到此目錄

        #./arm-linux-gdbgdbtest

        (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
        #cdgdb-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./../../bfdlinux-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 header file. */

        /*define HAVE_SYS_REG_H 1 */

        /*have no 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@AT91RM9200DKarm]$./gdbserver 192.168.0.12:2345 mainparacarmgdb6.5

        ./gdbserver: error in loading shared libraries: libthread_db.so.1: cannot open

        [root@AT91RM9200DKarm]$./gdbserver 192.168.0.14:2345 mainparacarmgdb6.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.0libtermcap.so.2

        [root@AT91RM9200DKarm]$cp libthread_db-1.0.so libthread_db.so.1

        [root@AT91RM9200DKarm]$cp libthread_db.so.1 /lib/

        [root@AT91RM9200DKarm]$./gdbserver 192.168.0.12:2345 mainparacarm

        ./gdbserver: /lib/libc.so.6: version `GLIBC_2.2 not found (required by /lib/li)

        難道目前的gdb6.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



        評論


        技術專區

        關閉
        主站蜘蛛池模板: 台州市| 江永县| 句容市| 霍林郭勒市| 通河县| 乐清市| 辽宁省| 平谷区| 民权县| 梁河县| 广昌县| 青铜峡市| 惠来县| 邳州市| 通许县| 江孜县| 英吉沙县| 克山县| 龙胜| 乌什县| 大关县| 新蔡县| 新安县| 昌图县| 博湖县| 黄平县| 莲花县| 达拉特旗| 霍城县| 长沙市| 朝阳区| 蒲江县| 洛阳市| 江口县| 阳春市| 叶城县| 瑞安市| 凤阳县| 革吉县| 贡嘎县| 司法|