1. 目标
核心目标为解决特定硬件及特定内核版本下QEMU启动问题。
前段时间我们通过“在aarch64环境下编译kvmtool,基于kvmtool启动最小linux”来验证aarch64架构下虚拟化支持问题,发现在一环境下,出现CPU stuck问题,直接导致裸机卡住,详细信息如下:
我们需要进一步定位问题所在,故产生了通过GDB去调试启动过程,由于kvmtool调试手段有限,下面我们采用qemu来完成该项任务。
2. qemu编译
2.1 下载qemu
通过网址https://download.qemu.org/进行相应版本选择下载,我们选择比较新的版本,6.0.0进行下载
wget https://download.qemu.org/qemu-6.0.0.tar.bz2
tar -xvf qemu-6.0.0.tar.bz2
cd qemu-6.0.0/
2.2 依赖下载
软件依赖包可以通过网站https://pkgs.org/搜索下载
wget https://vault.centos.org/centos/8/PowerTools/aarch64/os/Packages/re2c-0.14.3-2.el8.aarch64.rpm
wget https://vault.centos.org/centos/8/PowerTools/aarch64/os/Packages/ninja-build-1.8.2-1.el8.aarch64.rpm
rpm -ivh re2c-0.14.3-2.el8.aarch64.rpm
rpm -ivh ninja-build-1.8.2-1.el8.aarch64.rpm
2.3 编译
添加kvm和debug选项,仅编译需要的aarch64架构的以节约编译时间,如下所示:
mkdir build && cd build
../configure --enable-kvm --target-list=aarch64-softmmu --enable-debug
make -j64
3. 内核及文件系统编译制作
在配置菜单中,启用内核debug,关闭地址随机化,不然断点处无法停止
-> Kernel hacking
-> [*] Kernel debugging
-> Kernel hacking
-> Compile-time checks and compiler options
-> [*] Compile the kernel with debug info
-> [*] Provide GDB scripts for kernel debugging
-> Kernel Features
-> [ ] Randomize the address of the kernel image
内核及文件系统编译制作具体可以参考https://blog.csdn.net/nanhai_happy/article/details/124835581
4. 调试
4.1 GDB知识回顾
GDB 全称“GNU symbolic debugger”,主要功能就是监控程序的执行流程。
常用调试命令如下:
调试命令 | 作用 |
---|---|
target remote <hostname>:<port> | 连接到主机名:端口 |
target remote :<port> | 如果没有指定主机名,GDB将使用localhost |
break (b) <location> | 在源代码指定的某一行设置断点 |
hbreak <location> | 在源代码指定的某一行设置硬件断点 |
info break | 输出GDB会话中所有断点的信息 |
enable <breakpoint-number> | 启用断点号断点 |
disable <breakpoint-number> | 禁用断点号断点 |
delete(d) <breakpoint-number> | 以断点号删除断点。 |
info registers | 打印出CPU的寄存器 |
run (r) | 执行被调试的程序,其会自动在第一个断点处暂停执行 |
run (r) | 执行被调试的程序,其会自动在第一个断点处暂停执行 |
continue (c) | 当程序在某一断点处停止后,用该指令可以继续执行,直至遇到断点或者程序结束 |
next (n) | 令程序一行代码一行代码的执行 |
step(s) | 如果有调用函数,进入调用的函数内部;否则,和 next 命令的功能一样 |
print (p)<var> | 打印指定变量的值 |
set <var>=<val> | 将val值赋给变量var |
list (l) | 显示源程序代码的内容,包括各行代码所在的行号 |
finish(fi) | 结束当前正在执行的函数,并在跳出函数后暂停程序的执行 |
return(return) | 结束当前调用函数并返回指定值,到上一层函数调用处停止程序执行 |
jump(j) | 使程序从当前要执行的代码处,直接跳转到指定位置处继续执行后续的代码 |
quit (q) | 终止调试 |
4.2 qemu启动调试虚拟机
启动路径如下:
./qemu-system-aarch64 -enable-kvm -machine virt -cpu host -smp 4 -m size=8192M -kernel ../../linux-4.19.90/arch/arm64/boot/Image -initrd ../../busybox-1.35.0/rootfs.cpio -append "nokaslr root=/dev/ram rdinit=/linuxrc " -L ../pc-bios/ -nographic -S -s
可以通过组合键CTRL+A松开后按X或者进入系统后poweroff退出qemu
参数说明:
-kernel ../../linux-4.19.90/arch/arm64/boot/Image : 指定启用的内核镜像;
-initrd ../../busybox-1.35.0/rootfs.cpio:指定启动的内存文件系统;
-append "nokaslr root=/dev/ram rdinit=/linuxrc ": 附加参数,其中 nokaslr 参数必须添加进来,防止内核起始地址随机化;
-s :监听在 gdb 1234 端口,若不想使用1234端口,则可以使用-gdb tcp:<port>来取代-s选项
-S :表示启动后就挂起,等待 gdb 连接;
-nographic:不启动图形界面,调试信息输出到终端与参数 console=ttyAMA0 组合使用;
-L ../pc-bios/ :指定启动bios
4.3 GDB调试
cd linux-4.19.90/
gdb
(gdb) file vmlinux
(gdb) target remote :1234
(gdb) hbreak start_kernel
(gdb) hbreak rest_init
(gdb) info break
(gdb) c
(gdb) info registers
根据需要设置不同的的断点,进行单步调试
如下图:
详细调试过程这里不做赘述,网上有很多更详细的GDB资料可供参考,请谅解
5. 问题记录
- 编译qemu(git clone)报无法克隆子模组路径
具体报错信息如下:
git clone https://github.com/qemu/qemu.git
git checkout stable-6.0
../configure --enable-kvm --target-list=aarch64-softmmu
正克隆到 '/nvme0n1/qemu/capstone'...
fatal: 无法访问 'https://gitlab.com/qemu-project/capstone.git/':Could not resolve host: gitlab.com
fatal: 无法克隆 'https://gitlab.com/qemu-project/capstone.git' 到子模组路径 '/nvme0n1/qemu/capstone'
克隆 'capstone' 失败。按计划重试
正克隆到 '/nvme0n1/qemu/dtc'...
fatal: 无法访问 'https://gitlab.com/qemu-project/dtc.git/':Could not resolve host: gitlab.com
fatal: 无法克隆 'https://gitlab.com/qemu-project/dtc.git' 到子模组路径 '/nvme0n1/qemu/dtc'
克隆 'dtc' 失败。按计划重试
正克隆到 '/nvme0n1/qemu/meson'...
fatal: 无法访问 'https://gitlab.com/qemu-project/meson.git/':Could not resolve host: gitlab.com
fatal: 无法克隆 'https://gitlab.com/qemu-project/meson.git' 到子模组路径 '/nvme0n1/qemu/meson'
克隆 'meson' 失败。按计划重试
正克隆到 '/nvme0n1/qemu/slirp'...
fatal: 无法访问 'https://gitlab.com/qemu-project/libslirp.git/':Could not resolve host: gitlab.com
fatal: 无法克隆 'https://gitlab.com/qemu-project/libslirp.git' 到子模组路径 '/nvme0n1/qemu/slirp'
克隆 'slirp' 失败。按计划重试
正克隆到 '/nvme0n1/qemu/tests/fp/berkeley-softfloat-3'...
fatal: 无法访问 'https://gitlab.com/qemu-project/berkeley-softfloat-3.git/':Could not resolve host: gitlab.com
fatal: 无法克隆 'https://gitlab.com/qemu-project/berkeley-softfloat-3.git' 到子模组路径 '/nvme0n1/qemu/tests/fp/berkeley-softfloat-3'
克隆 'tests/fp/berkeley-softfloat-3' 失败。按计划重试
正克隆到 '/nvme0n1/qemu/tests/fp/berkeley-testfloat-3'...
fatal: 无法访问 'https://gitlab.com/qemu-project/berkeley-testfloat-3.git/':Could not resolve host: gitlab.com
fatal: 无法克隆 'https://gitlab.com/qemu-project/berkeley-testfloat-3.git' 到子模组路径 '/nvme0n1/qemu/tests/fp/berkeley-testfloat-3'
克隆 'tests/fp/berkeley-testfloat-3' 失败。按计划重试
正克隆到 '/nvme0n1/qemu/ui/keycodemapdb'...
fatal: 无法访问 'https://gitlab.com/qemu-project/keycodemapdb.git/':Could not resolve host: gitlab.com
fatal: 无法克隆 'https://gitlab.com/qemu-project/keycodemapdb.git' 到子模组路径 '/nvme0n1/qemu/ui/keycodemapdb'
克隆 'ui/keycodemapdb' 失败。按计划重试
正克隆到 '/nvme0n1/qemu/capstone'...
fatal: 无法访问 'https://gitlab.com/qemu-project/capstone.git/':Could not resolve host: gitlab.com
fatal: 无法克隆 'https://gitlab.com/qemu-project/capstone.git' 到子模组路径 '/nvme0n1/qemu/capstone'
第二次尝试克隆 'capstone' 失败,退出
/nvme0n1/qemu/scripts/git-submodule.sh: failed to update modules
Unable to automatically checkout GIT submodules ' ui/keycodemapdb tests/fp/berkeley-testfloat-3 tests/fp/berkeley-softfloat-3 meson dtc capstone slirp'.
If you require use of an alternative GIT binary (for example to
enable use of a transparent proxy), then please specify it by
running configure by with the '--with-git' argument. e.g.
$ ./configure --with-git='tsocks git'
Alternatively you may disable automatic GIT submodule checkout
with:
$ ./configure --with-git-submodules=validate
and then manually update submodules prior to running make, with:
$ scripts/git-submodule.sh update ui/keycodemapdb tests/fp/berkeley-testfloat-3 tests/fp/berkeley-softfloat-3 meson dtc capstone slirp
通过git clone下载下来的qemu不包含子模块,需要连网下载,由于我这边是离线环境,故会报上述错误,直接通过去https://download.qemu.org/网站下载相应的压缩包即可解决
- 设置断点后,继续执行报Cannot access memory at address 0xffff000009000990错误
详细过程如下:
(gdb) b start_kernel
Breakpoint 1 at 0xffff000009000990: file init/main.c, line 531.
(gdb) c
Continuing.
Warning:
Cannot insert breakpoint 1.
Cannot access memory at address 0xffff000009000990
Command aborted.
(gdb) q
经过查阅资料,需要附加参数(-append “nokaslr root=/dev/ram rdinit=/linuxrc”),其中 nokaslr 参数必须添加进来且为第一个参数,防止内核起始地址随机化,导致 gdb 断点不能命中;及第一次只能用hbreak (GDB的硬件断点) 替代普通断点。
- qemu编译是报错 ERROR: Cannot find Ninja
解决方法为下载相关依赖包,进行安装,网址为https://pkgs.org/ - qemu-system-aarch64启动虚拟机报错为failed to find romfile “efi-virtio.rom”
解决方法启动参数加入-L …/pc-bios/,该目录在qemu源码目录中
6. 参考文献
https://docs.kernel.org/dev-tools/gdb-kernel-debugging.html
https://xilinx-wiki.atlassian.net/wiki/spaces/A/pages/821624963/Debugging+Guest+Applications+with+QEMU+and+GDB
https://www.ebpf.top/post/qemu_gdb_busybox_debug_kernel/
https://blog.csdn.net/qq_28351609/article/details/114855630
https://www.songbingjia.com/nginx/show-60573.html