寫在最前面
昨天更新了一篇名為《QEMU+GDB調試linux內核全過程》[link][https://blog.csdn.net/weixin_37867857/article/details/88138432]的博客,發現排版比較混亂,而且思維也比較混亂。咋一看下來簡直是慘不忍睹,而且會給讀者在安裝過程中一種雲里霧里的感覺,加上將近一個半月沒有更新博客,導致對於博客的寫作比較生疏了。所以決定重新更新一篇博客,以彌補昨天的遺憾。
為了更新本博客,於今天特意安裝了一款虛擬機,重頭開始指導並且總結怎么調試linux內核,不足之處敬請大家諒解。
1.需要下載的工具或者源碼
使用qemu+gdb調試linux內核需要下載以下源碼包或者軟件包:
1. ubuntu14.04;
2. qemu;
3. linux內核, 版本2.6.32.20
4. gcc, 版本4.4
5. gdb, 版本7.9
6. busybox, 版本1.25.0
- 1
- 2
- 3
- 4
- 5
- 6
下面先解釋一下各個軟件的作用:
1. 綜述
1.1 ubuntu14.04
是我們需要編譯Linux內核和各個調試軟件運行的操作系統,建議虛擬機安裝即可,物理機太浪費;虛擬機安裝方法網上教程比較多,不在一一贅述。
1.2 qemu
是我們在虛擬機里面運行編譯好的內核的鏡像的一種方式,具體使用方法見以下使用。
1.3 linux內核
是我們主要調試的代碼,安裝gcc是為了編譯linux內核,請注意,我們如果想在qemu里面調試特定版本的內核代碼,一定要編譯,編譯好的鏡像才有可能使用(為什么說有可能?是因為不穩定的內核代碼有可能會產生panic)。
1.4 gdb
這個是我們主要的調試工具。
1.5 gcc
這個是我們主要的linux內核代碼編譯工具。
1.6 busybox
這個需要重點說明一下,是制作我們根文件系統的主要工具。什么是跟文件系統?
根文件系統首先是內核啟動時所mount的第一個文件系統,內核代碼映像文件保存在根文件系統中,而系統引導啟動程序會在根文件系統掛載之后從中把一些基本的初始化腳本和服務等加載到內存中去運行。
2. 工具及源碼下載
ubuntu14.04桌面版下載不需要贅述。網上很多。
2.1 qemu下載
sudo apt-get install qemu
- 1
如果有提示輸入密碼,輸入密碼即可。
2.2 linux內核
內核下載網址為:
[link][https://mirrors.edge.kernel.org/pub/linux/kernel/v2.6/]
或者使用終端命令:
wget https://mirrors.edge.kernel.org/pub/linux/kernel/v2.6/linux-2.6.32.20.tar.gz
- 1
2.3 gcc下載
由於ubuntu14.04默認安裝gcc為4.8的版本,對於編譯2.6.32.20的內核有點高,所以需要下載gcc源碼或者更換gcc版本。
對於gcc的版本查看使用如下命令:
- 1
alex@ubuntu:~$ gcc -v
Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/4.8/lto-wrapper
...此處省略
gcc version 4.8.2 (Ubuntu 4.8.2-19ubuntu1)
- 1
- 2
- 3
- 4
- 5
- 6
對於特定版本的gcc的安裝如下:
sudo apt-get install gcc-4.4 ls /usr/bin -l | grep gcc #可以看到gcc是一個鏈接文件,連接到gcc-4.8版本;可以把本鏈接刪除,重新鏈接gcc到gcc-4.4版本; sudo rm -rf /usr/bin/gcc-4.8 sudo ln /usr/bin/gcc-4.4 /usr/bin/gcc sudo gcc -v # 查看最終鏈接好的版本。
- 1
- 2
- 3
- 4
- 5
最后鏈接好的gcc版本如下:
alex@ubuntu:~$ gcc -v
Using built-in specs.
Target: x86_64-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Ubuntu/Linaro 4.4.7-8ubuntu1' --with-bugurl=file:///usr/share/doc/gcc-4.4/README.Bugs --enable-languages=c,c++,fortran --prefix=/usr --program-suffix=-4.4 --enable-shared --enable-linker-build-id --with-system-zlib --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --with-gxx-include-dir=/usr/include/c++/4.4 --libdir=/usr/lib --enable-nls --with-sysroot=/ --enable-clocale=gnu --enable-libstdcxx-debug --disable-libmudflap --disable-werror --with-arch-32=i686 --with-tune=generic --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu
Thread model: posix
gcc version 4.4.7 (Ubuntu/Linaro 4.4.7-8ubuntu1)
- 1
- 2
- 3
- 4
- 5
- 6
這樣gcc就安裝完成了。
2.4 . gdb源碼下載
ubuntu原生的gdb有bug,需要下載gdb源碼並且修改這個bug后才能使用,所以需要下載gdb源碼,並把gdb事先刪除掉,刪除命令為sudo apt-get autoremove gdb
等待刪除完成即可。
gdb源碼下載為7.9版本。源碼下載命令為:
wget http://ftp.gnu.org/gnu/gdb/gdb-7.9.tar.gz
- 1
2.5. busybox源碼下載。
wget https://busybox.net/downloads/busybox-1.25.0.tar.bz2
- 1
3. 已經下載源碼的編譯
對於以上源碼有些bug需要fix,有些可以直接編譯使用。所以需要編譯才能使用。
安裝之前一定要下載以下庫文件,否則編譯gdb或者linux內核時候可能會失敗。
sudo apt-get install aptitude
sudo aptitude install libncurses5-dev
sudo apt-get install libssl-dev
- 1
- 2
- 3
3.1 gdb的修改以及編譯。
編譯之前我們已經在2.4節中對於gdb已經remve掉,如果沒有確定是否reove掉可以使用gdb -v
命令查看,如果還有版本信息則說明沒有remove掉,需要重新remove掉。
gdb源碼下載解壓之后,以下操作全部在gdb解壓目錄完成。
需要更改gdb/remote.c中如下源碼:
if (buf_len > 2 * rsa->sizeof_g_packet) error (_("Remote 'g' packet reply is too long: %s"), rs->buf);
- 1
- 2
為如下:
if (buf_len > 2 * rsa->sizeof_g_packet) { rsa->sizeof_g_packet = buf_len; for (i = 0; i < gdbarch_num_regs (gdbarch); i++) { if (rsa->regs[i].pnum == -1) continue; if (rsa->regs[i].offset >= rsa->sizeof_g_packet) rsa->regs[i].in_g_packet = 0; else rsa->regs[i].in_g_packet = 1; } }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
對於此段的說明如下:修改gdb本地鏈接時候的棧區長度,把原先的錯誤判斷更改為靈活棧區長度。對於gdb的能力不變。
運行如下命令配置,編譯,安裝gdb。
sudo ./configure sudo apt-get install libncurses5-dev ## 一定要安裝此庫,否則編譯失敗。 sudo make sudo make install
- 1
- 2
- 3
- 4
編譯后的效果如下:
make install 命令之后效果:
驗證:gdb -v
命令版本驗證:
alex@ubuntu:~/Desktop/gdb-7.9$ gdb -v
GNU gdb (GDB) 7.9
Copyright (C) 2015 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 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-unknown-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word".
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
gdb7.9版本安裝完畢。
3.2 linux內核編譯
內核安裝配置,make menuconfig
選擇:kernel hacking,將frame size由1024改成2048即可。
如下圖:
內核編譯:
make -j4 數字根據自己的物理內核來判斷。
編譯后的效果圖:
3.3 busybox的編譯
解壓完busybox並進入解壓目錄之后完成makefile的配置文件如下:
make defconfig make menuconfig
- 1
- 2
make menuconfig 選擇配置時候應勾選如下配置:
跟配置選擇“Busybody settings”–>“Build Options”–>“Build BusyBox as a static binary (no shared libs)”
具體每一步配置如下:
最后使用make -j4
命令編譯busybox;
測試busy_box可以在編譯好的busybox跟目錄使用如下命令測試,可以使用./busybox ls
,如果測試命令跟ls
命令效果一致,則說明busybox編譯OK,否則找我也行,找度娘也行。
測試busybox效果如下:
上圖可以看到編譯時候產生了一個錯誤,不過沒有關系,一樣可以編譯是OK的,只要有ls
的效果就說明編譯OK。
然后使用make install
安裝busybox,可以看到busybox跟目錄下有一個"_install"目錄。這個目錄對於以后我們制作根文件系統很重要。
4.根文件系統的制作以及測試
4.1 根文件系統的制作
首先將上一步生成的_install文件夾復制到其他位置
cd .. mkdir ramdisk cd ramdisk cp -r ../busy-1.25.0/_install/* .
- 1
- 2
- 3
- 4
設置初始化進程init(建立一個軟鏈接,一定不能直接復制過去)
cd ramdisk ln -s bin/busybox init
- 1
- 2
設置開機啟動程序
首先,我們需要先設定一些程序運行所需要的文件夾
mkdir -pv {bin,sbin,etc,proc,sys,usr/{bin,sbin},dev}
- 1
init程序首先會訪問etc/inittab文件,因此,我們需要編寫inittab,指定開機需要啟動的所有程序
cd etc vim inittab
- 1
- 2
inittab文件的內容如下所示:
::sysinit:/etc/init.d/rcS
::askfirst:-/bin/sh
::restart:/sbin/init
::ctrlaltdel:/sbin/reboot
::shutdown:/bin/umount -a -r
::shutdown:/sbin/swapoff -a
- 1
- 2
- 3
- 4
- 5
- 6
賦予可執行權限
chmod +x inittab
- 1
編寫系統初始化命令
從inittab文件中可以看出,首先執行的是/etc/init.d/rcS腳本,因此,我們生成初始化腳本
mkdir init.d cd init.d vim rcS
- 1
- 2
- 3
rcS文件的內容如下所示:
#!/bin/sh mount proc mount -o remount,rw / mount -a clear echo "My Tiny Linux Starting, press enter to active"
- 1
- 2
- 3
- 4
- 5
- 6
- 7
賦予可執行權限
chmod +x rcS
- 1
在rcS腳本中,mount -a 是自動掛載 /etc/fstab 里面的東西,可以理解為掛在文件系統,因此我們還需要編寫 fstab文件來設置我們的文件系統。
cd ramdisk/etc/ vim fstab
- 1
- 2
要掛載臨時文件系統,固定的文件名為fstab, fstab文件內容如下:
#/etc/fstab proc /proc proc defaults 0 0 sysfs /sys sysfs defaults 0 0 devtmpfs /dev devtmpfs defaults 0 0
- 1
- 2
- 3
- 4
至此,我們已經完成了RAM Disk中相關文件的配置,可以壓縮生成文件鏡像了。
cd ramdisk find . -print0 | cpio --null -ov --format=newc | gzip -9 > ../initramfs.img
- 1
- 2
最后生成的initramfs.img就是我們的根文件系統。
4.2 根文件系統的測試
運行如下命令測試根文件系統
qemu-system-x86_64 -kernel ./linux-2.6.32.20/arch/x86_64/boot/bzImage -initrd ./initramfs.img -append "console=ttyS0" -nographic
- 1
測試效果如下:
按"enter"鍵進入用戶界面。
下面對於qemu命令的一個解釋:
qemu-system-x86_64 命令對於你編譯器對應的CPU版本,啟動,也可以制作虛擬磁盤,下文會介紹;
-kernel參數對應編譯好的內核鏡像的位置。
-initrd參數對應的是根文件系統的位置。
-append "console=ttyS0"參數為內核啟動后傳入參數,啟動哪個termnal.
-nographic 無界面啟動,啟動在ubuntu本地終端里面。
- 1
- 2
- 3
- 4
- 5
4.3 啟動總結。
在本章中我們使用了第一章中討論的除了gdb之外的全部文件,這些文件對應了我們在ubuntu14.04中使用編譯好的鏡像和制作好的根文件系統中的技術細節。
下一章重點討論啟動磁盤以及gdb調試問題。
5 gdb調試。
在第四章中我們討論了在根文件系統中啟動內核鏡像的問題,在本章中我們重點介紹GDB調試以及qemu在gdb調試中的一些注意事項問題。
在4.2節中我們掌握了如何啟動內核的問題。其實qemu在gdb調試中只要增加兩個參數就可以:-S -s
兩個參數,總體命令如下:
qemu-system-x86_64 -kernel ./linux-2.6.32.20/arch/x86_64/boot/bzImage -initrd ./initramfs.img -append "console=ttyS0" -nographic
- 1
這時候我們看到的啟動的內核是沒有打印輸出的,是因為需要gdb配合才能夠有打印輸出。我們的gdb該出場了。我們使用如下命令:
gdb vmlinux
### 參數對應的是編譯好的內核根目錄下的vmlinux文件。
- 1
- 2
效果圖如下:
在沒有GDB調試的情況下QEMU帶-S -s參數默認阻塞。
需要啟動gdb,如下:
附圖中我按了兩次continue,是因為我第一次continue之前做了一次break start_kernel操作,如果想一下子啟動到終端界面,可以直接按continue。
如果想打斷點,則需要ctrl +c暫定gdb即可設置斷點。
OK,至此qemu+gdb調試內核代碼全部介紹完畢,下一篇我們會介紹內核塊設備驅動原理,屆時會使用gdb調試內核代碼。敬請期待。
<完>