GDB調(diào)試CentOS內(nèi)核

通過(guò)GDB和QEMU調(diào)試Linux內(nèi)核已經(jīng)有很多介紹了,但基本都是制作簡(jiǎn)單的根文件系統(tǒng)。有時(shí)候需要調(diào)試的模塊或者場(chǎng)景需要用到發(fā)行版的Linux,因此本文介紹調(diào)試CentOS內(nèi)核的步驟。

GDB調(diào)試Linux內(nèi)核關(guān)鍵步驟

debug_kernel.jpg
  1. 首先需要有一個(gè)可以啟動(dòng)運(yùn)行Cent OS 7的虛擬機(jī)
    • 準(zhǔn)備一個(gè)qcow2格式的磁盤(pán)鏡像文件centos.img.qcow2,通過(guò)CentOS的光盤(pán)iso將系統(tǒng)安裝到這個(gè)磁盤(pán)鏡像中。
  2. 虛擬機(jī)需要連接外部網(wǎng)絡(luò)(例如mount nfs文件系統(tǒng)、調(diào)試內(nèi)核的網(wǎng)絡(luò)代碼等),所以我們要為QEMU虛擬機(jī)選擇合適的網(wǎng)絡(luò)方案
    • 通過(guò)將tap和物理網(wǎng)卡加入到bridge
  3. GDB的調(diào)試需要源代碼和編譯生成的原始文件vmlinux,因此我們要下載Linux內(nèi)核源代碼,完成patch、configure和編譯等工作;
  4. 編譯Linux內(nèi)核的最終產(chǎn)品為bzImage,因此要能夠更新虛擬機(jī)中的內(nèi)核bzImage以及GRUB配置;
  5. QEMU內(nèi)置一個(gè)gdbserver,通過(guò)”-s -S”選項(xiàng)來(lái)開(kāi)啟并等待GDB的連接,默認(rèn)端口為1234
    • -S freeze CPU at startup (use 'c' to start execution)
    • -s shorthand for -gdb tcp::1234
    • 也可以在QEMU命令行中通過(guò)gdbserver tcp::1234來(lái)指定gdbserver的端口號(hào)

安裝CentOS 7.1到虛擬磁盤(pán)

從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
  • 通過(guò)VNC View連接ip:5901進(jìn)入虛擬機(jī)的控制臺(tái),在控制臺(tái)中完成CentOS 7.1的安裝過(guò)程
  • 安裝完成后得到虛擬機(jī)的磁盤(pán)鏡像centos.img.qcow2
  • 重新啟動(dòng)QEMU虛擬機(jī):
qemu-system-x86_64 -m 4096 -vnc 0.0.0.0:1 centos.img.qcow2

通過(guò)VNC Viewer連接到虛擬機(jī)的控制臺(tái),確認(rèn)CentOS 7正常啟動(dòng)

VNC

QEMU虛擬機(jī)網(wǎng)絡(luò)配置

QEMU支持多種方式的網(wǎng)絡(luò)配置,對(duì)于需要訪問(wèn)外部網(wǎng)絡(luò)的虛擬機(jī),最合適的方式是tap橋接模式,即將虛擬機(jī)網(wǎng)卡對(duì)應(yīng)的tap網(wǎng)卡和Host的物理網(wǎng)卡加入到同一個(gè)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)建虛擬機(jī)時(shí)指定-net nic -net tap,script=/etc/qemu-ifup1,其中qemu-ifup1由QEMU初始化虛擬機(jī)網(wǎng)卡時(shí)調(diào)用,它負(fù)責(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ī)啟動(dòng)之后可以看到其已經(jīng)通過(guò)DHCP的方式自動(dòng)獲取到ip

編譯Linux內(nèi)核源代碼

下載Linux內(nèi)核的源代碼可以在Linux內(nèi)核網(wǎng)站上下載“干凈”的版本,但是需要自己來(lái)配置內(nèi)核,更簡(jiǎn)單的辦法是在CentOS網(wǎng)站上下載對(duì)應(yīng)版本的rpm源碼包,這樣不需要關(guān)心patch和內(nèi)核配置,比如CentOS 7.1對(duì)應(yīng)的源碼包kernel-3.10.0-229.1.2.el7.src.rpm

  • 安裝源碼包
rpm –ivh kernel-3.10.0-229.1.2.el7.src.rpm
  • 通過(guò)rpmbuild -bp完成源碼的patch和config工作(-bp表示prepare)
rpmbuild -bp ~/rpmbuild/SPECS/kernel.spec

完成之后我們就得到了一套準(zhǔn)備好的待編譯的源代碼,此時(shí)完全可以將這一套源代碼打包拷貝到別處重復(fù)使用。rpmbuild -bb可以完成代碼的prepare和編譯打包的全部工作,直接生成新內(nèi)核的rpm安裝包。

  • 進(jìn)入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安裝到虛擬機(jī)中去,一種方式是制作rpm包然后拷貝到虛擬機(jī)中去安裝:

make binrpm-pkg INSTALL_MOD_STRIP=1

另外一種方式:在host中將內(nèi)核源碼通過(guò)nfs共享給虛擬機(jī),由虛擬機(jī)自己來(lái)完成install的工作

  1. 進(jìn)入源碼目錄,修改代碼后編譯
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)核時(shí)需要)
  1. QEMU啟動(dòng)虛擬機(jī)
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

*注意此時(shí)不需要’-s -S’選項(xiàng),同時(shí)可以新增’-enable-kvm’和’-smp 2’來(lái)提高虛擬機(jī)的性能;

  1. 在虛擬機(jī)中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/
  1. 進(jìn)入源碼目錄安裝新編譯的內(nèi)核
make modules_install INSTALL_MOD_STRIP=1    (第一次編譯安裝新內(nèi)核時(shí)需要)
make install

安裝成功后重啟虛擬機(jī)后通過(guò)uname -a確認(rèn)內(nèi)核更新成功

開(kāi)始調(diào)試內(nèi)核

  1. 檢查網(wǎng)絡(luò)配置
  2. 在會(huì)話1中通過(guò)QEMU創(chuàng)建虛擬機(jī)
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

  1. 會(huì)話2中進(jìn)入源碼目錄并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
  1. 在gdb中連接QEMU的gdbserver并設(shè)置斷點(diǎn)
(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.

此時(shí)通過(guò)VNC Viewer連接到虛擬機(jī)的控制臺(tái),應(yīng)該可以看到啟動(dòng)的輸出

  1. 在虛擬機(jī)中執(zhí)行調(diào)試代碼的操作,然后會(huì)話2中會(huì)被break住,此時(shí)像普通程序一樣進(jìn)行g(shù)db調(diào)試;

附一:gdb編譯

GDB在調(diào)試64位Linux內(nèi)核時(shí)會(huì)出錯(cuò):

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,而是需要通過(guò)libvirt的接口來(lái)創(chuàng)建虛擬機(jī),因此要使用QEMU推薦下載QEMU源代碼編譯安裝,編譯安裝也很簡(jiǎn)單:

./configure --target-list=x86_64-softmmu
make && make install

附三:虛擬機(jī)組網(wǎng)

橋接

上文介紹中QEMU虛擬機(jī)是通過(guò)橋接方式連接外部網(wǎng)絡(luò),其將虛擬機(jī)對(duì)應(yīng)的tap網(wǎng)卡和物理網(wǎng)卡加入到同一個(gè)bridge中,將物理網(wǎng)卡的ip分配到bridge上,這樣從邏輯上看虛擬機(jī)的報(bào)文都是通過(guò)bridge進(jìn)出

Bridge

橋接方式的好處是每個(gè)VM都可以獲取到物理機(jī)同網(wǎng)段的ip,因此也可以訪問(wèn)物理機(jī)所屬的網(wǎng)絡(luò)

NAT

有時(shí)虛擬機(jī)不需要訪問(wèn)物理機(jī)所屬的網(wǎng)絡(luò)(比如受ip沖突影響),虛擬機(jī)只需要能夠互相訪問(wèn)同時(shí)可以訪問(wèn)外網(wǎng),這時(shí)NAT方式是比較適合的。其將所有VM對(duì)應(yīng)的tap網(wǎng)卡加入到同一個(gè)bridge中,這個(gè)bridge作為這個(gè)私有網(wǎng)段的網(wǎng)關(guān),每個(gè)VM啟動(dòng)后配置一個(gè)同網(wǎng)段的ip;然后物理網(wǎng)卡為這個(gè)bridge做NAT。

NAT

配置方式如下:

  1. 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
  1. 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都需要配置成固定的。如果要實(shí)現(xiàn)VM自動(dòng)分配私有ip那么還需要在host上安裝一個(gè)本地的DHCP Server

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容