sharkie's inn

Back

环境:Ubuntu 25.10

内核版本:Linux 6.18

安装依赖#

sudo apt-get install git make gcc flex bison libssl-dev libelf-dev \
    clang lld llvm qemu-system-x86 python3
bash

配置与编译#

# 清理旧的构建
make LLVM=1 clean

# 生成默认配置 (x86_64)
make LLVM=1 defconfig

# 使用图形化界面进行一些其他配置
make LLVM=1 menuconfig
bash
  • 保留调试信息:Kernel hacking -> Compile-time checks and compiler options -> Debug information,可以选择 Rely on the toolchain’s implicit default DWARF version”,让工具链决定 DWARF 版本。
  • (可选)GDB 辅助脚本:Kernel hacking -> Compile-time checks and compiler options -> 勾选 Provide GDB scripts for kernel debugging
  • 关闭 KASLR:Processor type and features -> 取消勾选 Randomize the address of the kernel image (KASLR)

然后编译内核

make LLVM=1 -j$(nproc)
bash

生成 compile_commands.json(供 clangd 使用)

# 这会在源码根目录生成 compile_commands.json
python3 scripts/clang-tools/gen_compile_commands.py
bash

启动 QEMU#

# -s: 开启 gdbserver (默认端口 1234)
# -S: 启动时冻结 CPU,直到调试器输入 'c'
# -nographic: 在终端显示输出
qemu-system-x86_64 \
    -kernel arch/x86/boot/bzImage \
    -append "console=ttyS0 root=/dev/sda earlyprintk=serial" \
    -nographic \
    -s -S
bash

注意:这里没有挂载 rootfs,内核启动到最后会 panic,但足够调试内核启动过程。如果你有 rootfs 镜像,请通过 -hda 或 -initrd 加上。

配置 VSCode#

首先 VSCode 要打开远程 linux 源代码的根目录,然后配置 .vscode/launch.json。

{
    "version": "0.2.0",
    "configurations": [
        {
            "type": "lldb",
            "request": "launch",
            "name": "Debug Linux Kernel (Remote)",
            "targetCreateCommands": [
                // 加载带有符号表的 vmlinux
                "target create ${workspaceFolder}/vmlinux"
            ],
            "processCreateCommands": [
                // 连接到 QEMU 的 GDB Stub
                "gdb-remote localhost:1234"
            ],
            // 解决源码映射问题(如果在不同机器或容器编译,需要调整这里)
            // 如果是在本机编译,通常不需要
            // "sourceMap": {
            //     "/proc/self/cwd": "${workspaceFolder}"
            // }
        }
    ]
}
json

然后直接在 init/main.c 文件下的 start_kernel 函数打个断点,启动调试,它就会停在梦开始的地方了。

到此为止,我们已经可以调试整个 linux 的启动过程了。

(可选)如何让内核成功启动#

构建 initramfs 目录结构#

在你想要的目录下构建即可

mkdir -p my_initramfs/{bin,sbin,etc,proc,sys,usr/bin,usr/sbin,dev}
bash

安装 BusyBox 的静态可执行文件#

官网:https://busybox.net/

BusyBox 被称为“嵌入式 Linux 的瑞士军刀”,它能为你提供 sh, ls, cat 等基本命令。

可以直接下载编译好的静态可执行文件,或者下载源码自己编译出 busybox 的二进制文件。

然后将 busybox 静态可执行文件放入上面创建的 my_initramfs/bin 文件夹里面即可。

编写 init 文件#

内核启动之后会在根目录下找 init 可执行文件然后执行,所以 init 可以是 elf 也可以是 shell 脚本。下面示例的是 init 为脚本的情况。

touch my_initramfs/init
chmod +x my_initramfs/init
bash

编辑 init 内容如下

#!/bin/busybox sh

# 挂载必要的核心文件系统
/bin/busybox mount -t proc none /proc
/bin/busybox mount -t sysfs none /sys
/bin/busybox mount -t devtmpfs devtmpfs /dev

# 自动创建所有软链接(给常用的工具创建软链接,比如 ls, ps 之类的)
# 不然每次都需要在命令前加 busybox 前缀
/bin/busybox --install -s /bin

# 友好提示语
echo "---------------------------------------"
echo "  Welcome to your Debug Linux Kernel!  "
echo "---------------------------------------"

# 启动一个交互式 Shell,并解决 tty 访问问题
exec setsid cttyhack sh
bash

打包为 cpio 镜像#

cd my_initramfs
find . | cpio -o -H newc | gzip > ../initramfs.cpio.gz
bash

启动 QEMU#

# 这里的 rdinit=/init 就是指内核一进去要运行哪个可执行文件,这里是 /init,即根目录下的 init 文件。
qemu-system-x86_64 \
    -kernel arch/x86/boot/bzImage \
    -initrd initramfs.cpio.gz \
    -append "console=ttyS0 rdinit=/init nokaslr" \
    -nographic \
    -s -S
bash

到这里我们发现,其实不用安装 busybox 也可以,只要我们的 rdinit 指向一个静态可执行文件即可,可以是我们自己的可执行文件。

调试 Linux 内核(使用 clang, lldb 工具)
https://sharkie.cn/blog/%E8%B0%83%E8%AF%95-linux-%E5%86%85%E6%A0%B8%E4%BD%BF%E7%94%A8-clang-lldb-%E5%B7%A5%E5%85%B7
Author sharkie
Published at December 24, 2025
Comment seems to stuck. Try to refresh?✨