1. Prepare related tools
sudo apt install build-essential sudo apt install qemu sudo apt install libncurses5-dev bison flex libssl-dev libelf-dev
2. Install and configure the kernel
sudo apt install axel axel -n 20 https://mirrors.edge.kernel.org/pub/linux/kernel/v5.x/linux-5.4.34.tar.xz xz -d linux-5.4.34.tar.xz tar -xvf linux-5.4.34.tar cd linux-5.4.34 make defconfig # Default configuration is based on 'x86_64_defconfig' make menuconfig # Open debug related options
In make menuconfig, change the following settings
Kernel hacking ->
Compile-time checks and compiler options ->
[] Compile the kernel with debug info
[] Provide GDB scripts for kernel debugging
[*] Kernel debugging
Turn off KASLR (random address), otherwise the breakpoint will fail.
Processor type and features —->
[ ] Randomize the address of the kernel image (KASLR)
3. Compile and run the kernel
make -j$(nproc) qemu-system-x86_64 -kernel arch/x86/boot/bzImage
A panic occurs and the root file system needs to be mounted
4. Create a root file system
Download and compile and install busybox:
#First download the busybox source code from https://www.busybox.net and decompress it. After the decompression is complete, configure, compile and install it like the kernel. axel -n 20 https://busybox.net/downloads/busybox-1.31.1.tar.bz2 tar -jxvf busybox-1.31.1.tar.bz2 cd busybox-1.31.1 Build static binary (no shared libs) in make menuconfig #Setting is selected make -j$(nproc) & amp; & amp; sudo make install
Create a root filesystem image
mkdir rootfs # Create a new rootfs folder under the /linux-5.4.34 folder cd rootfs cp ../busybox-1.31.1/_install/* ./ -rf mkdir dev proc sys home sudo cp -a /dev/{<!-- -->null,console,tty,tty1,tty2,tty3,tty4} dev/
Prepare the init script file and place it in the root directory of the root file system (rootfs/init), and add the following content to the init file.
#!/bin/sh
mount -t proc none /proc
mount -t sysfs none /sys
echo “Wellcome OS!”
echo “——————–“
cd home
/bin/sh
Add executable permissions to init
chmod +x init
Packaged into a memory root file system image and run the kernel
find . -print0 | cpio --null -ov --format=newc | gzip -9 > ../rootfs.cpio.gz qemu-system-x86_64 -kernel ./arch/x86/boot/bzImage -initrd rootfs.cpio.gz
6. VSCode configures the Linux kernel debugging environment
Since the Linux kernel is highly customized, there is no way to make Intellisense prompt normally by configuring includePath, etc. Here, a Python script is used to generate the compile_commands.json file to help Intellisense prompt normally (including header files and macro definitions, etc.). Run the following command directly in the Linux source code directory to generate compile_commands.json.
python ./scripts/gen_compile_commands.py
Create a new .vscode folder, put the configuration file into this folder, and configure the following five files: c_cpp_properties.json, init, launch.json, settings.json, tasks.json
c_cpp_properties.json
{<!-- --> "configurations": [ {<!-- --> "name": "Linux", "includePath": [ "${workspaceFolder}/arch/x86/include/**", "${workspaceFolder}/include/**", "${workspaceFolder}/include/linux/**", "${workspaceFolder}/arch/x86/**", "${workspaceFolder}/**" ], "cStandard": "c11", "intelliSenseMode": "gcc-x64", "compileCommands": "${workspaceFolder}/compile_commands.json" } ], "version": 4 }
init
#!/bin/sh mount -t proc none /proc mount -t sysfs none /sys echo "Wellcome OS!" echo "--------------------" cd home /bin/sh
launch.json
{<!-- --> // Use IntelliSense to learn about possible attributes. // Hover to view descriptions of existing attributes. // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 "version": "0.2.0", "configurations": [ {<!-- --> "name": "(gdb) linux", "type": "cppdbg", "request": "launch", "preLaunchTask": "vm", "program": "${workspaceRoot}/vmlinux", "miDebuggerServerAddress": "localhost:1234", "args": [], "stopAtEntry": true, "cwd": "${workspaceFolder}", "environment": [], "externalConsole": false, "MIMode": "gdb", "miDebuggerArgs": "-n", "targetArchitecture": "x64", "setupCommands": [ {<!-- --> "text": "set arch i386:x86-64:intel", "ignoreFailures": false }, {<!-- --> "text": "dir .", "ignoreFailures": false }, {<!-- --> "text": "add-auto-load-safe-path ./", "ignoreFailures": false }, {<!-- --> "text": "-enable-pretty-printing", "ignoreFailures": true } ] } ] }
settings.json
{<!-- --> "search.exclude": {<!-- --> "**/.git": true, "**/.svn": true, "**/.DS_Store": true, "**/drivers": true, "**/sound": true, "**/tools": true, "**/arch/alpha": true, "**/arch/arc": true, "**/arch/c6x": true, "**/arch/h8300": true, "**/arch/hexagon": true, "**/arch/ia64": true, "**/arch/m32r": true, "**/arch/m68k": true, "**/arch/microblaze": true, "**/arch/mn10300": true, "**/arch/nds32": true, "**/arch/nios2": true, "**/arch/parisc": true, "**/arch/powerpc": true, "**/arch/s390": true, "**/arch/sparc": true, "**/arch/score": true, "**/arch/sh": true, "**/arch/um": true, "**/arch/unicore32": true, "**/arch/xtensa": true }, "files.exclude": {<!-- --> "**/.*.*.cmd": true, "**/.*.d": true, "**/.*.o": true, "**/.*.S": true, "**/.git": true, "**/.svn": true, "**/.DS_Store": true, "**/drivers": true, "**/sound": true, "**/tools": true, "**/arch/alpha": true, "**/arch/arc": true, "**/arch/c6x": true, "**/arch/h8300": true, "**/arch/hexagon": true, "**/arch/ia64": true, "**/arch/m32r": true, "**/arch/m68k": true, "**/arch/microblaze": true, "**/arch/mn10300": true, "**/arch/nds32": true, "**/arch/nios2": true, "**/arch/parisc": true, "**/arch/powerpc": true, "**/arch/s390": true, "**/arch/sparc": true, "**/arch/score": true, "**/arch/sh": true, "**/arch/um": true, "**/arch/unicore32": true, "**/arch/xtensa": true }, "[c]": {<!-- --> "editor. detectIndentation": false, "editor.tabSize": 8, "editor.insertSpaces": false }, "C_Cpp.errorSquiggles": "Disabled" }
tasks.json
{<!-- --> // See https://go.microsoft.com/fwlink/?LinkId=733558 // for the documentation about the tasks.json format "version": "2.0.0", "tasks": [ {<!-- --> "label": "vm", "type": "shell", "command": "qemu-system-x86_64 -kernel ${workspaceFolder}/arch/x86/boot/bzImage -initrd /rootfs.cpio.gz -S -s -nographic -append "console=ttyS0 "", "presentation": {<!-- --> "echo": true, "clear": true, "group": "vm" }, "isBackground": true, "problemMatcher": [ {<!-- --> "pattern": [ {<!-- --> "regexp": ".", "file": 1, "location": 2, "message": 3 } ], "background": {<!-- --> "activeOnStart": true, "beginsPattern": ".", "endsPattern": ".", } } ] }, {<!-- --> "label": "build linux", "type": "shell", "command": "make", "group": {<!-- --> "kind": "build", "isDefault": true }, "presentation": {<!-- --> "echo": false, "group": "build" } } ] }
7. start_kernel analysis
The starting point of the Linux kernel is the start_kernel function, so first break the point at start_kernel, start debugging, the program pauses at the breakpoint, and start tracking and analysis from start_kernel. Stepping over to track and analyze, it is found that process No. 0 init_task is set as the initial process of the whole system, that is, process No. 0 is created manually, and other processes are all created by Process No. 0. When the kernel boots, init_task is created and started, which is the starting point for all other processes.
![Insert picture description here](https://img-blog.csdnimg.cn/4f6b9949d05a4d39ae5f07deaa9eacf8.png
At the end of the start_kernel() function, the arch_call_rest_init() function body is the rest_init() function, so set a rest_init function breakpoint and enter the rest_init function body, which is executed by process 0.
Next, the kernel_init and kthreadd processes will be created
kernel_init will try to run /sbin/init, /etc/init, /bin/init, /bin/sh in turn
The cpu_startup_entry function is executed at the end of rest_init