1. 程式人生 > >QEMU+gdb除錯Linux核心全過程

QEMU+gdb除錯Linux核心全過程

1、編譯原始碼(Linux kernel 4.6.2)

make menuconfig

執行make menuconfig時報錯缺少庫檔案

需要安裝依賴庫

sudo apt-get install aptitude
sudo aptitude install libncurses5-dev
sudo apt-get install libssl-dev

首先編譯核心,編譯核心時注意要選兩個選項。(注意:除此之外kernel hacking選項下其他的選項都不要選,否則會出現斷點無法攔截的情況。這個說法有待驗證)

kernel hacking –> kernel debugging –> compile the kernel with debug info

kernel hacking –> compile the kernel with frame pointers

make bzImage -j4

在編譯過程中會出現如下的warning,最好解決掉
這裡寫圖片描述
解決方法:
進入make menuconfig的圖形介面,選擇kernel hacking,將frame size由1024改成2048即可。
這裡寫圖片描述
這裡寫圖片描述
這裡寫圖片描述
這樣就生成了bzImagevmlinux

bzImage 位於Linux-4.6.2/arch/x86/boot/bzImage

vmlinux 位於linux-4.6.2/vmlinux

2、編譯QEMU(使用最新版2.12)

2.1 下載原始碼

https://download.qemu.org/

下載2.12.0版本;

2.2 編譯前的工作

編譯之前需要安裝一些庫檔案,否則會卡在下面的頁面不動;
這裡寫圖片描述

因此需要安裝SDL圖形庫:(注意是2.0版本的SDL)

sudo apt-get install  libsdl2-2.0
sudo apt-get install  libsdl2-dev

執行下列程式碼:

./configure --enable-debug  --target-list=x86_64-softmmu

執行上述命令,會出現不存在pixman錯誤;
這裡寫圖片描述

執行如下命令解決:

sudo apt-get install libpixman-1-dev

緊接著,執行:

sudo make &&  make install

又會遇到如下問題:
缺少flex和bison
這裡寫圖片描述
執行如下命令:

sudo apt-get install flex bison

再執行sudo make install即可。

3、編譯gdb(更換新的gdb)

  • 開始編譯:

    ./configure --prefix=~/gdb
    make 
    make install

    執行gdb需要進入~/gdb/bin目錄下,執行./gdb;

4、busybox建立最小檔案系統

4.1 動態編譯的方法(不推薦)

動態編譯

4.2 另一種靜態庫編譯方法(推薦):

靜態編譯

具體的流程如下:

4.2.1 安裝busybox(1.25.0版本)

  • 編譯busybox原始碼

    cd busybox-1.25.0
    
    make defconfig
    
    make menuconfig

    在menuconfig中修改配置,使用靜態編譯busybox,否則在程式執行期間需要對相應的庫進行動態載入,那麼在根檔案系統中則需要提供其所需的共享庫。

    進入menuconfig圖形介面,選擇

           -> Busybox Settings 
    
          -> Build Options  
    
            [*] Build BusyBox as a static binary (no shared libs)         
    
    make -j4

    make 期間會遇到許多的warning,不用管它,對安裝影響不大。

    sudo make install

    此時可以在busybox-1.25.0/中看到生成的_install目錄。通過下面的命令可以驗證busybox是否安裝正確:

    ./busybox ls
  • 生成initrd

    首先將上一步生成的_install資料夾複製到其他位置

     cd ..
    
     mkdir ramdisk
    
     cd ramdisk
    
     cp -r ../busy-1.25.0/_install/*  .

    設定初始化程序init(建立一個軟連結,一定不能直接複製過去)

    cd ramdisk
    
    ln -s bin/busybox init

    設定開機啟動程式

    首先,我們需要先設定一些程式執行所需要的資料夾

    mkdir -pv {bin,sbin,etc,proc,sys,usr/{bin,sbin},dev}

    init程式首先會訪問etc/inittab檔案,因此,我們需要編寫inittab,指定開機需要啟動的所有程式

    cd etc
    vim inittab

    inittab檔案的內容如下所示:

    ::sysinit:/etc/init.d/rcS   
    ::askfirst:-/bin/sh    
    ::restart:/sbin/init
    ::ctrlaltdel:/sbin/reboot
    ::shutdown:/bin/umount -a -r
    ::shutdown:/sbin/swapoff -a

    賦予可執行許可權

    chmod +x inittab

    編寫系統初始化命令
    從inittab檔案中可以看出,首先執行的是/etc/init.d/rcS指令碼,因此,我們生成初始化指令碼

    mkdir init.d
    cd init.d
    vim rcS

    rcS檔案的內容如下所示:

    
    #!/bin/sh
    
    mount proc
    mount -o remount,rw /
    mount -a    
    clear                               
    echo "My Tiny Linux Start :D ......"

    賦予可執行許可權

    chmod +x rcS

    在rcS指令碼中,mount -a 是自動掛載 /etc/fstab 裡面的東西,可以理解為掛在檔案系統,因此我們還需要編寫 fstab檔案來設定我們的檔案系統。

    cd ramdisk/etc/
    
    vim fstab

    fstab檔案內容如下:

    
    # /etc/fstab
    
    
    proc            /proc        proc    defaults          0       0
    
    sysfs           /sys         sysfs   defaults          0       0
    
    devtmpfs        /dev         devtmpfs  defaults          0       0

    至此,我們已經完成了RAM Disk中相關檔案的配置,可以壓縮生成檔案映象了。

    cd ramdisk
    
    find . -print0 | cpio --null -ov --format=newc | gzip -9 > ../initramfs.img

    最後生成的initramfs.img就是我們的根檔案系統。

4.2.2 測試根檔案系統

下面就可以測試根檔案系統了。

 qemu-system-x86_64 -kernel /usr/src/Linux-4.6.2/arch/x86/boot/bzImage -initrd ../initramfs.img

這時qemu上會顯示出核心列印的各種資訊,最終顯示:

My Tiny Linux Start :D ......
Please press Enter to activate this console.

按Enter鍵後,就可以進入到檔案系統中,執行命令

ls /dev

如果能夠成功顯示目錄下所有檔案,則檔案系統掛載成功。

5. QEMU+GDB除錯核心

在之前的四個步驟中,已經準備好了全部的工具,下面就可以使用QEMU+GDB進行核心除錯了。
執行如下命令:

qemu-system-x86_64 -kernel /usr/src/linux-4.6.2/arch/x86/boot/bzImage -initrd ../initramfs.img -smp 2  -S -s

先使用命令啟動qemu。
  qemu-system-x86_64的引數比較多,這裡簡單說下:
  -kernel 是指定一個大核心檔案,當仁不讓的是bzImage。
  -initrd 是指定一個 initrd.img檔案,這個檔案就是我們使用busybox生成的initramfs.img。
  -smp 可以從名字猜想,它是給qemu指定幾個處理器,或者是幾個執行緒<嗯,大概意思就thread吧>。
  -gdb則是啟動qemu的內嵌gdbserver,監聽的是本地tcp埠1234—如果這樣寫: -gdb tcp:192.168.1.100:1234 ,似乎也是沒問題的。
  -S 就是掛起gdbserver,讓gdb remote connect it。
  -s 預設使用1234埠進行遠端除錯,和-gdb tcp::1234類似。
  -m 2048 指定記憶體大小為2048M

具體圖片如下:
這裡寫圖片描述
執行上個命令之後,彈出下面這個頁面:
這裡寫圖片描述

此時,開啟另一個terminal,執行如下命令:

gdb /usr/src/linux-4.6.2/vmlinux (修改成自己的vmlinux路徑)

target remote:1234 (預設埠是1234,進行遠端連線)

b start_kernel (設定斷點)

c (continue 執行到斷點處)

具體如下圖:
這裡寫圖片描述

可以看到gdb執行start_kernel斷點處停下來了,接下來就可以使用gdb的基本命令進行單步除錯了。

如果遇到b start_kernel停不下來,可以使用下面的命令:
(普通的break斷點無法使得gdb在斷點處執行,這是可能是一個gdb的bug())

hb start_kernel  //設定硬體斷點

如果需要檢視啟動過程中的資訊,則需要將qemu監視器中的內容重定向到terminal中。

使用如下命令 -nographic 配合-append”console=ttyS0”

這裡寫圖片描述

最後總結

安裝QEMU+GDB可以說是坎坷不斷,耗費了好幾天才最後弄完。
期間遇到過許多錯誤,這在其他教程中是沒有出現的,所以只能自己一點一點的解決。

安裝過程中遇到的各種warning和error都不能放過,也許就是因為這些錯誤導致安裝失敗。即使看起來安裝成功,這些錯誤未解決仍然可能造成許多錯誤。

比如,編譯核心原始碼時,遇到需要的warning,當時我沒有留意,結果在執行QEMU時就失敗了。所以我只能一個一個解決每一個warning,直到完全成功。
另一方面,如果核心編譯時總是有錯誤,可以選擇更高版本的核心。有些低版本核心年久失修,而且需要使用特定版本的gcc編譯。而你的機器gcc版本過高會造成許多的warning或者error。