How to debug Linux kernel modules using CGDB + QEMU

preface

Linux code is huge and complex, light code will make people dizzy, if you can debug the code execution process through debugging tools, it will be great help to learn Linux kernel and solve the usual problems. This article will explain how to debug the Linux kernel code using CGDB + qEMu. The operating system version is CentOS Linux Release 7.2.1511 (Core).

1. Compile the kernel

1) Get the kernel code

Kernel code download address: [www.kernel.org/] (www.kernel.org/), this paper is based on 4.9.15…As shown below, click the tarball link of the corresponding version to download it

After the download is complete, copy the tar file to the /root directory on the test machine and decompress it.

# cd /root
# tar xf linux-4.9.153.tar.xz
Copy the code

2) Compile a kernel that supports debugging

Configure compilation options
# cd linux-4.9.153
# make menuconfig
Copy the code

Enable Loadable Module support:

Press the space bar to remove the Module signature verification option to prevent Module verification failed: signature and/or required key missing – tainting kernel

Navigate to the Exit button and return to the superior menu.

Locate File Systems and press Enter:

Select EXT4 debugging support and JBD2 (EXT4) debugging support.

Navigate to the Exit button and return to the superior menu.

To locate Kernel Hacking, press Enter:

Locate Kernel debugging and press the space bar to select it.

Navigate to compile-time checks and Compiler options and press Enter.

Locate Compile the kernel with debug info and Provide GDB scripts for kernel debugging respectively, and press the space bar to select.

Save, exit

Begin to compile
make -j 30
Copy the code

-j 30 Indicates the number of CPU cores for parallel compilation

2. Build initramfs root file system

Here Busybox builds a minimalist initramfs to provide basic user-mode executables.

1) build Busybox

[Download Busybox-1.28.0] (busybox.net/downloads/b…

The CONFIG_STATIC parameter can be configured to compile a static version of Busybox, so that the Busybox executable file does not depend on the dynamic library, so that initramfs can be built easily

# cd /root/busybox-1.28.0
# make menuconfig
Copy the code

Select Settings and press Enter.

Select Build static binary (no shared libs) and press Enter.

Exit, prompting you to save, and select Yes

Begin to compile

# yum install glibc-static -y
# gcc --version
  gcc (GCC) 4.8.5 20150623 (Red Hat 4.8.5-4)
# make -j 30
# make install
Copy the code

2) create initramfs

Create initramfs, which contains the BusyBox executable, the necessary device files, the startup script init, and the module to debug. Only virtual file systems procfs and sysfs are mounted in the init script. The root file system is not mounted. All debugging operations are performed in memory. This example uses the ext4.ko module as a demonstration, so you need to put ext4.ko and its dependencies into initramfs.

# mkdir initramfs # cd initramfs # cp .. /_install/* -rf ./ # mkdir dev proc sys # sudo cp -a /dev/{null, console, tty1, tty2, tty3, Tty4} dev/ # rm linuxrc # chmod a+x init # mkdir -p modules/4.9.153/ # cp /root/linux-4.9.153/fs/ext4/ext4.ko lib/modules/4.9.153/ # cp /root/linux-4.9.153/fs/jbd2/jbd2.ko lib/modules/4.9.153/ # Cp /root/linux-4.9.153/fs/mbcache.ko lib/modules/4.9.153/ # ls bin dev init lib proc sbin sys usrCopy the code

The contents of the init file

#! /bin/busybox sh mount -t proc mone /proc mount -t sysfs none /sys mdev -s exec /sbin/initCopy the code

Packaging initramfs:

# find . -print0 | cpio --null -ov --format=newc | gzip -9 > .. /initramfs.cpio.gzCopy the code

3. Start the VM

# yum install qemu-system-x86-2.0.0 # yum install qemu-system-x86-2.0.0 # CD.. # qemu-system-x86_64-s -kernel /root/linux-4.9.153/arch/x86_64/boot/ bzimage-initrd initramfs.cpio.gz -nographic -append "console=ttyS0" -m 1GCopy the code

The parameters of the qemu-system-x86_64 command are described as follows:

  • -s-gdb tcp::1234Listen on port 1234 in GDBtarget remote localhost:1234The connection;
  • -kernelSpecify a compiled debug version of the kernel;
  • -initrdSpecify the initramfs made;
  • -nographicCancel the graphics output window, making QEMU a simple command line program;
  • -append console=ttyS0Redirect the output to console, and stdio will appear in standard output, note that the S in ttyS0 is capitalized;
  • -m 1GExample Set the VM memory size.

After the startup is complete, press Enter to go to the CLI interface

. [1.645828] Freeing unused kernel memory: 836K [1.659842] 748K can't run '/etc/init.d/rcS': No such file or directory Please press Enter to activate this console. [2.144752] TSC: Refined TSC clocksource Calibration: 2194.896 MHz [2.145315] ClockSource: TSC: mask: 0xFFFFFFFFFFFFFF MAX_Cycles: 0x1FA35D3C521, MAX_IDLE_NS: 440795261667 ns [2.377779] INPUT: 0x1FA35D3C521, MAX_IDLE_NS: 440795261667 ns [2.377779] input: ImExPS / 2 Generic Explorer Mouse as/devices/platform/i8042 / serio1 / input/input3 [3.153834] clocksource: Switched to clocksource tsc / # ls bin dev init proc root sbin sys usr / #Copy the code

4. Use CGDB for debugging

CGDB is an enhanced version of GDB, which makes the code look much nicer when debugging. We will log in to the test machine in a separate window and run CGDB debug kernel.

# yum install cgdb -y
# cgdb -v
  CGDB 0.6.8
# cd /root/linux-4.9.153
# cgdb vmlinux
Copy the code

In the GDB command line, type target remote :1234 for remote debugging, set breakpoint in register_filesystem, type C, Then run modprobe ext4 on the VM to load the ext4 filesystem module to enter the register_filesystem breakpoint function. Register_as_ext3 () is called in ext4’s module initialization function ext4_init_fs; register_as_ext3() is called in ext4’s module initialization function ext4_init_fs. Register_as_ext2 () is then called; And register_filesystem (& ext4_fs_type);

(gdb) target remote :1234 Remote debugging using :1234 native_safe_halt () at ./arch/x86/include/asm/irqflags.h:57 (gdb)  b register_filesystem Breakpoint 1 at 0xffffffff81257dd0: file fs/filesystems.c, line 70. (gdb) c Continuing. Breakpoint 1, register_filesystem (fs=0xffffffffa00a0ac0) at fs/filesystems.c:70 (gdb) p fs->name $1 = 0xffffffffa0095cc0 "ext3" (gdb)Copy the code

Author: Yuan Xin [Didi Chuxing Senior Software Development Engineer yuan Xin]

  • If you register with Didi Cloud now, you will get 10,000 yuan in red envelopes
  • Special offer in August, 1C2G1M cloud server 9.9 yuan/month limited time grab
  • Didi Cloud messenger exclusive special benefits, including annual cloud server as low as 68 yuan/year
  • Enter master code [7886] to get a 10% discount for all GPU products