引言
前面我们安装的Ubuntu 24.04.2 LTS的内核版本是6.11
这是由Ubuntu提供的内核,基于主线内核应用了一系列补丁而来,且大部分debug选项都是关闭的,而我们要调试的内核是主线内核,所以现在我们要编译一个主线内核,并打开他的debug选项,然后安装我们自己编译的这个debug内核,以供后面我们debug使用。
进行后面的操作之前,最好先使用下面的命令更新一下:
sudo apt upgrade
编译内核
选择一个主线内核 - 6.12.28
截至目前,最新稳定主线内核的版本是6.14.6,longterm的版本是6.12.28
预计我们完成该系列所有的内核debug学习,需要很长一段时间,所以最好的选择是使用LTS内核,即我们将编译安装6.12.28版本的内核
下载内核
这里选择中科大镜像源来下载6.12.28版本的内核:
执行如下命令进行下载:
mkdir debug_kernel
cd debug_kernel
wget https://mirrors.ustc.edu.cn/kernel.org/linux/kernel/v6.x/linux-6.12.28.tar.xz
执行如下命令解压内核
tar xf linux-6.12.28.tar.xz
现在我们就得到6.12.28的内核源码了
配置debug内核
进入内核目录
cd linux-6.12.28
然后执行如下命令:
lsmod > lsmod.txt
make LSMOD=lsmod.txt localmodconfig
命令解释
lsmod > lsmod.txt:列出当前加载到内核中的所有模块,并保存到lsmod.txt文件中
make LSMOD=lsmod.txt localmodconfig:生成一个最小化的内核配置文件.config
,只保留当前系统使用的模块和配置选项
此时会提示没有make命令,同时也会提示应该如何做:
执行如下命令安装make
sudo apt install make -y
然后再执行一次
make LSMOD=lsmod.txt localmodconfig
此时又提示没有gcc
安装gcc
sudo apt install gcc -y
gcc安装完成后再执行make LSMOD=lsmod.txt localmodconfig,又显示没有flex
安装flex
sudo apt install flex -y
flex安装完成后再执行make LSMOD=lsmod.txt localmodconfig,又显示没有bison
安装bison
sudo apt install bison -y
这下终于可以执行了
然后出现了很多选项需要我们选择,在这里我们直接回车选择所有的默认值就行了
全部执行完之后,就会生成.config文件了
关键的一步来了,为了后面能debug内核,我们需要修改内核配置,执行如下命令:
make menuconfig
很不幸,又报错了,相信现在你能根据报错log进行解决了:
对的,就是安装libncurses-dev:
sudo apt install libncurses-dev
安装成功后,再执行make menuconfig就可以看到配置界面了:
配置内核
内核有这么多配置选项,和debug相关的也不在少数,我们要如何配置呢?
这里先给出一些和debug相关的配置建议,现在这些配置选项不知道含义也没关系,后面会讲到的,先照着配置即可:
General setup --->
<*> Kernel .config support //允许.config包含在kernel中,可以通过scripts/extract-ikconfig提取出来
[*] Enable access to .config through /proc/config.gz //可以通过/proc/config.gz获取.config
-*- Load all symbols for debugging/ksymoops //启用内核符号
-*- Include all symbols in kallsyms
Kexec and crash features --->
[*] kernel crash dumps //启用crash dumps
General architecture-dependent options --->
-*- Kprobes //允许在不修改内核代码的情况下,在任意内核函数或指令地址上插入probe
[*] Stack Protector buffer overflow detection
[*] Strong Stack Protector //启用金丝雀(canary)栈溢出保护机制,基于GCC 编译器的
-fstack-protector-strong
选项实现的(28) Number of bits to use for ASLR of mmap base address //Linux在为进程分配 mmap区域(如共享库、堆、mmap 文件等)时,会在基地址中加入一段随机偏移的位数,值越高越安全,所以我们不要把该值设置太大
[*] Use a virtually-mapped stack //每个线程的内核栈使用vmalloc分配
在栈的下方(或上方)插入一个不可访问的“防护页(guard page)”,一旦发生栈溢出写操作,就会触发页错误(page fault),从而防止恶意利用。
Executable file formats --->
[*] Enable core dump support //启用核心转储(core dump)功能
[*] Enable loadable module support --->
[ ] Module signature verification //关闭内核模块签名验证,该选项需要先关闭Security options下的Basic module for enforcing kernel lockdown才能关闭
[ ] Automatically sign all modules //关闭对模块的签名
[ ] Trim unused exported kernel symbols //在最终的内核和模块中移除未被使用的符号,不要开启
Device Drivers --->
[*] Network device support --->
[*] Network core driver support
<*> Network console logging support //内核可以使用网络控制台功能,即将printk()输出的日志通过网络发送到远程机器
[*] Dynamic reconfiguration of logging targets //在运行时动态开启/关闭netconsole,并修改其目标IP、端口等配置
Security options --->
[ ] Restrict unprivileged access to the kernel syslog //限制非特权用户使用dmesg访问内核日志,为了便于调试,关闭该选项
[] Basic module for enforcing kernel lockdown //关闭Lockdown LSM
Kernel hardening options --->
Hardening of kernel data structures --->
[*] Trigger a BUG when data corruption is detected //内核检测到数据损坏就会触发一个 BUG()(即引发 Oops 或 panic)
Kernel hacking --->
printk and dmesg options --->
[*] Enable dynamic printk() support //在运行时动态地启用或禁用内核中使用 pr_debug() 和 dev_dbg() 等调试打印语句的输出
-*- Enable core function of dynamic debug support
[*] Verbose BUG() reporting (adds 70K) //用于在触发 BUG()、BUG_ON()、WARN()、WARN_ON() 等宏时,输出更详细的调试信息(如出错的文件名、行号、函数名等)
Compile-time checks and compiler options --->
Debug information (Rely on the toolchain's implicit default DWARF version) ---> //使用gcc -g为内核或模块生成debug信息
Generic Kernel Debugging Instruments --->
(0x1) Enable magic SysRq key functions by default //启用Magic SysRq所有功能
-*- Debug Filesystem
Debugfs default access (Access normal) ---> //控制 /sys/kernel/debug 文件系统(即 debugfs)的访问权限,这里不限制对debugfs的访问[*] KGDB: kernel debugger --->
[*] Undefined behaviour sanity checker ---> //启用 Undefined Behavior Sanitizer 支持。UBSan 是一种运行时检测工具,旨在捕获程序中未定义行为的实例
[*] KCSAN: dynamic data race detector ---> //该选项和KASAN不可同时选择,后面选择KASAN这个会被取消
Memory Debugging --->
[*] Debug page memory allocations //通过在每次内存页释放后将其从虚拟地址空间中解除映射(unmap),使得任何试图访问已释放内存的操作都会触发一个缺页异常(page fault),从而暴露潜在的 bug
[*] Enable debug page memory allocations by default?
[*] Warn on W+X mappings at boot //扫描整个内核映射空间,报告所有 WX 区域,并在发现时打印警告信息
[*] Kernel memory leak detector //检测内核中的内存泄漏
[ ] Default kmemleak to off (NEW)
[*] Detect stack corruption on calls to schedule() //用于在调度器(scheduler)中检测任务栈(thread stack)是否发生 栈缓冲区溢出
[*] KASAN: dynamic memory safety error detector ---> //KASAN内存错误检测
Debug Oops, Lockups and Hangs --->
[ ] Panic on Oops //关闭该选项,当内核发生 Oops(即严重异常)时不触发 panic
(0) panic timeout
-*- Detect Soft Lockups
[ ] Panic (Reboot) On Soft Lockups //关闭该选项,soft lockup不触发panic
[ ] Panic (Reboot) On Hard Lockups //hard lockup不触发panic
[*] Detect Hung Tasks
[ ] Panic (Reboot) On Hung Tasks //Hung tasks不触发panic
Lock Debugging (spinlocks, mutexes, etc...) --->
[*] Lock debugging: prove locking correctness //检测内核中使用的锁之间的依赖关系是否合理
[*] Lock usage statistics //启用对各种锁(如 spinlock、mutex、rwlock 等)的运行时统计信息收集
[*] Sleep inside atomic section checking //用于检测在 原子上下文(atomic context)中调用可能导致睡眠的函数 的错误行为
[*] Latency measuring infrastructure //用于收集和展示系统中导致 调度延迟(即任务等待运行的时间)的根源
[*] Tracers --->
-*- Kernel Function Tracer //trace每个内核函数
[*] Kernel Function Graph Tracer
[*] enable/disable function tracing dynamically
[*] Filter access to /dev/mem //限制用户空间对 /dev/mem 设备的访问权限
x86 Debugging --->
[*] Early printk //在内核启动的非常早期阶段(甚至早于 printk() 可用之前)就能打印调试信息
Kernel Testing and Coverage --->
[*] Fault-injection framework //在运行时人为地引入错误(如内存分配失败、I/O 错误、延迟等),以测试内核或模块的 健壮性、容错能力和异常处理逻辑
未提到的配置暂时就按默认值来就行,配置完成后,保存退出:
配置CONFIG_DEBUG_INFO_BTF
现在还有一个推荐配置没有打开,是因为我们缺少了必须的依赖包,所以刚才配置时,没有出现该选项,导致我们无法选择,这个配置就是CONFIG_DEBUG_INFO_BTF,现在让我们安装一下这个配置需要的依赖:
sudo apt install dwarves -y
安装完成后,再次执行make menuconfig,选择以下选项:
General setup --->
[*] Compile also drivers which will not load //启用一些 仅用于编译验证的代码路径或模块。它并不改变内核的功能行为,而是帮助开发者在构建过程中发现潜在的语法错误、类型不匹配、未使用的变量等问题,CONFIG_DEBUG_INFO_BTF依赖该选项
此时我们就可以选择CONFIG_DEBUG_INFO_BTF了:
Kernel hacking --->
Compile-time checks and compiler options --->
[*] Generate BTF type information //用于增强 eBPF(extended Berkeley Packet Filter)生态系统中程序与内核之间的类型交互能力,使用户空间工具可以准确理解内核中数据结构的布局和成员信息
编译debug内核
编译之前还需要做如下配置:
-*- Cryptographic API --->
() File name or PKCS#11 URI of module signing key //删除这个文件,否则后面编译报错
() X.509 certificates to be preloaded into the system blacklist keyring //删除这个文件,否则后面编译报错
现在可以编译内核了,执行如下命令开始编译内核:
make -j6 all
根据你自己机器的情况修改-j后面的数字
刚开始编译就遇到的错误^_^
这需要安装libelf-dev:
sudo apt install libelf-dev -y
再次编译又出现错误:
这需要安装libssl-dev:
sudo apt install libssl-dev -y
终于可以编译了,耐心等待编译完成,可以休息一下了 ======
终于编译完成
编译完成后,生成vmlinux和arch/x86/boot/bzImage文件:
安装debug内核
执行如下命令安装内核:
sudo make modules_install && sudo make install
启动debug内核
现在让我们重启机器,进入我们自己编译的debug内核
nice,现在我们的ubuntu内核已经变成了我们自己编译的6.12.28了 ^_^
如果你没进到新内核,可能需要你重启进入grub界面选择启动6.12.28内核,如果你的机器重启后直接就进入老内核,并没有进入grub界面给你选择的机会,那就需要你修改/etc/default/grub文件:
sudo vim /etc/default/grub
(如果你之前没有安装vim,可能需要你安装vim:sudo apt install vim -y)
修改如下内容:
GRUB_TIMEOUT_STYLE=menu
GRUB_TIMEOUT=5
保存退出,然后执行:
sudo update-grub
再重新启动,此时应该可以进入grub界面了,选择启动6.12.28内核即可。