天天看點

gdbserver遠端調試

遠端調試環境由主控端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:如果放到了/home/cby目錄:

#cd /home/cby

#tar zxvf gdb-6.6.tar.gz

#cd gdb-6.6

#./configure --target=arm-linux --prefix=/home/cby/arm-gdb

#make

#make install

然後建立gdbserver:

#mkdir /home/cby/gdbserver

#cd ../gdbserver

#chmod +x /home/cby/gdb-6.6/gdb/gdbserver/configure

#CC=arm-linux-gcc ../gdb-6.6/gdb/gdbserver/configure /

--host=arm-linux  --prefix=/home/cby/gdbserver

#arm-linux-strip gdbserver

#cp gdbserver /tftpboot

二、調試步驟

1、交叉編譯,帶參數-gstabs 或 -g 加入調試資訊。

假設要調試的程式為hello.c。

#arm-linux-gcc -g hello.c -o hello

2、在Target Board開啟gdbserver

#gdbserver  <host-ip>:2345 hello   (我的host-ip是192.168.0.178)

gdbserver開始監聽2345端口(你也可以設其他的值),然後啟動hello,你會看到“Process test created:pid=88”

3、回到Host端

#export PATH=$PATH:/home/cby/arm-gdb/bin(arm-linux-gdb的路徑)

#arm-linux-gdb hello

最後一行顯示:This GDB was configured as “--host=i686-pc-linux-gnu,--target=arm-linux”...

說明此gdb在X86的Host上運作,但是調試目标是ARM代碼。

(gdb)target remote <target-board-ip>:2345    (我的target-board-ip is 192.168.0.177)

注意:你的端口号必須與gdbserver開啟的端口号一緻,這樣才能進行通信。

建立連結後,就可以進行調試了。調試在Host端,跟gdb調試方法相同。注意的是要用“c”來執行指令,不能用“r”。因為程式已經在Target Board上面由gdbserver啟動了。結果輸出是在Target Board端,用超級終端檢視。

GDB調試器提供了兩種不同的調試代理用于支援遠端調試,即gdbserver方式和stub(插樁)方式。

   這兩種遠端調試方式是有差別的。gdbserver本身的體積很小,能夠在具有很少存儲容量的目标系統上獨立運作,因而非常适合于嵌入式環境;而stub 方式則需要通過連結器把調試代理和要調試的程式連結成一個可執行的應用程式檔案,如果程式運作在沒有作業系統的機器上,那麼stub需要提供異常和中斷處 理序,以及序列槽驅動程式,如果程式運作在有作業系統的嵌入式平台上,那麼stub需要修改序列槽驅動程式和作業系統異常處理。顯然,對于在有嵌入式作業系統 支援下的開發而言,gdbserver比stub程式更容易使用。這裡使用的是GDB+gdbserver的方式,建立遠端調試的環境。

   gdbserver是一個可以獨立運作的控制程式,它可以運作在類UNIX作業系統上,當然,也可以運作在Linux的諸多變種。gdbserver允許遠端GDB調試器通過target remote指令與運作在目标闆上的程式建立連接配接。

   GDB和gdbserver之間可以通過序列槽線或TCP/IP網絡連接配接通信,采用的通信協定是标準的GDB遠端串行協定( Remote Serial Protocol RSP)。

   使用gdbserver調試方式時,在目标機端需要一份要調試的程式的拷貝,這通常是通過ftp或NFS下載下傳到目标機上的,主控端端也需要這信一份拷貝。 由于gdbserver不處理程式符号表,是以如果有必要,可以用strip工具将要複制到目标機上的程式中的符号表去掉以節省空間。符号表是由運作在主 機端的GDB調試器處理的,不能将主機端的程式中的符号表去掉。

    雖然大部分的Linux發行版都已經安裝了GDB,但是那都是基于PC的平台的,我們要使用的是在ARM平台上,是以要重新下載下傳gdb的源碼,并修改以 适應自己的目标平台。可以從http://www.gnu.org/software/gdb/download,獲得。這裡使用的是GDB的最新的版本 7.1。首先将下載下傳到的gdb-7.1.tar.bz2複制到/home/zfz/gdb目錄下。在控制台下輸入下面的解包指令

<code>#tar -jxvf gdb-7.1.tar.bz2</code>

  解包之後會生成gdb-7.1/目錄,所有的源碼都在這個目錄下,在gdb目錄下建立立一個目錄arm-linux-gdb,把生成的可執行檔案都放在這個目錄下,

在gdb-7.1目錄下,輸入下面的指令,配置GDB源碼:

<code>./configure --target=arm-linux --prefix=/home/frank/gdb --program-prefix=arm-linux-</code>

   其中 --target=arm-linux選項,表示針對的目标平台是運作linux核心的ARM體系結構的平台,後面的選項--prefix=/home /frank/gdb 則是指定編譯完成後的安裝目錄,最後一個選項--program-prefix=arm-

linux-用來指定在編譯生成的二進制可執行檔案名之前加上字首,這裡加上的字首是arm-linux-。這樣就可以和主控端上的調試檔案相差別。

<code>make</code>

把源檔案編譯成相應的目标檔案,并連接配接成可執行的二進制檔案 。然後,再執行下面的指令,

<code>make intall</code>

   執行完該指令後,就會在我們剛才建立的arm-linux-gdb目錄下生成bin/,lib/,share/這幾個子目錄,其中在bin下有三個可執行 檔案分别為:arm-linux-gdb. arm-linux-run ,arm-linux-gdbui.arm-linux-gdb,就是我們需要的調試器。

   編譯完arm-linux-gdb之後,接下來就需要編譯在目标闆上運作的gdbserver了,在gdb/gdb7.1/gdb下有一個gdbserver的子目錄,這個目錄包括了編譯gdbserver所需要的所有的東西。

首先,進行gdbserver目錄

<code>./configure --target=arm-linux --host=arm-linux</code>

此時,如果要直接進行編譯的話,會出現錯誤,

<code>linux-arm-low.c:26:21:sys/reg.h: 沒有那個檔案 或目錄 make:*****[linux-arm-low.0] Error 1</code>

<code>這裡的sys/reg.h是指/usr/include/sys/reg.h,在該檔案中定義的寄存器都是針對x86平台的,對于運作 在ARM平台上的gdbserver顯然是不對的,在configure後,将會生成一個config.h檔案,在這個檔案中定 義了相應的宏,在config.h中我們可以看到這樣一個C語言的宏定義,</code>

<code>#define HAVE_SYS_REG_H 1 在linux-arm-low.c檔案中出錯的代碼行: #ifdef HAVE_SYS_REG_H  #include &lt;sys/reg.h&gt; #endif</code>

這裡我們隻需要把conifg.h檔案中 的#define HAVE_SYS_REG_H 1注釋掉就OK了。由于gdbserver是運作在ARM-linux平台上的,是以需要使用交叉編譯器arm-linux-gcc,這可以通過給 make加上CC參數來實作這裡我使用的是預設的,你也可以使用一個絕對的路徑來指定一個arm-linux-gcc.

   所有的工作都已經完成了,隻要把生成的gdbserver下載下傳到目标闆上,或者是通過NFS挂載到目标闆上就可能進行遠端的調試了,如果就是足夠幸運的 話,編譯中沒有出現什麼錯誤的話,這樣完全可以了,不過在我的系統上卻不是那麼幸運。我一直使用的是友善之臂的arm-linux-gcc-4.3.2交 叉編譯器,這個版本的編譯器中,已經自帶了arm-linux-gdb.

在終端中輸入arm-linux-gdb -v 可以看到下面的資訊

<code>#make CC = arm-linux-gcc</code>

GNU gdb (Sourcery G++ Lite 2008q3-72) 6.8.50.20080821-cvs

<code>Copyright (C) 2008 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later &lt;http://gnu.org/licenses/gpl.html&gt; 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 "--host=i686-pc-linux-gnu --target=arm-none-linux-gnueabi". For bug reporting instructions, please see: &lt;https:</code>

   這裡可以看出arm-linux-gcc-4.3.2自帶的arm-linux-gdb的版本是6.8版的,而我們一直編譯的是7.1版本的。一開始我并 沒有注意arm-linux-gdb的版本資訊,不過在使用過程中,我把用生成的gdbserver的7.1版本用NFS加載到目标闆上,而在主控端上用 的arm-linux-gcc 6.8版本,一直出現下面的錯誤:

先在目标機上運作下面的gdbserver,注意這裡的IP位址是主控端上的IP位址。

<code>[root@Frankzfz]$gdbserver 10.27.10.48:9000 ./test_seg_fault  Process ./test_seg_fault created; pid = 760  Listening on port 9000  Remote debugging from host 10.27.10.48</code>

然後在主控端上運作

<code>arm-linux-gdb/bin$ arm-linux-gdb GNU gdb (Sourcery G++ Lite 2008q3-72) 6.8.50.20080821-cvs Copyright (C) 2008 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later &lt;http://gnu.org/licenses/gpl.html&gt; 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 "--host=i686-pc-linux-gnu --target=arm-none-linux-gnueabi". For bug reporting instructions, please see: &lt;https://support.codesourcery.com/GNUToolchain/&gt;. (gdb) target remote 10.27.10.23:9000 Remote debugging using 10.27.10.23:9000 Malformed packet(b) (missing colon): ore:0; Packet: 'T050b:00000000;0d:804ebdbe;0f:b0070040;thread:2f8;core:0;' (gdb) symbol-file test_seg_fault Reading symbols from /home/zfz/kernel/fs/arm-linux-gdb/bin/test_seg_fault...done. (gdb) break main Breakpoint 1 at 0x84a8: file test_seg_fault.c, line 7. (gdb) info main Undefined info command: "main". Try "help info". (gdb) info b Num Type Disp Enb Address What 1 breakpoint keep y 0x000084a8 in main at test_seg_fault.c:7 (gdb) c The program is not being run. (gdb)</code>

輸入c指令時說是程式沒有運作,可是程式已經在目标闆上運作了,不用輸入run指令,當輸入  target remote 10.27.10.23:9000結束後,在minicom中可以看到目标闆上的輸出資訊有了變化.

<code>[root@Frankzfz]$gdbserver 10.27.10.48:9000 ./test_seg_fault  Process ./test_seg_fault created; pid = 760  Listening on port 9000  Remote debugging from host 10.27.10.48  readchar: Got EOF  Remote side has terminated connection. GDBserver will reopen the connection.  Listening on port 9000</code>

   在這裡搗鼓了一整天沒有弄明白錯誤在那裡,看到了arm-linux-gdb 和gdbserver的版本不比對,是以就變換arm-linux-gdb,這裡要把arm-linux-gdb加入到.profile檔案中,或者environment檔案中,

<code>PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/lib/jvm/j dk1.6.0_17/bin:/home/zfz/kernel/fs/arm-linux-gdb/bin:/home/zfz/linux- tool/usr/local/arm/4.3.2/bin"</code>

   當時一直在,arm-linux-gdb/bin目錄下執行,其實執行的也都是arm-linux-gcc-4.3.2中的arm-linux-gdb, 如果這樣你看到的版本資訊還是6.8版本的話,也可以直接在arm-linux-gcc-4.3.2中把arm-linux-gdb檔案删除掉。

   這樣再一次檢視arm-linux-gdb的資訊。

<code>GNU gdb (GDB) 7.1 Copyright (C) 2010 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later &lt;http://gnu.org/licenses/gpl.html&gt; 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 "--host=x86_64-unknown-linux-gnu --target=arm-linux". For bug reporting instructions, please see: &lt;http://www.gnu.org/software/gdb/bugs/&gt;.</code>

   使用GDB進行調試,首先,我的主控端的IP 10.27.10.48,開發闆上的IP 10.27.10.23在開發闆上運作gdbserver 10.27.10.48:9000 test,這裡是通過NFS,把要調試的程式加載到目标版上面的。這樣就可從在主控端上運作。

<code>zfz@zfz:~/kernel/fs/arm-linux-gdb/bin$ ./arm-linux-gdb test ./arm-linux-gdb: Symbol `acs_map' has different size in shared object, consider re-linking GNU gdb (GDB) 7.1 Copyright (C) 2010 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later &lt;http://gnu.org/licenses/gpl.html&gt; 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 "--host=x86_64-unknown-linux-gnu --target=arm-linux". For bug reporting instructions, please see: &lt;http://www.gnu.org/software/gdb/bugs/&gt;... Reading symbols from /home/zfz/kernel/fs/arm-linux-gdb/bin/test...done. (gdb) target remote 10.27.10.23:9000 Remote debugging using 10.27.10.23:9000 warning: Unable to find dynamic linker breakpoint function. GDB will be unable to debug shared library initializers and track explicitly loaded dynamic code. 0x400007b0 in ?? () (gdb) l Cannot access memory at address 0x0 1    #include &lt;stdio.h&gt; 2    int main( void ) 3    { 4     int i=2; 5     int x, y; 6      7     x=(++i); 8     printf(" %d %d\n", i,x); 9     x+=(++i); 10     printf(" %d %d\n", i,x); (gdb) l 11     x+=(++i); 12     printf(" %d %d\n", i,x); 13     i=2; 14     y=(i++)+(i++)+(i++); 15     printf(" %d %d\n", i,y); 16      17     return 0; 18    } 19      (gdb) break 9 Breakpoint 1 at 0x83b8: file test.c, line 9. (gdb) c Continuing. Error while mapping shared library sections: `/lib/libc.so.6': not in executable format: File format not recognized Error while mapping shared library sections: /lib/ld-linux.so.3: No such file or directory. Breakpoint 1, main () at test.c:9 9     x+=(++i); (gdb) step 10     printf(" %d %d\n", i,x); (gdb) next 11     x+=(++i); (gdb) next 12     printf(" %d %d\n", i,x); (gdb) next 13     i=2; (gdb) next 14     y=(i++)+(i++)+(i++); (gdb) next 15     printf(" %d %d\n", i,y); (gdb) next 17     return 0; (gdb) next 18    } (gdb) next</code>

在目标闆上的輸出:

<code>[root@Frankzfz]$gdbserver 10.27.10.48:9000 ./test  Process ./test created; pid = 746  Listening on port 9000  Remote debugging from host 10.27.10.48   3 3   4 7   5 12   5 6</code>

補充:前面還有庫檔案錯誤的問題,這裡可以通過下面這樣的方法的來解決。需要說明的是,遠端調試,可能程式動态庫,我們可以這樣設定:

<code>set solib-absolute-prefix /nfsroot/rootfs set solib-search-path /nfsroot/rootfs/lib</code>

調試找不到源代碼,如下設定:

<code>directory xfer-1.0.0/src/</code>

下面是調試的情況:

<code>(gdb) set solib-absolute-prefix /home/kernel/fs/root_nfs (gdb) set solib-search-path /home/kernel/fs/root_nfs/lib (gdb) target remote 10.27.10.23:9000 Remote debugging using 10.27.10.23:9000 Reading symbols from /home/kernel/fs/root_nfs/lib/ld-linux.so.3...(no debugging symbols  found)...done. Loaded symbols for /home/kernel/fs/root_nfs/lib/ld-linux.so.3 0x400007b0 in ?? () from /home/kernel/fs/root_nfs/lib/ld-linux.so.3 (gdb) l 3    { 4     int i=2; 5     int x, y; 6      7     x=(++i); 8     printf(" %d %d\n", i,x); 9     x+=(++i); 10     printf(" %d %d\n", i,x); 11     x+=(++i); 12     printf(" %d %d\n", i,x); (gdb) l 13     i=2; 14     y=(i++)+(i++)+(i++); 15     printf(" %d %d\n", i,y); 16      17     return 0; 18    } 19      (gdb) b 8 Note: breakpoint 1 also set at pc 0x83a8. Breakpoint 2 at 0x83a8: file test.c, line 8. (gdb) c Continuing. Breakpoint 1, main () at test.c:8 8     printf(" %d %d\n", i,x); (gdb) n 9     x+=(++i); (gdb) b 12 Breakpoint 3 at 0x8400: file test.c, line 12. (gdb) n 10     printf(" %d %d\n", i,x); (gdb) n 11     x+=(++i); (gdb) n Breakpoint 3, main () at test.c:12 12     printf(" %d %d\n", i,x); (gdb) n 13     i=2; (gdb) n 14     y=(i++)+(i++)+(i++); (gdb) n 15     printf(" %d %d\n", i,y); (gdb) n 17     return 0; (gdb) n 18    } (gdb) n 0x4003b004 in __libc_start_main ()    from /home/kernel/fs/root_nfs/lib/libc.so.6 (gdb)</code>