我是内核开发的新手,我想知道如何使用QEMU和gdb运行/调试linux内核。我实际上正在阅读Robert Love的书,但不幸的是它无法帮助读者如何安装适当的工具来运行或调试内核......所以我所做的就是遵循本教程http://opensourceforu.efytimes.com/2011/02/kernel-development-debugging-using-eclipse/。我正在使用eclipse作为IDE在内核上开发,但我想首先让它在QEMU / gdb下运行。所以我到目前为止所做的是:
1)用:
编译内核make defconfig (then setting the CONFIG_DEBUG_INFO=y in the .config)
make -j4
2)编译完成后,我使用以下命令运行Qemu:
qemu-system-x86_64 -s -S /dev/zero -kernel /arch/x86/boot/bzImage
以“已停止”状态启动内核
3)因此我必须使用gdb,我尝试以下命令:
gdb ./vmlinux
正确运行但是......现在我不知道该怎么办...我知道我必须在端口1234(Qemu使用的默认端口)上使用远程调试,使用vmlinux作为符号用于调试的表文件。
所以我的问题是:我应该怎样做才能在Qemu上运行内核,将调试器附加到它上面,从而让它们协同工作,让内核开发更轻松。
答案 0 :(得分:28)
我试试:
(gdb) target remote localhost:1234
(gdb) continue
使用'-s'选项使qemu侦听端口tcp :: 1234,如果你在同一台机器上,你可以连接到localhost:1234。 Qemu的'-S'选项使Qemu停止执行,直到你给出continue命令。
最好的办法可能是看看一个体面的GDB教程,以便与你正在做的事情相处。 This one看起来很不错。
答案 1 :(得分:21)
在Ubuntu 16.10主机上测试的逐步程序
为了快速入门,我在https://github.com/cirosantilli/linux-kernel-module-cheat/blob/c7bbc6029af7f4fab0a23a380d1607df0b2a3701/gdb-step-debugging.md制作了一个最小的全自动QEMU + Buildroot示例。主要步骤如下所示。
首先获取根文件系统rootfs.cpio.gz
。如果您需要,请考虑:
init
- 仅可执行图片:https://unix.stackexchange.com/questions/122717/custom-linux-distro-that-runs-just-one-program-nothing-else/238579#238579 然后在Linux内核上:
git checkout v4.15
make mrproper
make x86_64_defconfig
cat <<EOF >.config-fragment
CONFIG_DEBUG_INFO=y
CONFIG_DEBUG_KERNEL=y
CONFIG_GDB_SCRIPTS=y
EOF
./scripts/kconfig/merge_config.sh .config .config-fragment
make -j"$(nproc)"
qemu-system-x86_64 -kernel arch/x86/boot/bzImage \
-initrd rootfs.cpio.gz -S -s \
-append nokaslr
在另一个终端上,从Linux内核树内部开始,假设您要从start_kernel
开始调试:
gdb \
-ex "add-auto-load-safe-path $(pwd)" \
-ex "file vmlinux" \
-ex 'set arch i386:x86-64:intel' \
-ex 'target remote localhost:1234' \
-ex 'break start_kernel' \
-ex 'continue' \
-ex 'disconnect' \
-ex 'set arch i386:x86-64' \
-ex 'target remote localhost:1234'
我们完成了!!
对于内核模块,请参阅:How to debug Linux kernel modules with QEMU?
对于Ubuntu 14.04,GDB 7.7.1,hbreak
是必需的,break
软件断点被忽略。 16.10不再是这种情况了。另见:https://bugs.launchpad.net/ubuntu/+source/qemu-kvm/+bug/901944
凌乱的disconnect
以及之后发生的事情是解决错误:
Remote 'g' packet reply is too long: 000000000000000017d11000008ef4810120008000000000fdfb8b07000000000d352828000000004040010000000000903fe081ffffffff883fe081ffffffff00000000000e0000ffffffffffe0ffffffffffff07ffffffffffffffff9fffff17d11000008ef4810000000000800000fffffffff8ffffffffff0000ffffffff2ddbf481ffffffff4600000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007f0300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000801f0000
相关主题:
nokaslr
:https://unix.stackexchange.com/questions/397939/turning-off-kaslr-to-debug-linux-kernel-using-qemu-and-gdb/421287#421287 已知限制:
-O0
:How to de-optimize the Linux kernel to and compile it with -O0? max-completions
修复之后,GDB 7.11也会在某些类型的标签页上完成记忆:Tab completion interrupt for large binaries可能是某个角落的情况,该修补程序中没有涉及。所以ulimit -Sv 500000
在调试之前是一个明智的动作。当我选中file<tab>
filename
sys_execve
import java.util.Scanner;
public class Mari_WS15IsDateEarlier {
public static void main (String[] args) {
//initial variables, asks for variable input and determines month/day/year
Scanner dateInput = new Scanner(System.in);
String answr;
//splits string input into month, day and year
System.out.println("Enter a month, day, and year(separate with a slash) :");
String date1 = dateInput.next();
String[] splitStrings = date1.split("/");
String month1 = splitStrings[0];
String day1 = splitStrings[1];
String year1 = splitStrings[2];
System.out.println("Enter another month, day, and year (separate with a slash) :");
String date2 = dateInput.next();
String[] splitStrings2 = date1.split("/");
String month2 = splitStrings[0];
String day2 = splitStrings[1];
String year2 = splitStrings[2];
//turns string into integer for testing (greater than/less than)
int mn1 = Integer.parseInt(month1);
int mn2 = Integer.parseInt(month2);
int dy1 = Integer.parseInt(day1);
int dy2 = Integer.parseInt(day2);
int yr1 = Integer.parseInt(year1);
int yr2 = Integer.parseInt(year2);
//Determine if the set of variables is a geometric sequence
if (yr1 < yr2) {
answr = "true";
}
else if ((yr1 == yr2)&&(mn1 < mn2)) {
answr = "true";
}
else if ((mn1 == mn2)&&(dy1 < dy2)) {
answr = "true";
}
else if (dy1 == dy2) {
answr = "ERROR: Dates are identical.";
}
else {
answr = "false";
}
//Prints out the answer
System.out.println(answr);
参数时,我会明确地将其展开,如:https://stackoverflow.com/a/42290593/895245 另见:
答案 2 :(得分:3)
BjoernID的答案并不适合我。在第一次继续之后,没有达到断点并且在中断时,我会看到如下行:
0x0000000000000000 in ?? ()
(gdb) break rapl_pmu_init
Breakpoint 1 at 0xffffffff816631e7
(gdb) c
Continuing.
^CRemote 'g' packet reply is too long: 08793000000000002988d582000000002019[..]
我想这与不同的CPU模式有关(当Linux启动时,BIOS中的实模式与长模式相比)。无论如何,解决方案是首先运行QEMU而不等待(即没有-S
):
qemu-system-x86_64 -enable-kvm -kernel arch/x86/boot/bzImage -cpu SandyBridge -s
在我的情况下,我需要在启动过程中打破一些东西,所以在几分钟之后,我运行了gdb命令。如果你有更多的时间(例如你需要调试手动加载的模块),那么时间并不重要。
gdb
允许您指定启动时应运行的命令。这使自动化更容易一些。要连接到QEMU(现在应该已经启动),打破一个函数并继续执行,使用:
gdb -ex 'target remote localhost:1234' -ex 'break rapl_pmu_init' -ex c ./vmlinux
答案 3 :(得分:2)
当你尝试使用gdb启动vmlinux exe时,gdb上的第一件事就是发出cmds:
(gdb)target remote localhost:1234
(gdb)break start_kernel
(继续)
这将在start_kernel中破坏内核。
答案 4 :(得分:1)
对我来说,调试内核的最佳解决方案是使用Eclipse环境中的gdb。您应该在远程调试部分为gdb设置适当的端口(必须与您在qemu启动字符串中指定的端口相同)。这是手册: http://www.sw-at.com/blog/2011/02/11/linux-kernel-development-and-debugging-using-eclipse-cdt/
答案 5 :(得分:0)
在Linux系统上,vmlinux是一个静态链接的可执行文件,其中包含 Linux内核采用Linux支持的一种目标文件格式,其中 包括ELF,COFF和a.out。内核可能需要vmlinux文件 调试,符号表生成或其他操作,但必须进行 在通过添加多重引导用作操作系统内核之前可引导 标头,引导程序和设置例程。
此初始根文件系统的映像必须存储在可访问的位置 由Linux引导程序加载到计算机的引导固件。这可以是 根文件系统本身,光盘上的启动映像,磁盘上的小分区 本地磁盘(引导参数,通常使用ext4或FAT文件系统),或 TFTP服务器(在可以从以太网启动的系统上)。
编译linux内核
应用此系列文章构建内核,并启用 CONFIG_DEBUG_INFO (但请关闭CONFIG_DEBUG_INFO_REDUCED)
安装GDB和Qemu
sudo pacman -S gdb qemu
创建initramfs
#!/bin/bash
# Os : Arch Linux
# Kernel : 5.0.3
INIT_DIR=$(pwd)
BBOX_URL="https://busybox.net/downloads/busybox-1.30.1.tar.bz2"
BBOX_FILENAME=$(basename ${BBOX_URL})
BBOX_DIRNAME=$(basename ${BBOX_FILENAME} ".tar.bz2")
RAM_FILENAME="${INIT_DIR}/initramfs.cpio.gz"
function download_busybox {
wget -c ${BBOX_URL} 2>/dev/null
}
function compile_busybox {
tar xvf ${BBOX_FILENAME} && cd "${INIT_DIR}/${BBOX_DIRNAME}/"
echo "[*] Settings > Build options > Build static binary (no shared libs)"
echo "[!] Please enter to continue"
read tmpvar
make menuconfig && make -j2 && make install
}
function config_busybox {
cd "${INIT_DIR}/${BBOX_DIRNAME}/"
rm -rf initramfs/ && cp -rf _install/ initramfs/
rm -f initramfs/linuxrc
mkdir -p initramfs/{dev,proc,sys}
sudo cp -a /dev/{null,console,tty,tty1,tty2,tty3,tty4} initramfs/dev/
cat > "${INIT_DIR}/${BBOX_DIRNAME}/initramfs/init" << EOF
#!/bin/busybox sh
mount -t proc none /proc
mount -t sysfs none /sys
exec /sbin/init
EOF
chmod a+x initramfs/init
cd "${INIT_DIR}/${BBOX_DIRNAME}/initramfs/"
find . -print0 | cpio --null -ov --format=newc | gzip -9 > "${RAM_FILENAME}"
echo "[*] output: ${RAM_FILENAME}"
}
download_busybox
compile_busybox
config_busybox
使用Qemu启动Linux内核
#!/bin/bash
KER_FILENAME="/home/debug/Projects/kernelbuild/linux-5.0.3/arch/x86/boot/bzImage"
RAM_FILENAME="/home/debug/Projects/kerneldebug/initramfs.cpio.gz"
qemu-system-x86_64 -s -kernel "${KER_FILENAME}" -initrd "${RAM_FILENAME}" -nographic -append "console=ttyS0"
$ ./qemuboot_vmlinux.sh
SeaBIOS (version 1.12.0-20181126_142135-anatol)
iPXE (http://ipxe.org) 00:03.0 C980 PCI2.10 PnP PMM+07F92120+07EF2120 C980
Booting from ROM...
Probing EDD (edd=off to disable)... o
[ 0.019814] Spectre V2 : Spectre mitigation: LFENCE not serializing, switching to generic retpoline
can't run '/etc/init.d/rcS': No such file or directory
Please press Enter to activate this console.
/ # uname -a
Linux archlinux 5.0.3 #2 SMP PREEMPT Mon Mar 25 10:27:13 CST 2019 x86_64 GNU/Linux
/ #
使用GDB调试Linux内核
~/Projects/kernelbuild/linux-5.0.3 ➭ gdb vmlinux
...
(gdb) target remote localhost:1234
Remote debugging using localhost:1234
0xffffffff89a4b852 in ?? ()
(gdb) break start_kernel
Breakpoint 1 at 0xffffffff826ccc08
(gdb)
Display all 190 possibilities? (y or n)
(gdb) info functions
All defined functions:
Non-debugging symbols:
0xffffffff81000000 _stext
0xffffffff81000000 _text
0xffffffff81000000 startup_64
0xffffffff81000030 secondary_startup_64
0xffffffff810000e0 verify_cpu
0xffffffff810001e0 start_cpu0
0xffffffff810001f0 __startup_64
0xffffffff81000410 pvh_start_xen
0xffffffff81001000 hypercall_page
0xffffffff81001000 xen_hypercall_set_trap_table
0xffffffff81001020 xen_hypercall_mmu_update
0xffffffff81001040 xen_hypercall_set_gdt
0xffffffff81001060 xen_hypercall_stack_switch
0xffffffff81001080 xen_hypercall_set_callbacks
0xffffffff810010a0 xen_hypercall_fpu_taskswitch
0xffffffff810010c0 xen_hypercall_sched_op_compat
0xffffffff810010e0 xen_hypercall_platform_op