通過GDB和QEMU調(diào)試Linux內(nèi)核已經(jīng)有很多介紹了笨蚁,但基本都是制作簡單的根文件系統(tǒng)凶朗。有時候需要調(diào)試的模塊或者場景需要用到發(fā)行版的Linux致稀,因此本文介紹調(diào)試CentOS內(nèi)核的步驟。
GDB調(diào)試Linux內(nèi)核關(guān)鍵步驟
- 首先需要有一個可以啟動運行Cent OS 7的虛擬機
- 準備一個qcow2格式的磁盤鏡像文件centos.img.qcow2俱尼,通過CentOS的光盤iso將系統(tǒng)安裝到這個磁盤鏡像中。
- 虛擬機需要連接外部網(wǎng)絡(luò)(例如mount nfs文件系統(tǒng)萎攒、調(diào)試內(nèi)核的網(wǎng)絡(luò)代碼等)遇八,所以我們要為QEMU虛擬機選擇合適的網(wǎng)絡(luò)方案
- 通過將tap和物理網(wǎng)卡加入到bridge
- GDB的調(diào)試需要源代碼和編譯生成的原始文件vmlinux,因此我們要下載Linux內(nèi)核源代碼耍休,完成patch刃永、configure和編譯等工作;
- 編譯Linux內(nèi)核的最終產(chǎn)品為bzImage,因此要能夠更新虛擬機中的內(nèi)核bzImage以及GRUB配置羊精;
- QEMU內(nèi)置一個gdbserver斯够,通過”-s -S”選項來開啟并等待GDB的連接,默認端口為1234
- -S freeze CPU at startup (use 'c' to start execution)
- -s shorthand for -gdb tcp::1234
- 也可以在QEMU命令行中通過gdbserver tcp::1234來指定gdbserver的端口號
安裝CentOS 7.1到虛擬磁盤
從CentOS網(wǎng)站上下載CentOS 7的安裝包CentOS-7-x86_64-DVD-1503-01.iso
qemu-img create centos.img.qcow2 -f qcow2 40G
qemu-system-x86_64 -cdrom CentOS-7-x86_64-DVD-1503-01.iso -hda centos.img.qcow2 -boot d -net nic -net user -m 4096 -vnc 0.0.0.0:1 -enable-kvm -smp 2
- 通過VNC View連接ip:5901進入虛擬機的控制臺喧锦,在控制臺中完成CentOS 7.1的安裝過程
- 安裝完成后得到虛擬機的磁盤鏡像centos.img.qcow2
- 重新啟動QEMU虛擬機:
qemu-system-x86_64 -m 4096 -vnc 0.0.0.0:1 centos.img.qcow2
通過VNC Viewer連接到虛擬機的控制臺读规,確認CentOS 7正常啟動
QEMU虛擬機網(wǎng)絡(luò)配置
QEMU支持多種方式的網(wǎng)絡(luò)配置,對于需要訪問外部網(wǎng)絡(luò)的虛擬機燃少,最合適的方式是tap橋接模式束亏,即將虛擬機網(wǎng)卡對應(yīng)的tap網(wǎng)卡和Host的物理網(wǎng)卡加入到同一個bridge中
- 創(chuàng)建bridge并將物理網(wǎng)卡加入到該bridge中
/home/gj/virt/qemu-kernel/net_config.sh
#!/bin/sh
ifconfig enp2s0 0.0.0.0
brctl addbr br0
brctl addif br0 enp2s0
dhclient br0
- QEMU創(chuàng)建虛擬機時指定-net nic -net tap,script=/etc/qemu-ifup1,其中qemu-ifup1由QEMU初始化虛擬機網(wǎng)卡時調(diào)用阵具,它負責(zé)將tap網(wǎng)卡加入到bridge中
/etc/qemu-ifup1
#! /bin/sh
# TAP interface will be passed in $1
ifconfig $1 up
brctl addif br0 $1
虛擬機啟動之后可以看到其已經(jīng)通過DHCP的方式自動獲取到ip
編譯Linux內(nèi)核源代碼
下載Linux內(nèi)核的源代碼可以在Linux內(nèi)核網(wǎng)站上下載“干凈”的版本碍遍,但是需要自己來配置內(nèi)核定铜,更簡單的辦法是在CentOS網(wǎng)站上下載對應(yīng)版本的rpm源碼包,這樣不需要關(guān)心patch和內(nèi)核配置怕敬,比如CentOS 7.1對應(yīng)的源碼包kernel-3.10.0-229.1.2.el7.src.rpm
- 安裝源碼包
rpm –ivh kernel-3.10.0-229.1.2.el7.src.rpm
- 通過rpmbuild -bp完成源碼的patch和config工作(-bp表示prepare)
rpmbuild -bp ~/rpmbuild/SPECS/kernel.spec
完成之后我們就得到了一套準備好的待編譯的源代碼揣炕,此時完全可以將這一套源代碼打包拷貝到別處重復(fù)使用。rpmbuild -bb可以完成代碼的prepare和編譯打包的全部工作东跪,直接生成新內(nèi)核的rpm安裝包畸陡。
- 進入Linux內(nèi)核源碼目錄
cd ~/rpmbuild/BUILD/kernel-3.10.0-229.1.2.el7/linux-3.10.0-229.1.2.el7.centos.x86_64/
make bzImage && make modules
生成gdb需要的vmlinux和待安裝的新內(nèi)核bzImage(arch/x86_64/boot/bzImage)
安裝新內(nèi)核
編譯完成需要將生成的內(nèi)核(bzImage)和modules安裝到虛擬機中去,一種方式是制作rpm包然后拷貝到虛擬機中去安裝:
make binrpm-pkg INSTALL_MOD_STRIP=1
另外一種方式:在host中將內(nèi)核源碼通過nfs共享給虛擬機越庇,由虛擬機自己來完成install的工作
- 進入源碼目錄罩锐,修改代碼后編譯
cd ~/rpmbuild/BUILD/kernel-3.10.0-229.1.2.el7/linux-3.10.0-229.1.2.el7.centos.x86_64
make bzImage
make modules (第一次編譯安裝新內(nèi)核時需要)
- QEMU啟動虛擬機
qemu-system-x86_64 -m 4096 -vnc 0.0.0.0:1 centos.img.qcow2 -net nic -net tap,script=/etc/qemu-ifup1 -enable-kvm -smp 2
*注意此時不需要’-s -S’選項,同時可以新增’-enable-kvm’和’-smp 2’來提高虛擬機的性能卤唉;
- 在虛擬機中mount Host導(dǎo)出的源碼目錄
showmount -e 192.168.10.115
Export list for 192.168.10.115:
/root/rpmbuild/BUILD 192.168.10.0/24
mount -t nfs 192.168.10.115:/root/rpmbuild/BUILD /mnt/nfs/
- 進入源碼目錄安裝新編譯的內(nèi)核
make modules_install INSTALL_MOD_STRIP=1 (第一次編譯安裝新內(nèi)核時需要)
make install
安裝成功后重啟虛擬機后通過uname -a確認內(nèi)核更新成功
開始調(diào)試內(nèi)核
- 檢查網(wǎng)絡(luò)配置
- 在會話1中通過QEMU創(chuàng)建虛擬機
cd /home/gj/virt/qemu-kernel
qemu-system-x86_64 -m 4096 -vnc 0.0.0.0:1 centos.img.qcow2 -net nic -net tap,script=/etc/qemu-ifup1 -s -S
使用VNC viewer連接ip:5901
- 會話2中進入源碼目錄并gdb
cd ~/rpmbuild/BUILD/kernel-3.10.0-229.1.2.el7/linux-3.10.0-229.1.2.el7.centos.x86_64
gdb vmlinux
- 在gdb中連接QEMU的gdbserver并設(shè)置斷點
(gdb) target remote localhost:1234
Remote debugging using localhost:1234
0x0000000000000000 in irq_stack_union ()
(gdb) break yfs_mount
Breakpoint 1 at 0xffffffff812b259f: file fs/yfs/super.c, line 777.
(gdb) c
Continuing.
此時通過VNC Viewer連接到虛擬機的控制臺涩惑,應(yīng)該可以看到啟動的輸出
- 在虛擬機中執(zhí)行調(diào)試代碼的操作,然后會話2中會被break住桑驱,此時像普通程序一樣進行g(shù)db調(diào)試竭恬;
附一:gdb編譯
GDB在調(diào)試64位Linux內(nèi)核時會出錯:
Remote ‘g’ packet reply is too long
解決辦法是下載GDB的源代碼,如下所示修改remote.c
if (buf_len > 2 * 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);
rsa->sizeof_g_packet = buf_len ;
for (i = 0; i < gdbarch_num_regs (gdbarch); i++) {
if (rsa->regs->pnum == -1)
continue;
if (rsa->regs->offset >= rsa->sizeof_g_packet)
rsa->regs->in_g_packet = 0;
else
rsa->regs->in_g_packet = 1;
}
}
重新編譯并安裝GDB
./configure
make && make install
附二:qemu編譯
Redhat/CentOS不推薦直接使用qemu-kvm熬的,而是需要通過libvirt的接口來創(chuàng)建虛擬機痊硕,因此要使用QEMU推薦下載QEMU源代碼編譯安裝,編譯安裝也很簡單:
./configure --target-list=x86_64-softmmu
make && make install
附三:虛擬機組網(wǎng)
橋接
上文介紹中QEMU虛擬機是通過橋接方式連接外部網(wǎng)絡(luò)押框,其將虛擬機對應(yīng)的tap網(wǎng)卡和物理網(wǎng)卡加入到同一個bridge中岔绸,將物理網(wǎng)卡的ip分配到bridge上,這樣從邏輯上看虛擬機的報文都是通過bridge進出
橋接方式的好處是每個VM都可以獲取到物理機同網(wǎng)段的ip,因此也可以訪問物理機所屬的網(wǎng)絡(luò)
NAT
有時虛擬機不需要訪問物理機所屬的網(wǎng)絡(luò)(比如受ip沖突影響)橡伞,虛擬機只需要能夠互相訪問同時可以訪問外網(wǎng)盒揉,這時NAT方式是比較適合的。其將所有VM對應(yīng)的tap網(wǎng)卡加入到同一個bridge中兑徘,這個bridge作為這個私有網(wǎng)段的網(wǎng)關(guān)刚盈,每個VM啟動后配置一個同網(wǎng)段的ip;然后物理網(wǎng)卡為這個bridge做NAT挂脑。
配置方式如下:
- Host網(wǎng)絡(luò)初始化腳本
#!/bin/sh
# create bridge
brctl addbr br0-qemu
ifconfig br0-qemu 192.168.125.1
iptables -t nat -A POSTROUTING -o enp2s0 -j MASQUERADE
iptables -A FORWARD -i enp2s0 -o br0-qemu -m state --state RELATED,ESTABLISHED -j ACCEPT
iptables -A FORWARD -i br0-qemu -o enp2s0 -j ACCEPT
echo 1 > /proc/sys/net/ipv4/ip_forward
- qemu初始化腳本 /etc/qemu-ifup-nat
#! /bin/sh
# TAP interface will be passed in $1
ifconfig $1 promisc up
brctl addif br0 $1
上述方式中所有VM的ip, gateway和DNS都需要配置成固定的藕漱。如果要實現(xiàn)VM自動分配私有ip那么還需要在host上安裝一個本地的DHCP Server