VS Code-based Linux kernel debugging environment construction and start_kernel tracking analysis

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