lab3- use vscode to debug linux

Environment: ubuntu-18.04.6

Install development tools

sudo apt install build-essential
sudo apt install qemu # install QEMU
sudo apt install libncurses5-dev bison flex libssl-dev libelf-dev

Download kernel source code

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

Configure kernel options

make defconfig # Default configuration is based on 'x86_64_defconfig'
make menuconfig
# Open debug related options
Kernel hacking --->
    Compile-time checks and compiler options --->
        [*] Compile the kernel with debug info
        [*] Provide GDB scripts for kernel debugging
 [*] Kernel debugging
# Close KASLR, otherwise the breakpoint will fail
Processor type and features ---->
    [] Randomize the address of the kernel image (KASLR)

Compile and run the kernel

make -j$(nproc) # nproc gives the number of CPU cores/threads available
# Test whether the kernel can be loaded and run normally, because there will be a kernel panic without a file system
qemu-system-x86_64 -kernel arch/x86/boot/bzImage

You can see kerel panic

Make a root filesystem

When the computer is powered on, the kernel is first loaded by the bootloader, and then the kernel needs to mount the memory root file system, which contains the necessary device drivers and tools. The bootloader loads the root file system into the memory, and the kernel will mount it to the root directory/download , and then run the init script in the root file system to perform some startup tasks, and finally mount the real disk root file system. In order to simplify the experimental environment, we only make the memory root file system here. Here, BusyBox is used to build a minimal memory root file system and provide basic user mode executable programs.

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

make menuconfig
# Remember to compile it into a static link instead of a dynamic link library.
Settings —>
[*] Build static binary (no shared libs)
Then compile and install, and it will be installed in the _install directory under the source code directory by default.
make -j$(nproc) & amp; & amp; make install

Then make a memory root file system image, the general process is as follows:
mkdir rootfs
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 file system and directory (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 MengningOS!”
echo “——————–“
cd home
/bin/sh
Add executable permissions to the init script
chmod +x init

Packaged into a memory root file system image
find . -print0 | cpio –null -ov –format=newc | gzip -9 > ../rootfs.cpio.gz
Test to mount the root file system to see if the init script is executed after the kernel boot is complete
qemu-system-x86_64 -kernel linux-5.4.34/arch/x86/boot/bzImage -initrd rootfs.cpio.gz

VSCODE configuration

Open the linux-5.4.34 folder with VSCode

Create a .vscode folder on the linux-5.4.34 folder and add the following configuration files

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
}

Use a Python script 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.

python3 ./scripts/gen_compile_commands.py

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
}
]
}
]
}

task.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”
}
}
]
}

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”
}

Then add a breakpoint in vscode

Enter the command and press F5 to debug

qemu-system-x86_64 -kernel linux-5.4.34/arch/x86/boot/bzImage -initrd rootfs.cpio.gz -S -s -nographic -append "console=ttyS0"

You can track the execution process of linux, as follows

It can be seen that after the system creates process 0, it performs a series of initializations, and finally executes rest_init.

rest_init creates two kernel threads through kernel_thread: one is kernel_init, which finally starts the user-mode process init, which is the ancestor of all user processes; the other is the kthreadd kernel thread, which is the ancestor of all kernel threads. Responsible for managing all kernel threads.

When the system starts, except that the initialization process of process 0 is manually coded and created, the creation of init process 1 is actually a copy of process 0, and the process pid is modified according to the needs of process 1.

The No. 2 kthreadd kernel thread is also created by duplicating the No. 0 process. Through the following code, we can see that process No. 1 and process No. 2 are finally created through _do_fork, and the process created by the user state through the system call fork is finally completed through _do_fork.

The idle process runs the test continuously to determine whether it needs to be scheduled, and if not, it will enter the idle state

The knowledge points of the article match the official knowledge files, and you can further learn relevant knowledge. CS introductory skill treeIntroduction to LinuxFirst acquaintance with Linux28827 People are studying systematically