kernel/module 内核模块(Loadable Kernel Module)机制与原理详解

kernel/module 内核模块(Loadable Kernel Module)机制与原理详解

源码路径rk3588/kernel-6.1/kernel/module/
内核版本:Linux 6.1(RK3588 平台)
平台:RK3588(4×Cortex-A76 + 4×Cortex-A55 big.LITTLE,ARM64)

该目录实现 Linux 可加载内核模块(LKM) 的核心运行时:模块的加载、重定位、符号解析、初始化/卸载、权限与安全校验,以及 sysfs/proc 管理接口。驱动、文件系统、网络协议栈等大量功能以 .ko 模块形式动态插入内核。

定位:连接用户态 insmod/modprobe/rmmod 与内核 ELF 加载器的桥梁;架构相关重定位在 arch/arm64/kernel/module.c,通用逻辑在本目录。


目录


一、源码目录结构

1.1 编译依赖(Makefile)

1
2
3
4
5
6
7
8
9
10
11
12
13
obj-y += main.o strict_rwx.o

obj-$(CONFIG_MODULE_DECOMPRESS) += decompress.o
obj-$(CONFIG_MODULE_SIG) += signing.o
obj-$(CONFIG_LIVEPATCH) += livepatch.o
obj-$(CONFIG_MODULES_TREE_LOOKUP) += tree_lookup.o
obj-$(CONFIG_DEBUG_KMEMLEAK) += debug_kmemleak.o
obj-$(CONFIG_KALLSYMS) += kallsyms.o
obj-$(CONFIG_PROC_FS) += procfs.o
obj-$(CONFIG_SYSFS) += sysfs.o
obj-$(CONFIG_KGDB_KDB) += kdb.o
obj-$(CONFIG_MODVERSIONS) += version.o
obj-$(CONFIG_MODULE_UNLOAD_TAINT_TRACKING)+= tracking.o
配置项 说明
CONFIG_MODULES 启用可加载模块支持(总开关)
CONFIG_MODVERSIONS 符号 CRC 版本校验
CONFIG_MODULE_SIG 模块 PKCS#7 签名验证
CONFIG_MODULE_SIG_FORCE 拒绝未签名模块
CONFIG_STRICT_MODULE_RWX 模块 text/ro 只读、data 不可执行
CONFIG_MODULE_UNLOAD 允许卸载模块
CONFIG_MODULE_DECOMPRESS 内核内解压 .ko.gz/.ko.xz
CONFIG_MODULES_TREE_LOOKUP latch tree 加速 __module_address()
CONFIG_TRIM_UNUSED_KSYMS 裁剪未使用导出符号(与 livepatch 互斥)

Kconfig 位于 kernel/module/Kconfig

1.2 源文件

文件 行数 功能
main.c 3149 核心:加载/卸载/符号/系统调用/通知链
kallsyms.c 532 模块符号表构建与查询
sysfs.c 436 /sys/module/<name>/ 属性与参数
decompress.c 276 GZIP/XZ 模块解压
internal.h 305 内部 API、load_info、mod_tree
strict_rwx.c 143 模块内存 RWX 权限控制
procfs.c 146 /proc/modules
tracking.c 132 已卸载 taint 模块追踪
signing.c 125 签名验证
tree_lookup.c 117 latch tree 地址→模块查找
version.c 101 modversion CRC 校验
livepatch.c 74 livepatch 模块 ELF 持久化
kdb.c 62 KGDB/KDB 模块支持
debug_kmemleak.c 30 kmemleak 模块区域注册

架构相关(目录外)

文件 功能
arch/arm64/kernel/module.c module_alloc、AArch64 ELF 重定位
arch/arm64/kernel/kaslr.c 模块区域 KASLR 随机化
include/linux/module.h struct module、公共 API
include/linux/moduleloader.h 架构 loader 接口
kernel/module/ 外:kernel/kmod.c 内核侧 request_module

二、整体架构

2.1 用户态到内核态路径

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
用户态                          内核态
────── ──────
insmod foo.ko args
→ init_module() ──────→ copy_module_from_user()
modprobe foo load_module()
→ do_init_module()
→ mod->init()

modprobe -r foo
→ delete_module() ──────→ mod->exit() → free_module()

finit_module(fd, args, flags)
→ kernel_read_file_from_fd()
→ [module_decompress()]
→ load_module()

2.2 模块与子系统关系

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
┌─────────────────────────────────────────────────────────────┐
│ 用户态: insmod / modprobe / rmmod │
└──────────────────────────┬──────────────────────────────────┘
│ init_module / finit_module / delete_module

┌─────────────────────────────────────────────────────────────┐
│ kernel/module/main.c │
│ load_module() → ELF 解析 → 重定位 → 符号 → init │
└──────┬──────────────┬──────────────┬────────────────────────┘
│ │ │
▼ ▼ ▼
signing.c strict_rwx.c kallsyms.c
version.c decompress.c sysfs.c / procfs.c
│ │ │
▼ ▼ ▼
arch/arm64/ ftrace/klp/ /sys/module/
kernel/module.c jump_label /proc/modules

2.3 全局同步

1
2
3
DEFINE_MUTEX(module_mutex);   /* 保护 modules 链表、mod_tree、module_use */
LIST_HEAD(modules); /* 所有已注册模块(RCU 遍历) */
struct mod_tree_root mod_tree; /* 地址范围索引 */

RCU 读路径find_symbol()__module_address() 可在 preempt_disable() 或持 module_mutex 下无锁遍历。


三、核心数据结构

3.1 struct module(include/linux/module.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
struct module {
enum module_state state;
struct list_head list; /* 全局 modules 链表 */
char name[MODULE_NAME_LEN];

struct module_layout core_layout; /* 常驻:text + ro + rw */
struct module_layout init_layout; /* 初始化后可释放 */
struct module_layout data_layout; /* 可选:vmalloc 数据区 */

struct module *init; /* 入口函数 */
void (*exit)(void); /* 卸载函数 */

const struct kernel_symbol *syms, *gpl_syms;
unsigned int num_syms, num_gpl_syms;
const s32 *crcs, *gpl_crcs;

atomic_t refcnt; /* MODULE_REF_BASE 偏移 */
struct list_head source_list; /* 本模块依赖的模块 (module_use) */
struct list_head target_list; /* 依赖本模块的模块 */

struct module_kobject mkobj; /* sysfs kobject */
struct mod_tree_node mtn; /* latch tree 节点 */
/* ftrace, jump_label, tracepoints, params, taints, klp_info ... */
};

3.2 struct module_layout

1
2
3
4
5
6
7
8
struct module_layout {
void *base;
unsigned int size;
unsigned int text_size; /* 可执行段 */
unsigned int ro_size; /* text + ro-data */
unsigned int ro_after_init_size; /* + ro-after-init */
struct mod_tree_node mtn;
};

内存分区CONFIG_STRICT_MODULE_RWX):

1
[ text (X) | ro-data (R) | ro-after-init (R) | writable data (RW, NX) ]

3.3 struct load_info(internal.h

加载过程中的临时上下文:

1
2
3
4
5
6
7
8
9
10
11
struct load_info {
const char *name;
struct module *mod;
Elf_Ehdr *hdr;
unsigned long len;
Elf_Shdr *sechdrs;
char *secstrings, *strtab;
struct _ddebug_info dyndbg;
bool sig_ok;
struct { unsigned int sym, str, mod, vers, info, pcpu; } index;
};

四、模块状态机

1
2
3
4
5
6
enum module_state {
MODULE_STATE_LIVE, /* 正常运行 */
MODULE_STATE_COMING, /* 已形成,正在 module_init */
MODULE_STATE_GOING, /* 正在卸载 */
MODULE_STATE_UNFORMED, /* 加载中,尚未完成 formation */
};
状态 含义 可见性
UNFORMED ELF 解析/重定位中 仅在 modules 链表,kallsyms 不可见
COMING 符号已就绪,init 未返回 kallsyms 可见,try_module_get 失败
LIVE init 成功 完全可用
GOING exit 已调用或加载失败 正在 tear down

状态迁移:

1
2
3
4
UNFORMED → complete_formation() → COMING
COMING → do_init_module() 成功 → LIVE
LIVE → delete_module() → GOING → free_module()
任意 → 加载失败路径 → GOING → free_module()

五、加载流程(load_module)

load_module() 是加载核心,由 init_module / finit_module 系统调用触发。

5.1 主要步骤

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
1.  module_sig_check()           ← 签名验证,裁剪 sig 长度
2. elf_validity_check() ← ELF 头/段合法性
3. setup_load_info() ← 解析 section 索引
4. blacklist / rewrite_section_headers()
5. check_modstruct_version() ← module_layout CRC
6. layout_and_allocate() ← layout_sections + move_module
7. add_unformed_module() ← 加入 modules 链表 (UNFORMED)
8. percpu_modalloc()
9. find_module_sections() ← __ksymtab, __param, ftrace, jump_label...
10. check_module_license_and_versions()
11. setup_modinfo()
12. simplify_symbols() ← 解析未定义符号
13. apply_relocations() ← 架构重定位
14. post_relocation() ← extable排序, percpu, kallsyms, module_finalize
15. flush_module_icache()
16. complete_formation() → COMING
17. prepare_coming_module() ← ftrace, klp_module_coming, notifier
18. parse_args() ← 模块参数
19. mod_sysfs_setup()
20. do_init_module() → LIVE

5.2 layout_sections — 段布局

按优先级分配 core / init / data 区域:

轮次 段属性 目标 layout
0 SHF_EXECINSTR | SHF_ALLOC core text
1 SHF_ALLOC(非 WRITE) data ro
2 SHF_RO_AFTER_INIT data ro-after-init
3 SHF_WRITE | SHF_ALLOC data writable
init 段 .init*, __init* init_layout(带 INIT_OFFSET_MASK

5.3 move_module — 内存拷贝

1
2
3
4
mod->core_layout.base = module_alloc(core_size);   /* 架构实现 */
mod->init_layout.base = module_alloc(init_size);
/* ARCH_WANTS_MODULES_DATA_IN_VMALLOC: data 用 vzalloc */
/* 逐 SHF_ALLOC 段 memcpy 到最终地址 */

5.4 find_module_sections — 特殊段

解析并记录模块内各类元数据段:

段名 用途
__ksymtab / __ksymtab_gpl 模块导出符号
__param 模块参数
__ex_table 异常表
__jump_table jump label
__tracepoints_ptrs tracepoint
__dyndbg 动态 debug
.kprobes.text kprobes
.static_call_sites static call
.klp.rela.* livepatch 重定位

六、初始化与卸载

6.1 do_init_module()

1
2
3
4
5
6
7
8
9
10
11
12
do_mod_ctors(mod);                    /* .ctors / .init_array */
ret = mod->init(); /* 模块入口 */
mod->state = MODULE_STATE_LIVE;
blocking_notifier_call_chain(..., MODULE_STATE_LIVE, mod);
kobject_uevent(..., KOBJ_ADD); /* uevent 通知 udev */

/* init 段释放 */
ftrace_free_mem(mod, init_base, init_end);
rcu_assign_pointer(mod->kallsyms, &mod->core_kallsyms);
module_enable_ro(mod, true); /* ro-after-init 生效 */
mod_tree_remove_init(mod);
schedule_work(&init_free_wq); /* 异步释放 init 内存 */

init 段延迟释放:kallsyms 可能在 RCU 读路径遍历 init 区,故通过 workqueue + synchronize_rcu() 异步 module_memfree()

6.2 delete_module 系统调用

1
2
3
4
5
6
7
8
9
1. CAP_SYS_MODULE + !modules_disabled
2. find_module(name)
3. 检查 source_list 非空 → -EWOULDBLOCK(有被依赖)
4. state == LIVE
5. 有 init 无 exit → 需 force(-f)
6. try_stop_module() → refcnt 归零 → GOING
7. mod->exit()
8. notifier GOING + klp_module_going + ftrace_release_mod
9. free_module()

6.3 引用计数

1
2
3
4
atomic_t refcnt;   /* 加载时 +MODULE_REF_BASE */
try_module_get() /* LIVE 且 atomic_inc_not_zero */
module_put() /* atomic_dec_if_positive */
ref_module() /* 符号解析时建立 module_use 依赖 */

module_use 链表:模块 A 使用模块 B 的符号 → A.source_listB,卸载 B 前须先卸载所有依赖 A。


七、符号解析与版本校验

7.1 find_symbol()

1
2
3
4
5
bool find_symbol(struct find_symbol_arg *fsa)
{
/* 1. 内核 __ksymtab / __ksymtab_gpl(bsearch) */
/* 2. 遍历 modules 链表中各 mod->syms / gpl_syms */
}

搜索顺序:内核普通导出 → 内核 GPL 导出 → 各已形成模块的普通 → GPL。

7.2 resolve_symbol()

1
2
3
4
5
6
find_symbol(name)
→ GPL 许可检查(proprietary 模块不可用 GPL-only 符号)
→ inherit_taint(专有模块 taint 继承)
→ check_version(CRC) ← version.c
→ verify_namespace_is_imported ← EXPORT_SYMBOL_NS
→ ref_module(mod, owner) ← 建立依赖

7.3 modversion(version.c)

1
2
3
4
5
6
int check_version(info, symname, mod, crc)
{
/* 在 .modversions 段查找 symname 的 CRC */
/* 匹配 → OK;不匹配 → 拒绝加载 */
/* versindex==0 → try_to_force_load()(--force) */
}

module_layout 符号 CRC 校验 struct module 布局兼容性。

7.4 vermagic

1
2
static const char vermagic[] = VERMAGIC_STRING;
/* modinfo 中 vermagic 须匹配;有 CRC 时仅比较 SMP/preempt 等后缀 */

八、内存布局与 RWX 保护(strict_rwx.c)

CONFIG_STRICT_MODULE_RWX 启用时:

区域 权限
text RO + X(via module_enable_x
ro-data / ro-after-init RO + NX
writable data RW + NX
1
2
3
4
5
6
7
8
9
10
void module_enable_ro(mod, after_init)
{
frob_text(..., set_memory_ro); /* text 变只读 */
frob_rodata(..., set_memory_ro);
if (after_init)
frob_ro_after_init(..., set_memory_ro); /* init 完成后 */
}

void module_enable_nx(mod) /* data 段不可执行 */
void module_enable_x(mod) /* text 段可执行 */

module_enforce_rwx_sections() 拒绝同时具有 SHF_WRITE | SHF_EXECINSTR 的段。


九、模块签名(signing.c)

1
模块末尾: [ ELF 内容 | PKCS#7 签名 | module_signature 结构 | "~Module signature appended~\n" ]

流程:

1
2
3
4
5
6
7
module_sig_check(info, flags)
→ 检测 MODULE_SIG_STRING 标记
→ mod_verify_sig() → verify_pkcs7_signature()
→ sig_ok = true / taint TAINT_UNSIGNED_MODULE

is_module_sig_enforced() ← CONFIG_MODULE_SIG_FORCE 或 cmdline
→ 未签名/无效签名 → -EKEYREJECTED

Lockdown LSM 联动:lockdown 模式下未签名模块可能被拒绝。


十、模块解压(decompress.c)

finit_module(..., MODULE_INIT_COMPRESSED_FILE) 或内核内置解压:

格式 实现
GZIP (.ko.gz) zlib_inflateInit2 + 逐页输出
XZ (.ko.xz) xz_dec_run

解压到 load_info.pages[] 页数组,供后续 ELF 解析。用于 load pinning 等需内核自行读模块的场景。


十一、Kallsyms 与地址查找

11.1 kallsyms.c

  • layout_symtab() — 将符号表移入 init 段布局
  • add_kallsyms() — 构建 mod->core_kallsyms(symtab + strtab)
  • init 完成后切换:rcu_assign_pointer(mod->kallsyms, &mod->core_kallsyms)

11.2 地址→模块查找

tree_lookup.cCONFIG_MODULES_TREE_LOOKUP,PERF/TRACING 启用):

1
2
3
4
5
/* latch tree,RCU-sched 安全 */
struct module *mod_find(unsigned long addr, struct mod_tree_root *tree)
{
ltn = latch_tree_find(addr, &mod_tree.root, &mod_tree_ops);
}

否则线性扫描 modules 链表 + within_module()

1
2
struct module *__module_address(unsigned long addr)
struct module *__module_text_address(unsigned long addr) /* 仅 text 段 */

十二、Sysfs 与 Procfs 接口

12.1 Sysfs(sysfs.c)

1
2
3
4
5
6
7
8
9
10
11
/sys/module/<name>/
├── initstate (RO) Live/Loading/Unloading
├── coresize (RO)
├── initsize (RO)
├── refcnt (RO) [CONFIG_MODULE_UNLOAD]
├── taint (RO)
├── version (RO)
├── parameters/ (RW) 模块参数
├── sections/ (RO) 各段地址 [CONFIG_KALLSYMS]
├── holders/ (RO) 依赖本模块的模块
└── notes/ (RO) ELF notes

12.2 Procfs(procfs.c)

/proc/modules 格式:

1
name size refcnt deps address [taint_flags]

例:nvme 123456 0 - Live 0xffffffc008000000


十三、Livepatch 集成(livepatch.c)

livepatch 补丁模块加载时:

1
2
3
copy_module_elf(mod, info)
→ 持久化 Elf header、section headers、secstrings
→ symtab 指向 core_kallsyms.symtab(init 释放后仍可用)

配合 kernel/livepatch/core.cklp_apply_section_relocs() 解析 .klp.rela.* 段。


十四、RK3588/ARM64 平台说明

14.1 module_alloc(arch/arm64/kernel/module.c

1
2
3
4
5
6
7
8
9
void *module_alloc(unsigned long size)
{
p = __vmalloc_node_range(size, MODULE_ALIGN,
module_alloc_base,
module_alloc_base + MODULES_VSIZE,
GFP_KERNEL, PAGE_KERNEL, ...);
/* KASAN shadow 分配 */
return kasan_reset_tag(p);
}
  • 模块加载于 vmalloc 模块区域_etext 下方 MODULES_VSIZE
  • KASLRkaslr.c 随机化 module_alloc_base(21-bit 偏移)
  • MODULE PLT:静态区域耗尽时 fallback 到 2GB 窗口
  • ARCH_HAS_STRICT_MODULE_RWX:ARM64 支持严格 RWX

14.2 AArch64 重定位

arch/arm64/kernel/module.c 处理 ELF 重定位类型:

  • R_AARCH64_ABS64 / PREL32 / CALL26 / JUMP26
  • R_AARCH64_ADR_PREL_PG_HI21 等页相对重定位
  • alternativesftracestatic_call 模块段处理
  • CFI 影子类型校验(module_cfi_finalize

14.3 RK3588 驱动开发注意点

  1. 内核与模块须 相同 vermagic 或启用 CONFIG_MODVERSIONS 且 CRC 匹配
  2. 启用 CONFIG_MODULE_SIG_FORCE 时须用 scripts/sign-file 签名
  3. 树外模块编译:make modules + make modules_install;依赖 Kbuild 导出符号
  4. EXPORT_SYMBOL_NS() 须对应 MODULE_IMPORT_NS(),否则加载失败
  5. 卸载驱动前确认 refcnt=0 且无 holders(lsmod / /sys/module/.../holders/

14.4 架构 Kconfig

配置 ARM64
ARCH_USE_QUEUED_SPINLOCKS
ARCH_HAS_STRICT_MODULE_RWX ✅ select
ARCH_SUPPORTS_ATOMIC_RMW
MODULES_TREE_LOOKUP def_y(PERF/TRACING 时)

十五、完整加载/卸载时序

15.1 加载时序

arch/module.cdo_init_moduleload_module系统调用用户态arch/module.cdo_init_moduleload_module系统调用用户态finit_module(fd, args, 0)kernel_read_file + load_modulesig_check, elf_validity, layoutapply_relocationssimplify_symbols, post_relocationcomplete_formation (COMING)klp_module_coming, parse_argsdo_init_modulemod->init()state=LIVE, free init, module_enable_ro0

15.2 卸载时序

1
2
3
4
5
6
delete_module("foo")
→ try_stop_module: refcnt==0 → GOING
→ mod->exit()
→ klp_module_going, ftrace_release_mod
→ free_module: sysfs teardown, module_memfree, 从 modules 移除
→ wake_up(module_wq)

15.3 通知链

1
2
register_module_notifier()
blocking_notifier_call_chain(&module_notify_list, state, mod)

通知时机:COMING(prepare)、LIVE(init 成功)、GOING(卸载/失败)。


十六、总结

组件 职责
main.c 加载/卸载主流程、系统调用、符号解析、依赖管理
strict_rwx.c 模块内存 W^X 安全
signing.c PKCS#7 签名验证
version.c modversion CRC 兼容性
kallsyms.c 模块符号表
tree_lookup.c O(log n) 地址→模块
sysfs.c / procfs.c 用户态管理接口
decompress.c 压缩模块解压
livepatch.c 热补丁 ELF 持久化
tracking.c 卸载 taint 模块审计

内核模块机制将 ELF 对象 安全地映射进运行中内核:通过 vermagic/modversion 保证 ABI 兼容,通过签名与 RWX 保护增强安全,通过 refcnt 与 module_use 保证卸载安全。RK3588 驱动开发几乎完全依赖此框架。


附录:源文件清单

文件 行数
kernel/module/main.c 3149
kernel/module/kallsyms.c 532
kernel/module/sysfs.c 436
kernel/module/decompress.c 276
kernel/module/internal.h 305
kernel/module/strict_rwx.c 143
kernel/module/procfs.c 146
kernel/module/tracking.c 132
kernel/module/signing.c 125
kernel/module/tree_lookup.c 117
kernel/module/version.c 101
kernel/module/livepatch.c 74
kernel/module/kdb.c 62
kernel/module/debug_kmemleak.c 30
kernel/module/Kconfig 293
kernel/module/Makefile 21
合计 ~5628

关联源码(目录外)

文件 行数(约) 说明
arch/arm64/kernel/module.c 519 AArch64 重定位与 module_alloc
arch/arm64/kernel/kaslr.c 模块区域 KASLR
include/linux/module.h struct module 定义
kernel/kmod.c request_module 内核接口
scripts/mod/modpost.c 构建时 modpost 处理

文章互动

阅读 --

留言

0 条留言

正在加载留言…