利用QEMU+GDB搭建Linux內核調試環境

      網友投稿 1052 2022-05-28

      前言

      對用戶態進程,利用gdb調試代碼是很方便的手段。而對于內核態的問題,可以利用crash等工具基于coredump文件進行調試。

      其實我們也可以利用一些手段對Linux內核代碼進行gdb調試,qemu就是一種。

      qemu是一款完全軟件模擬(Binary translation)的虛擬化軟件,在虛擬化的實現中性能相對較差。但利用它在測試環境中gdb調試Linux內核代碼,是熟悉Linux內核代碼的一個好方法。

      本文實驗環境:

      ubuntu 20.04

      busybox-1.32.1

      Linux kernel 4.9.3

      QEMU

      GDB 10.1

      編譯內核源碼

      git clone git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git tar -xvzf linux-4.9.301.tar.gz cd linux-4.9.301 make menuconfig

      在內核編譯選項中,開啟如下"Compile the kernel with debug info"

      Kernel hacking ---> Compile-time checks and compiler options ---> [ ] Compile the kernel with debug info

      示意圖如下,利用鍵盤選中debug選項,然后敲"Y"勾選:

      以上配置完成后會在當前目錄生成 .config 文件,我們可以使用 grep 進行驗證:

      grep CONFIG_DEBUG_INFO .config CONFIG_DEBUG_INFO=y

      編譯內核

      make bzimage -j4

      編譯完成后,會在當前目錄下生成vmlinux,這個在 gdb 的時候需要加載,用于讀取 symbol 符號信息,包含了所有調試信息,所以比較大。

      壓縮后的鏡像文件為bzImage, 在arch/x86/boot/目錄下。

      ? linux-4.9.301 ls -hl vmlinux -rwxrwxr-x 1 ubuntu ubuntu 578M Apr 15 08:14 vmlinux ? linux-4.9.301 ls -hl ./arch/x86_64/boot/bzImage lrwxrwxrwx 1 ubuntu ubuntu 22 Apr 15 08:15 ./arch/x86_64/boot/bzImage -> ../../x86/boot/bzImage ? linux-4.9.301 ls -hl ./arch/x86/boot/bzImage -rw-rw-r-- 1 ubuntu ubuntu 9.3M Apr 15 08:15 ./arch/x86/boot/bzImage

      幾種linux內核文件的區別:

      vmlinux 編譯出來的最原始的內核文件,未壓縮。

      利用QEMU+GDB搭建Linux內核調試環境

      zImage 是vmlinux經過gzip壓縮后的文件。

      bzImage bz表示“big zImage”,不是用bzip2壓縮的。兩者的不同之處在于,zImage解壓縮內核到低端內存(第一個640K)。

      bzImage解壓縮內核到高端內 存(1M以上)。如果內核比較小,那么采用zImage或bzImage都行,如果比較大應該用bzImage。

      uImage U-boot專用的映像文件,它是在zImage之前加上一個長度為0x40的tag。

      vmlinuz 是bzImage/zImage文件的拷貝或指向bzImage/zImage的鏈接。

      initrd 是“initial ramdisk”的簡寫。一般被用來臨時的引導硬件到實際內核vmlinuz能夠接管并繼續引導的狀態。

      編譯busybox

      Linux系統啟動階段,boot loader加載完內核文件vmlinuz后,內核緊接著需要掛載磁盤根文件系統,但如果此時內核沒有相應驅動,無法識別磁盤,就需要先加載驅動。

      而驅動又位于/lib/modules,得掛載根文件系統才能讀取,這就陷入了一個兩難境地,系統無法順利啟動。

      于是有了initramfs根文件系統,其中包含必要的設備驅動和工具,bootloader加載initramfs到內存中,內核會將其掛載到根目錄/,然后運行/init腳本,掛載真正的磁盤根文件系統。

      這里借助BusyBox構建極簡initramfs,提供基本的用戶態可執行程序。

      可以從busybox官網地址下載最新版本,或者直接使用wget下載我使用的版本。

      wget https://busybox.net/downloads/busybox-1.32.1.tar.bz2 $ tar -xvf busybox-1.32.1.tar.bz2 $ cd busybox-1.32.1/ $ make menuconfig

      在編譯busybox之前,我們需要對其進行設置,執行make menuconfig,如下

      這里一定要選擇靜態編譯,編譯好的可執行文件busybox不依賴動態鏈接庫,可以獨立運行,方便構建initramfs。

      之后選擇Exit退出,到這里我們就可以編譯busybox了,執行下面的命令

      make -j 8 # 安裝完成后生成的相關文件會在 _install 目錄下 make && make install

      構建initramfs根文件系統

      [root@localhost temp]# ls busybox-1.29.0 busybox-1.29.0.tar.bz2 [root@localhost temp]# mkdir initramfs [root@localhost temp]# cd initramfs [root@localhost initramfs]# cp ../busybox-1.29.0/_install/* -rf ./ [root@localhost initramfs]# mkdir dev proc sys [root@localhost initramfs]# sudo cp -a /dev/{null,console,tty,tty1,tty2,tty3,tty4} dev/ [root@localhost initramfs]# rm -f linuxrc [root@localhost initramfs]# vim init [root@localhost initramfs]# chmod a+x init [root@localhost initramfs]# ls bin dev init proc sbin sys usr

      其中init的內容如下

      #!/bin/busybox sh echo "{==DBG==} INIT SCRIPT" mount -t proc none /proc mount -t sysfs none /sys echo -e "{==DBG==} Boot took $(cut -d' ' -f1 /proc/uptime) seconds" exec /sbin/init

      打包initramfs

      find . -print0 | cpio --null -ov --format=newc | gzip -9 > ../initramfs.cpio.gz [root@localhost initramfs]# ls ../ busybox-1.29.0 busybox-1.29.0.tar.bz2 initramfs initramfs.cpio.gz

      安裝QEMU

      apt install qemu qemu-utils qemu-kvm virt-manager libvirt-daemon-system libvirt-clients bridge-utils

      安裝GDB

      wget https://ftp.gnu.org/gnu/gdb/gdb-10.1.tar.gz tar -xzvf gdb-10.1.tar.gz cd gdb-10.1 ./configure # 必需要安裝這兩個庫 sudo apt-get install texinfo sudo apt-get install build-essential make -j 8 sudo make install

      QEMU啟動調試內核

      ? linux-4.9.301 qemu-system-x86_64 -kernel ./arch/x86/boot/bzImage -initrd ../initramfs.cpio.gz -append "nokaslr console=ttyS0" -s -S -nographic

      -kernel ./arch/x86/boot/bzImage:指定啟用的內核鏡像;

      -initrd ../initramfs.cpio.gz:指定啟動的內存文件系統;

      -append "nokaslr console=ttyS0" :附加參數,其中 nokaslr 參數必須添加進來,防止內核起始地址隨機化,這樣會導致 gdb 斷點不能命中;

      -s :監聽在 gdb 1234 端口;

      -S :表示啟動后就掛起,等待 gdb 連接;

      -nographic:不啟動圖形界面,調試信息輸出到終端與參數 console=ttyS0 組合使用;

      在另一個窗口中,輸入gdb,即可開啟調試。

      (gdb) target remote localhost:1234 Remote debugging using localhost:1234 warning: Can not parse XML target description; XML support was disabled at compile time Remote 'g' packet reply is too long (expected 560 bytes, got 608 bytes): 0000000000000000000000000000000000000000000000006306000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f0ff0000000000000200000000f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000060000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007f0300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000801f0000 (gdb) Remote debugging using localhost:1234 Undefined command: "Remote". Try "help". (gdb) warning: Can not parse XML target description; XML support was disabled at compile timeQuit

      但是,在啟動GDP調試時報錯了,在查閱了諸多資料后,很多博客都給出了修復方法:源碼重新安裝gdb,并修改gdb/remote.c文件的一段代碼。但是我嘗試了,發現行不通。

      出現該問題的原因是:編譯 的是64 位模式的內核代碼,但是運行是在 32 位保護模式下。64 位代碼將無法在該環境中正常運行。

      終于在stackflow上找到了修復方法:具體可以參考下面兩篇文章。

      https://stackoverflow.com/questions/48620622/how-to-solve-qemu-gdb-debug-error-remote-g-packet-reply-is-too-long

      https://wiki.osdev.org/QEMU_and_GDB_in_long_mode

      文章中給出了三種修復方法,我這里只列出了一種,即修改GDB源碼,重新編譯安裝。

      --- gdb/remote.c 2016-04-14 11:13:49.962628700 +0300 +++ gdb/remote.c 2016-04-14 11:15:38.257783400 +0300 @@ -7181,8 +7181,28 @@ buf_len = strlen (rs->buf); /* Further sanity checks, with knowledge of the architecture. */ +// HACKFIX for changing architectures for qemu. It's ugly. Don't use, unless you have to. + // Just a tiny modification of the patch of Matias Vara (http://forum.osdev.org/viewtopic.php?f=13&p=177644) if (buf_len > 2 * rsa->sizeof_g_packet) - error (_("Remote 'g' packet reply is too long: %s"), rs->buf); + { + warning (_("Assuming long-mode change. [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[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; + } + + // HACKFIX: Make sure at least the lower half of EIP is set correctly, so the proper + // breakpoint is recognized (and triggered). + rsa->regs[8].offset = 16*8; + } /* 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

      cd gdb-10.1 ./configure make -j 8 sudo make install

      接著就可以敲gdb 啟動調試。

      ? linux-4.9.301 gdb GNU gdb (GDB) 10.1 Copyright (C) 2020 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later 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-pc-linux-gnu". Type "show configuration" for configuration details. For bug reporting instructions, please see: . Find the GDB manual and other documentation resources online at: . For help, type "help". Type "apropos word" to search for commands related to "word". (gdb) file vmlinux Reading symbols from vmlinux... (gdb) target remote localhost:1234 Remote debugging using localhost:1234 warning: Can not parse XML target description; XML support was disabled at compile time warning: Assuming long-mode change. [Remote 'g' packet reply is too long: PU] 0x000000000000fff0 in exception_stacks () (gdb) break start_kernel Breakpoint 1 at 0xffffffff81fc6a95: file init/main.c, line 486. (gdb) break rest_init Breakpoint 2 at 0xffffffff818aa1e1: file init/main.c, line 385. (gdb) c Continuing. Breakpoint 1, start_kernel () at init/main.c:486 486 set_task_stack_end_magic(&init_task); (gdb) c Continuing. Breakpoint 2, rest_init () at init/main.c:385 385 { (gdb)

      在start_kernel 和 rest_init 打了兩個斷點, 兩個斷點都成功命中了。

      本文參考

      https://www.shuzhiduo.com/A/kjdw2a2q5N/

      https://cloud.tencent.com/developer/article/1793157

      https://blog.csdn.net/alexanderwang7/article/details/113180447

      https://blog.csdn.net/sjc2870/article/details/122017247

      https://stackoverflow.com/questions/8662468/remote-g-packet-reply-is-too-long

      https://stackoverflow.com/questions/4943857/linux-kernel-live-debugging-how-its-done-and-what-tools-are-used/42316607#42316607

      ARM C 語言 Linux 嵌入式 虛擬化

      版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。

      上一篇:用戶代碼和操作系統代碼是如何在CPU上面運行的(用戶態和內核態)
      下一篇:采用CDM進行HDFS到OBS的數據遷移
      相關文章
      亚洲老熟女五十路老熟女bbw| 婷婷亚洲综合五月天小说在线| 亚洲毛片一级带毛片基地| 亚洲AV女人18毛片水真多| 国产成人精品日本亚洲网址 | 豆国产96在线|亚洲| 18亚洲男同志videos网站| 亚洲av不卡一区二区三区| 日本亚洲成高清一区二区三区| 亚洲精品第一国产综合精品99| 天天综合亚洲色在线精品| 成人婷婷网色偷偷亚洲男人的天堂 | 精品亚洲一区二区三区在线观看| 亚洲毛片av日韩av无码| 亚洲精品国产自在久久| 亚洲一区二区三区在线视频 | 亚洲一区综合在线播放| 亚洲VA中文字幕不卡无码| 国产成人综合亚洲AV第一页 | 国产亚洲人成网站在线观看不卡| 亚洲精品制服丝袜四区| 亚洲av中文无码乱人伦在线咪咕 | 亚洲av无码乱码国产精品fc2| 亚洲电影一区二区三区| 久久久久久亚洲AV无码专区| 亚洲美女视频一区| 亚洲va成无码人在线观看| 国产精品亚洲片在线va| 亚洲AV综合色区无码一二三区| 亚洲а∨天堂久久精品| 丁香五月亚洲综合深深爱| 亚洲精品无码久久千人斩| 久久久亚洲欧洲日产国码农村| 亚洲精品福利在线观看| 亚洲一级毛片在线播放| 亚洲a∨无码一区二区| 亚洲精品无码久久不卡| 亚洲精品你懂的在线观看| 久久久久亚洲精品无码蜜桃| 亚洲国产夜色在线观看| 亚洲av片在线观看|