技术知识库

交叉编译并加载 Linux 5.0 ARM64 Hello World 模块

交叉编译并加载 Linux 5.0 ARM64 Hello World 模块

摘要

本实验在 x86_64 主机上交叉编译 ARM64 外部模块,并在运行 Linux 5.0.0+ 的 QEMU Debian 12 中完成检查、加载和卸载。模块架构及 vermagic 均与目标系统匹配,加载时打印 hello world, i am benshushu,卸载时打印 goodbye,实验通过。

目标与源码

  • 源码目录:kmodules/rlk_lab/rlk_basic/chapter_1_quick_start/lab3_hello_world
  • 目标内核构建目录:/home/martin-zou/workspace/runninglinuxkernel_5.0/build/arm64
  • 目标系统:QEMU ARM64、Linux 5.0.0+、Debian 12

首次失败:误用主机内核构建目录

Makefile 默认值为:

BASEINCLUDE ?= /lib/modules/`uname -r`/build

这里的 uname -r 在主机执行,得到主机的 x86_64 Ubuntu 内核,而不是 QEMU 中的 ARM64 Linux 5.0。于是 x86 Kbuild 生成的编译选项被传给 ARM64 GCC,最终报错:

The kernel was built by: x86_64-linux-gnu-gcc
You are using:           aarch64-linux-gnu-gcc
aarch64-linux-gnu-gcc: error: unrecognized command-line option ‘-mrecord-mcount’

这不是交叉编译器版本差异,而是选错了内核构建目录和目标架构的 Kbuild 上下文。

正确交叉编译

cd /home/martin-zou/workspace/runninglinuxkernel_5.0/kmodules/rlk_lab/rlk_basic/chapter_1_quick_start/lab3_hello_world

make -s \
  BASEINCLUDE=/home/martin-zou/workspace/runninglinuxkernel_5.0/build/arm64 \
  ARCH=arm64 \
  CROSS_COMPILE=aarch64-linux-gnu-

检查并复制到正在运行的 Debian 虚拟机:

file hello-world.ko
modinfo hello-world.ko | grep -E '^(license|description|author|vermagic):'
scp -P 2222 hello-world.ko martin@127.0.0.1:/home/martin/

生成模块 hello-world.ko。将其复制到目标机后检查:

ELF 64-bit LSB relocatable, ARM aarch64
license:        GPL
author:         rlk
description:    hello world module
vermagic:       5.0.0+ SMP mod_unload modversions aarch64
uname -r:       5.0.0+

vermagic 与目标内核 release、SMP、模块卸载、符号版本和架构属性相容。

加载验证

file ~/hello-world.ko
sudo modinfo ~/hello-world.ko | grep -E '^(license|description|author|vermagic):'
uname -r
sudo insmod ~/hello-world.ko
echo "insmod_status=$?"
lsmod | grep hello
sudo dmesg | tail -n 10

结果:

insmod_status=0
hello_world  16384  0
hello_world: loading out-of-tree module taints kernel.
hello world, i am benshushu

文件名使用连字符 hello-world.ko,模块在 lsmod 中显示为标识符形式 hello_world。

卸载验证

sudo rmmod hello_world
echo "rmmod_status=$?"
lsmod | grep hello || echo "module unloaded"
sudo dmesg | tail -n 10

结果:

rmmod_status=0
module unloaded
goodbye

这证明模块退出函数被执行,模块也已从已加载模块列表移除。

tainted 值为什么是 12288

/proc/sys/kernel/tainted 返回十进制位掩码:

12288 = 4096 + 8192 = 2^12 + 2^13
  • bit 12,O:加载了 out-of-tree 模块。
  • bit 13,E:加载了未签名模块。

模块有 GPL license,因此不是 proprietary 模块导致的 P 标志。taint 是对本次内核运行历史的记录,卸载模块后不会清零;重启才会重新开始。

证据与来源

  • Linux 版本:Running Linux Kernel 5.0,5.0.0+
  • kernel/module.c:外部模块加载时设置 TAINT_OOT_MODULE
  • kernel/panic.c:taint 字符映射中 O 对应 out-of-tree,E 对应 unsigned module
  • 实验输出:insmod_status=0、rmmod_status=0、hello world 与 goodbye

关联