编译安装Debug内核

引言

前面我们安装的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内核即可。

<think>嗯,用户问的是如何在Jetson Nano上编译安装内核模块。我需要先理清楚整个过程,确保步骤正确,同时考虑到用户可能的疑问点。 首先,Jetson Nano是基于ARM架构的,所以编译内核模块需要特定的工具链和源码。我应该先确认用户是否需要安装交叉编译环境,或者是否直接在Jetson Nano上编译。不过通常来说,直接在设备上编译更简单,特别是对于新手。 接下来,安装依赖项是必要的。比如build-essential、libncurses-dev这些,可能还需要安装正确的内核头文件。用户可能会遇到找不到头文件的问题,所以需要指导他们如何正确安装。 然后,获取内核源码是关键步骤。NVIDIA提供了专门为Jetson系列准备的源码,可能需要用git来克隆,或者从官方页面下载。这里要注意版本匹配,内核版本必须与当前运行的保持一致,否则编译的模块无法加载。可能需要教用户如何查看当前内核版本,并确保源码的正确分支或标签。 配置内核的时候,使用已有的配置文件可以节省时间,比如从/boot目录复制config文件。之后可能需要运行make prepare和make modules_prepare,这步容易被忽略,导致后续编译出错。 编写一个简单的模块例子,比如hello world,能帮助用户测试整个流程。Makefile的编写也需要特别注意,指定正确的内核源码路径和架构。用户如果路径不对,可能会遇到各种错误。 编译完成后,加载模块需要用insmod或者modprobe,可能需要sudo权限。还要提醒用户检查dmesg的输出,确认模块是否加载成功。卸载模块的部分也不能少,避免留下问题。 最后,如果是自定义模块,需要修改代码和Makefile,这时候要强调版本一致性和路径正确性。常见问题比如模块版本不匹配、依赖项缺失,这些都需要给出解决方法,比如开启CONFIG_MODVERSIONS等。 整个过程要分步骤,尽量详细,避免用户踩坑。同时用中文表达,保持口语化,让用户容易理解。可能用户是嵌入式开发的新手,所以每个步骤都要解释清楚,确保他们能跟上操作。</think>好的,我将分步骤为您讲解Jetson Nano内核模块的编译安装流程。这是嵌入式Linux开发的进阶操作,请仔细跟随每一步: 1. **环境准备** - 连接Jetson Nano到网络 - 更新软件源: ```bash sudo apt update sudo apt upgrade -y ``` - 安装编译依赖: ```bash sudo apt install build-essential libncurses5-dev bc git libssl-dev ``` 2. **获取内核源码** - 确认当前内核版本: ```bash uname -r ``` 示例输出:`4.9.140-tegra` - 克隆NVIDIA官方内核仓库: ```bash git clone --depth 1 -b jetson-nano-4.9 https://github.com/ridgerchu/jetson-nano-linux-source ``` 3. **配置内核选项** - 进入源码目录: ```bash cd kernel/kernel-4.9 ``` - 复制现有配置: ```bash cp /proc/config.gz . gunzip config.gz mv config .config ``` - 准备编译环境: ```bash make prepare make modules_prepare ``` 4. **编写测试模块** - 创建`hello_module.c`: ```c #include <linux/init.h> #include <linux/module.h> MODULE_LICENSE("GPL"); static int __init hello_init(void) { printk(KERN_INFO "Hello NVIDIA Jetson!\n"); return 0; } static void __exit hello_exit(void) { printk(KERN_INFO "Goodbye NVIDIA Jetson!\n"); } module_init(hello_init); module_exit(hello_exit); ``` - 创建`Makefile`: ```makefile obj-m += hello_module.o KERNEL_SRC := /path/to/kernel/source PWD := $(shell pwd) all: make -C $(KERNEL_SRC) M=$(PWD) modules clean: make -C $(KERNEL_SRC) M=$(PWD) clean ``` 5. **编译模块** ```bash make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- -j4 ``` 参数说明: - `-j4`:使用4个线程加速编译 - 必须指定正确的架构和交叉编译工具链 6. **安装模块** - 加载模块: ```bash sudo insmod hello_module.ko ``` - 验证输出: ```bash dmesg | tail -n 1 ``` 应显示`Hello NVIDIA Jetson!` - 卸载模块: ```bash sudo rmmod hello_module ``` **常见问题处理**: 1. **版本不匹配**: ```bash sudo modprobe hello_module # 若出现"version magic"错误 # 需在.config中设置CONFIG_MODVERSIONS=y make menuconfig # 图形界面配置 ``` 2. **交叉编译环境配置**: - 安装工具链: ```bash sudo apt install gcc-aarch64-linux-gnu ``` - 编译命令需指定: ```bash make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- ``` 3. **内核头文件缺失**: ```bash sudo apt install linux-headers-$(uname -r) ``` **开发建议**: 1. 使用`journalctl -k`实时查看内核日志 2. 复杂模块建议使用`dkms`动态内核模块支持 3. 调试时可通过`printk`分级输出: ```c printk(KERN_DEBUG "Debug message"); ``` 请特别注意:内核模块开发具有较高风险,错误操作可能导致系统不稳定,建议在重要操作前备份数据。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

底层开发智库

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值