编译方法如下:
git clone https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.10.179.tar.xz
tar -xvzf linux-5.10.179.tar.gz
cd linux-5.10.179
make menuconfig
在内核编译选项中,开启如下"Compile the kernel with debug info",在配置菜单中,启用内核debug,关闭地址随机化,不然断点处无法停止。
Kernel hacking --->
Compile-time checks and compiler options --->
[ ] Compile the kernel with debug info
Processor type and features ---->
[] Randomize the address of the kernel image (KASLR)
以上配置完成后会在当前目录生成 .config
文件,我们可以使用 grep
进行验证:
grep CONFIG_DEBUG_INFO .config
CONFIG_DEBUG_INFO=y
编译内核
make -j4
编译完成后,会在当前目录下生成vmlinux,这个在 gdb 的时候需要加载,用于读取 symbol 符号信息,包含了所有调试信息,所以比较大。
压缩后的镜像文件为bzImage, 在arch/arm64/boot
目录下。
pwd
/home/liujianguo/DebugLinuxKernal/linux-5.10.174/arch/arm64/boot
ls
Image Image.gz install.sh Makefile
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,如下
Settings --->
[*] Build BusyBox as a static binary (no shared libs)
这里一定要选择静态编译,编译好的可执行文件busybox
不依赖动态链接库,可以独立运行,方便构建initramfs。
之后选择Exit退出,到这里我们就可以编译busybox了,执行下面的命令
make -j 8
# 安装完成后生成的相关文件会在 _install 目录下
make install
[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
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之前,先安装必要的包( 安装包的过程中,遇到哪个包安装出错,就把哪个包移除再安装其他的包)。
sudo apt-get update
sudo apt-get install gcc make perl ninja-build git libglib2.0-dev libfdt-dev libpixman-1-dev zlib1g-dev libaio-dev libbluetooth-dev libbrlapi-dev libbz2-dev libcap-dev libcap-ng-dev libcurl4-gnutls-dev libgtk-3-dev libibverbs-dev libjpeg8-dev libncurses5-dev libnuma-dev librbd-dev librdmacm-dev libsasl2-dev libsdl1.2-dev libseccomp-dev libsnappy-dev libssh2-1-dev libvde-dev libvdeplug-dev libxen-dev liblzo2-dev valgrind xfslibs-dev libnfs-dev libiscsi-dev uml-utilities net-tools bridge-utils
sudo apt install python3-pip
sudo pip3 install meson
下载Qemu:
wget https://download.qemu.org/qemu-8.0.0.tar.xz
编译Qemu:
tar -xvf qemu-6.1.1.tar.xz
cd qemu-6.1.1
sudo ./configure
sudo make -j 4
安装Qemu(可选):
sudo make install
检查Qemu版本:
$ cd ..
$ qemu-system-arm -version
QEMU emulator version 6.1.1
Copyright (c) 2003-2021 Fabrice Bellard and the QEMU Project developers
**注意:**编译后,在qemu-8.0.0/build
目录下有相应的执行文件,可以不用使用make install
安装。使用方法如下:
/home/liujianguo/DebugLinuxKernal/qemu-8.0.0/build/qemu-system-aarch64 \
-machine virt \
-m 8G \
-smp 24 \
-cpu cortex-a57 \
-kernel ./arch/arm64/boot/Image \
-initrd ../initramfs.cpio.gz \
-append "nokaslr console=ttyAMA0 loglevel=8" \
-nographic \
-s -S
安装过程中,可能出现pyton版本过低的问题,可以通过以下命令查看当前安装python版本
python3.6 --version
Python 3.6.9
python3.7 --version
Python 3.7.5
输入以下命令,将Python3.7设置为默认的Python版本:
sudo update-alternatives --install /usr/bin/python3 python3 /usr/bin/python3.6 1
sudo update-alternatives --install /usr/bin/python3 python3 /usr/bin/python3.7 2
sudo update-alternatives --config python3
执行最后一个命令后,会出现以下提示:
sudo update-alternatives --config python3
There are 2 choices for the alternative python3 (providing /usr/bin/python3).
Selection Path Priority Status
------------------------------------------------------------
0 /usr/bin/python3.7 2 auto mode
1 /usr/bin/python3.6 1 manual mode
* 2 /usr/bin/python3.7 2 manual mode
Press <enter> to keep the current choice[*], or type selection number:
输入相应数字就可以选择相应版本。
qemu卸载根据安装方式的不同也会有响应的卸载方式:(1)源码编译安装需要手动卸载;(2)ubutnu pakage安装需要命令卸载
(1) 源码编译安装的qemu需要手动卸载:
源码安装后,文件的位置:
可执行文件默认放在/usr/local/bin
库文件默认存放在/usr/local/libexec
配置文件默认存放在/usr/local/etc
共享文件默认存放在/usr/local/share
卸载源码只需将上面四个目录中相关文件或者目录删除
rm -rf /usr/local/bin/qemu-*
rm -rf /usr/local/libexec/qemu-bridge-helper
rm -rf /usr/local/etc/qemu
rm -rf /usr/local/share/qemu
(2) pakage安装方式需命令卸载
删除包和相关依赖
sudo apt-get remove --auto-remove qemu-system-x86
删除配置文件和相关的数据文件
sudo apt-get purge --auto-remove qemu-system-x86
apt install qemu qemu-utils qemu-kvm virt-manager libvirt-daemon-system libvirt-clients bridge-utils
(1) qemu-aarch64和qemu-system-aarch64区别?
qemu-aarch64
和qemu-system-aarch64
其实是同一个软件的两种不同命令,其中qemu-aarch64
是一种简化的命令,通常用于启动默认的ARM 64位架构,而qemu-system-aarch64
则是一个更全面的命令,可以更加灵活地配置各种参数。使用qemu-system-aarch64
命令可以更好地控制模拟器的行为,可以配置更多的设备,比如网卡、硬盘、CD-ROM等,并且可以定制化模拟器的启动参数。因此,在一些需要复杂配置的场景下,使用qemu-system-aarch64
可能会更加方便。
(2) console=ttyAMA0和console=ttyS0区别?
console=ttyAMA0
和console=ttyS0
都是指定Linux内核将控制台输出重定向到特定的串口设备。
ttyAMA0
是ARM体系结构中某些板卡默认的串口设备,通常在使用UART接口调试时使用。而
ttyS0
是标准的串口设备,通常在PC中使用,并且也适用于其他一些体系结构的设备。在使用虚拟机调试时,通常会使用ttyS0
作为控制台输出设备。
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
以下是一些 GDB 常用的操作快捷键:
run
(简写r
):运行程序,可以带参数
break
(简写b
):设置断点
continue
(简写c
):继续运行程序直到遇到下一个断点或者程序结束
next
(简写n
):单步执行一行源代码,如果这一行代码中有函数调用,则不会进入函数体内部
step
(简写s
):单步执行一行源代码,如果这一行代码中有函数调用,则会进入函数体内部
p
):打印变量的值或者表达式的值
info
:打印关于程序状态的信息
info break
:打印所有断点的信息info registers
:打印寄存器的值info locals
:打印当前函数中的局部变量
backtrace
(简写bt
):打印函数调用栈
quit
(简写q
):退出 GDB断开GDB连接快捷键:
Ctrl+D
这只是一些常用的操作快捷键,GDB 还有很多其它的命令和选项,可以通过输入
help
或者help
来查看帮助文档。
arm64 Linux内核调试启动方法如下:
/home/liujianguo/DebugLinuxKernal/qemu-8.0.0/build/qemu-system-aarch64 \
-machine virt \
-m 8G \
-smp 24 \
-cpu cortex-a57 \
-kernel ./arch/arm64/boot/Image \
-initrd ../initramfs.cpio.gz \
-append "nokaslr console=ttyAMA0 loglevel=8" \
-nographic \
-s -S
x86 Linux内核调试启动方法如下:
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
组合使用;- 结束qemu仿真方法:先按
Ctrl+a
,松开后按x
。
在另一个窗口中,输入gdb,即可开启调试。
gdb
GNU gdb (GDB) 10.1
Copyright (C) 2020 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
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:
<https://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
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 打了两个断点, 两个断点都成功命中了。
- ms-vscode.cpptools
- remote-ssh
- gdb
在界面上打开:run->Add Configuration
,在launch.json
中编辑以下内容(注意此配置设置调试目标Linux架构为arm架构):
{
"version": "0.2.0",
"configurations": [
{
"name": "kernel-debug",
"type": "cppdbg",
"request": "launch",
"miDebuggerServerAddress": "127.0.0.1:1234",
"program": "${workspaceFolder}/vmlinux",
"args": [],
"stopAtEntry": false,
"cwd": "${workspaceFolder}",
"environment": [],
"externalConsole": false,
"logging": {
"engineLogging": false
},
"MIMode": "gdb",
"setupCommands": [
{
"text": "set architecture arm",
"description": "Set target architecture to ARM"
"ignoreFailures": true
}
]
}
]
}
在调试之前,需要启动qemu
调试服务,使用F5
快捷键就可以启动调试。