天天看點

利用Qemu + Buildroot 進行核心源碼級調試利用Qemu + Buildroot 進行核心源碼級調試

利用Qemu + Buildroot 進行核心源碼級調試

概要:最近想研究下核心裡Bridge的具體實作過程,加之公司使用的嵌入式開發環境是基于Buildroot的,想着借用Buildroot來編譯qemu可以使用的核心和檔案系統,順便熟悉下Buildroot環境,也省去自己編譯核心通過busybox制作檔案系統的麻煩。

一、環境

1. 主控端(64位)系統:

$ lsb_release -d

Description: CentOS Linux release 7.2.1511 (Core)

2. Buildroot環境:

Buildroot是網上https://buildroot.org/download.html下載下傳的最新版本:buildroot-2016.05.tar.gz,預設編譯qemu_x86_64可用的版本。

$ tar zxvf buildroot-.tar.gz
$ cd buildroot-
$ make list-defconfigs                // 列出所有buildroot支援的平台的預設編譯。
$ make qemu_x86_64_defconfig          //  Build for qemu_x86_64
$ make linux-menuconfig               //  打開核心編譯選項
           

預設情況核心是沒有開啟調試資訊的,需要開啟核心調試資訊,否則gdb時候會出現:no debugging symbols found,開啟核心調試資訊選項的方式如下:

利用Qemu + Buildroot 進行核心源碼級調試利用Qemu + Buildroot 進行核心源碼級調試
利用Qemu + Buildroot 進行核心源碼級調試利用Qemu + Buildroot 進行核心源碼級調試
利用Qemu + Buildroot 進行核心源碼級調試利用Qemu + Buildroot 進行核心源碼級調試

可能有些時候這個”Compile the kernel with debug info”選項沒有直接顯示,可能會依賴其他編譯選項。開啟選項然後儲存。

$ make
           

直接編譯,最後編譯結果都會在output/images/目錄下面:

利用Qemu + Buildroot 進行核心源碼級調試利用Qemu + Buildroot 進行核心源碼級調試

3. Gdb環境

[[email protected] images]$ gdb -v 
GNU gdb (GDB) Red Hat Enterprise Linux -el7
Copyright (C)  Free Software Foundation, Inc.
License GPLv3+: GNU GPL version  or later <http://gnu.org/licenses/gpl.html>
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 "x86_64-redhat-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
           

系統自帶的gdb會有一個問題,當給核心設定斷點之後,繼續運作的時候回顯示:

再繼續運作則顯示:

利用Qemu + Buildroot 進行核心源碼級調試利用Qemu + Buildroot 進行核心源碼級調試

這樣沒辦法進行跟蹤調試,在網上找到解決辦法,給gdb打一個patch,patch内容如下:

--- remote.c    -- :: +
+++ remote-fixed.c  -- :: +
@@ -, +, @@
   buf_len = strlen (rs->buf);

   /* Further sanity checks, with knowledge of the architecture.  */
-  if (buf_len >  * rsa->sizeof_g_packet)
-    error (_("Remote 'g' packet reply is too long: %s"), rs->buf);
+  //if (buf_len > 2 * rsa->sizeof_g_packet)
+  //  error (_("Remote 'g' packet reply is too long: %s"), rs->buf);
+  
+    if(buf_len >  * rsa->sizeof_g_packet) {
+        rsa->sizeof_g_packet = buf_len;
+        for(i = ; i < gdbarch_num_regs(gdbarch); i++){
+            if(rsa->regs->pnum == -)
+               continue;
+            if(rsa->regs->offset >= rsa->sizeof_g_packet)
+               rsa->regs->in_g_packet = ;
+            else
+               rsa->regs->in_g_packet = ;
+        } 
+    }  
   /* Save the size of the packet sent to us by the target.  It is used
     as a heuristic when determining the max size of packets that the
     target can safely receive.  */
           

Centos系統上可以通過如下指令可以下載下傳系統已經安裝的對應版本的gdb源碼:

$ sudo yumdownloader  --source  gdb
           

gdb-7.6.1-80.el7.src.rpm 即為我們下載下傳的包含源碼的rpm包

安裝完之後在/root/rpmbuild/SPECS目錄下會生成一gdb對應的spec檔案:

利用Qemu + Buildroot 進行核心源碼級調試利用Qemu + Buildroot 進行核心源碼級調試

注意這個時候是在root使用者目錄操作,必須先切換成root使用者。然後通過下面的指令來編譯gdb。

我個人建議是把上面指令的輸出全部重定向到一個檔案中,然後在檔案中尋找gdb源碼包編譯的真正過程,其實可以直接通過比對configure和make關鍵字即可,我得到的gdb編譯的具體過程如下:

① 指定編譯選項,生成Makefile檔案

② 這步也是在配置

③ 這才是真正的編譯方法

④ 這個是生成文檔相關的方法

make -j4 -C gdb/doc gdb.info gdb/index.html gdb.pdf annotate.info annotate/index.html annotate.pdf MAKEHTMLFLAGS=--no-split MAKEINFOFLAGS=--no-split
make: Entering directory `/root/rpmbuild/BUILD/gdb-7.6.1/build-x86_64-redhat-linux-gnu/gdb/doc'
           

然後在/root/rpmbuild/BUILD目錄下會有gdb的源檔案,已經打好patch的源檔案。拷貝該源檔案到非root使用者的工作目錄,打上上面中的patch,按照上面的步驟重新編譯即可。

二、運作調試

ctrl+alt+1

ctrl+alt+2

可以切換,前者是螢幕輸出,後者是qemu控制台

運作起來後是黑屏,我們要切換到控制台,用滑鼠點選視窗,然後

ctrl+alt+2

如下圖所示輸入,然後回車,

ctrl+alt+1

切換回來,

ctrl+alt

切出滑鼠.

利用Qemu + Buildroot 進行核心源碼級調試利用Qemu + Buildroot 進行核心源碼級調試

在另一個終端中:

$ cd output/build/linux-..                                       
$ /home/james_xie/work/gdb-../gdb vmlinux 
(gdb) target remote localhost:1234
 Remote debugging using localhost:1234
 x0000000000000000 in irq_stack_union ()
 (gdb) b start_kernel
 Breakpoint  at xffffffff81882aad: file init/main.c, line .
 (gdb) c
 Continuing.
 Breakpoint , start_kernel () at init/main.c:
     {
 (gdb) step
         set_task_stack_end_magic(&init_task);
 (gdb) 
           

可以看到,qemu視窗,核心已經有部分顯示:

利用Qemu + Buildroot 進行核心源碼級調試利用Qemu + Buildroot 進行核心源碼級調試

通過gdb列出目前的源碼:

利用Qemu + Buildroot 進行核心源碼級調試利用Qemu + Buildroot 進行核心源碼級調試

可以看到現在就可以進行斷點調試核心。

三、調試驅動子產品

由于我要研究核心bridge的具體實作,是以把核心bridge部分以子產品的形式編譯進來。

利用Qemu + Buildroot 進行核心源碼級調試利用Qemu + Buildroot 進行核心源碼級調試

本來可以直接靜态編譯,編譯成子產品,順便熟悉下怎麼調試核心驅動子產品。

do_init_module

函數位置設定斷點。

利用Qemu + Buildroot 進行核心源碼級調試利用Qemu + Buildroot 進行核心源碼級調試

由于buildroot種有對ko檔案随核心啟動的時候自動加載的機制,是以這裡不需要手動去qemu裡面進行自動加載,在gdb裡面直接繼續運作,等待斷點即可,參數mod->sect_attrs->attrs放的是各個section的資訊。隻有.text資訊,并沒有.bss和.data,我們需要将這些資訊提供給gdb。使用如下指令即可:

利用Qemu + Buildroot 進行核心源碼級調試利用Qemu + Buildroot 進行核心源碼級調試

從圖檔中看到,虛拟機已經停在do_init_module函數位置,通過列印可知是正在加載stp.ko子產品,這個子產品在bridge子產品前面加載。

利用Qemu + Buildroot 進行核心源碼級調試利用Qemu + Buildroot 進行核心源碼級調試

這個列印的位址就是stp.ko子產品.text位址。通過add-symbol-file指令在gdb中引入該子產品符号。

利用Qemu + Buildroot 進行核心源碼級調試利用Qemu + Buildroot 進行核心源碼級調試

這個時候就可以調試stp.ko子產品裡面的函數,由于我首要看的是bridge子產品,是以先沒在stp.ko相關函數裡面設定斷點,在gdb裡面繼續運作等待其加載bridge.ko子產品時再次停止在斷點位置。

利用Qemu + Buildroot 進行核心源碼級調試利用Qemu + Buildroot 進行核心源碼級調試

同上操作一樣,我們引入bridge.ko子產品的符号。

利用Qemu + Buildroot 進行核心源碼級調試利用Qemu + Buildroot 進行核心源碼級調試

這樣就可以跟蹤核心代碼進行調試,不過子產品中__init, __devinit宏修飾的函數不能被找到,不知道是我編譯子產品的時候有問題,還是其他什麼原因,後續再更正。至此我們就可以跟蹤調試核心代碼,以及子產品代碼。

注意:現在的qemu都是預設開啟kvm加速的,這樣會導緻設定的核心斷點無法被斷住,當時最直接的想法是關閉kvm加速功能,打算重新編譯qemu對應的源碼包,但是關閉這個功能之後居然編譯不能通過,沒有深入糾結這個,而是無意中在另外一台電腦上發現,開啟VirtualBox虛拟機之後,qemu指令再執行的時候,會顯示“failed to initialize KVM: Device or resource busy”這樣的錯誤,表示kvm裝置被占用,這個時候設定的斷點就可以被斷住,之後想想其實要關閉kvm沒必要重新編譯qemu源碼,在bios裡面禁用虛拟化支援,或者解除安裝核心kvm相關驅動即可。