Using Qemu to debug the Linux kernel is a convenient way, so I recently did some practice and documented the general steps and some of the pitfalls.
Environment
Since I am at home on a long vacation, I only have a MBP with MacOS available, and it is easier to develop and debug Linux kernel in a Linux environment, so I created a virtual machine with Ubuntu 18.04 installed using VMware Fusion. Since compiling the Linux kernel and related software requires more resources, the virtual machine was configured with a dual-core CPU, 2GB of memory and 20GB of disk space (the laptop itself has limited resources), but the actual usage (especially physical memory and hard disk) was stretched to the limit, so 3GB of SWAP memory was added to the system and 20GB of disk space was expanded (which was still not enough) to The problem was solved.
Compile the Linux kernel
First, try to compile the kernel. Before compiling, you need to start the debugging configuration of the kernel using KConfig.
Download kernel source code
Downloading kernel source code in China is a complex physical activity due to the large amount of Linux kernel code and the availability of FGW in China.
The first method is to Clone the Linux source code directly from the Git repository, which is currently about 3.7GB. https://github.com/torvalds/linux), you often run into disconnections, which can be very annoying. If you clone from a domestic mirror, such as the Tsinghua Kernel Git mirror, you start out fast, but then it gets slower and slower. Therefore, if you are not as stubborn as I am, it is not recommended to download the Kernel source code in this way.
Another easier way is to download a specific version of the source code, which is available as a tarball from the kernel website or a mirror site. The kernel version I used in my experiments was 4.19, and the gz archive was about 150MB in size.
Configuring the Kernel
If you use Git Clone to get the kernel source code, you need to set the kernel source code to version 4.19 by using git checkout v4.19
.
Before you can compile, you need to install the dependencies (if you are prompted for other dependencies, you can install them as needed)
|
|
Before compiling, the kernel compilation options need to be configured using KConfig. Under the kernel folder, use make menuconfig
(command line interface) or make gconfig
(gtk-based graphical interface) to configure the kernel. During configuration, the following options need to be turned on.
and ensure that the following options are not turned on.
|
|
After exiting the configuration, you can find a configuration file named .config
generated in the kernel directory.
Compiling the kernel
Once configured, the kernel can be compiled using make
, and on multicore CPUs you can start a multithreaded compilation using make -jx
(x is the number of threads started).
If everything works, the kernel will be compiled after a long wait. The compilation will generate the vmlinux
file in the kernel root directory, which is the compiled raw kernel file (with debugging information), and the compressed kernel file in the arch/x86/boot/bzImage
directory (if the compiled architecture is x86, of course).
Compile and install GDB and Qemu
Since the versions of GDB and Qemu required for kernel debugging may be higher than the versions in the apt source, it is best to compile and install these software yourself.
Compile and install GDB
First, download the source code of GDB from the official website (http://www.gnu.org/software/gdb/download/) and unzip it (here we use the latest GDB 9.1 from the official website), it should be noted that some blogs on the Internet mention the need to modify the source code of GDB, but it is actually unnecessary, the reason for the error is that it is not automatically detected The reason for the error is that the type of the target architecture is not detected automatically, so you only need to set the type.
After unpacking, go to the GDB folder and execute the following command to complete the compilation and installation.
Finally, determine if the version of gdb is 9.1 by using gdb -v
, if it is, the installation is successful.
Compile and install Qemu
First, download (https://www.qemu.org/download/#source) the Qemu source code from the official website and unzip it (Qemu 5.0.0 is used here).
Since using Qemu in the Ubuntu GUI also requires the multimedia graphics library SDL, you need to first install sdl using apt.
|
|
After entering the Qemu directory, execute . /configure to check the system configuration and generate a Makefile, you need to pay attention to whether the check detects SDL support, the output is partly as follows.
Then execute make && make install
to complete the compilation and installation of Qemu.
After installing Qemu, a series of commands such as qemu-xxx
and qemu-system-xxx
will be generated to emulate different architectures of user-state applications and operating systems, and you can confirm whether Qemu is successfully installed by using commands such as qemu-system-x86_64 --version
.
Make ROOTFS
A rootfs with an init program is needed after the kernel is booted, so you need to make a rootfs before debugging the kernel.
Building an initrd-based rootfs
initrd is an in-memory root file system that loads the system before the hard disk is driven. Here, for convenience, only a simple program is written to initrd and is used as the init program (i.e. the first user-state process after the system is booted). Alternatively, you can use busybox as the init program in initrd.
Create a simple c program named fakeinit.c
.
Then use gcc to compile this code, which requires static linking and, if the kernel is not configured with 64-bit support (64-bit kernel) enabled, compile the code as a 32-bit program by adding the -m32
option to the gcc command line.
The compile command is as follows.
After compiling, use the cpio program to package.
|
|
Thus, an initrd-based rootfs is created.
Building a rootfs based on a hard disk image
Here we use busybox to build a rootfs based on a hard disk image. busybox is a single piece of software that integrates hundreds of common Linux commands and tools, which is very convenient when testing the kernel, called “The Swiss Army Knife of Embedded Linux “.
Download and compile busybox
First, download the busybox source code from the official website (https://busybox.net/downloads/) and unzip it (the latest busybox-1.31.1 is used here).
After unpacking and entering the busybox folder, first configure it using make gconfig
or make menuconfig
, which requires the following options to be enabled.
|
|
If you need to compile it to a 32-bit version, you need to fill the -m32
command with the following options.
As with the kernel, a configuration file named .config
will be generated in the directory after exiting.
Then, use the make
command to compile busybox.
Creating rootfs with busybox
First, create an empty disk image file, and then format it as follows.
Then, mount the disk image you just created (requires the use of a loop device).
Next, install the compiled busybox target file into the rootfs folder in the busybox source directory.
|
|
Finally, to configure busybox init and uninstall rootfs.
Now, a busybox-based rootfs disk image has been created.
Debugging the kernel with Qemu and GDB
Booting the kernel with Qemu
Since the compiled kernel architecture is x86, use the qemu-system-x86_64
program to load and boot the kernel.
If you use intird as the rootfs, the specific command is
|
|
If using a disk image as a rootfs, the specific command is
Debugging the kernel with GDB
As a final step, since Qemu has just enabled remote debugging, you only need to connect gdb via
|
|
If you use target remote:1234
to connect to Qemu’s gdb server directly in the gdb debugger at this point, you will get the error Remote 'g' packet reply is too long
, which is caused by the fact that gdb does not correctly identify the architecture of the debug This is caused by the fact that gdb does not correctly recognize the architecture of the target (some bloggers think they need to modify the source code to block this error, which is actually unnecessary), so you just need to set the target architecture with set arch i386:x86-64:intel
before remote attaching.
For example, if you want to set a breakpoint in the start_kernel function for debugging, the gdb command after starting Qemu would be as follows
As you can see, the kernel is interrupted on the start_kernel function after booting.
Postscript
Kernel documentation
The kernel documentation includes a detailed explanation of how to debug the kernel using GDB.
The latest version of this document can be found on the kernel’s website: https://www.kernel.org/doc/html/latest/dev-tools/gdb-kernel-debugging.html.
For example, the html version of the documentation can be compiled using make htmldocs
and can be accessed in a browser after starting the HTTP server, e.g. http://127.0.0.1:8000/dev-tools/gdb-kernel-debugging.html
.