Conversation
Contributor
Author
|
I've tested it and it works fine! |
Contributor
Author
Give the KPM loader the semantics of the kernel's own module loader. Symbol resolution - One resolution pipeline for both backends: KP runtime table -> cross-KPM exports (.kpm.export) -> kf_/kv_ pointer slots -> kernel kallsyms. A module-local PLT/GOT arena keeps out-of-range AArch64 branches (JUMP26/CALL26 and GOT-style relocs) reachable, and an unresolvable symbol now fails the load with the offending name instead of breaking silently at first call. Cross-module exports - KPM_EXPORT(sym), the EXPORT_SYMBOL equivalent: the entry is emitted into the allocatable .kpm.export section, later KPMs reference it with a plain extern, and the loader resolves it by name. Export entries are sanity-checked in setup_load_info and recorded in move_module. Dependencies - Dependencies are tracked like the kernel records them: a module whose exports are still imported refuses to unload (-EBUSY), an importer releases its providers when it goes away, and a failed load rolls back every reference it took. A full dependency table fails the load rather than succeeding silently, which would let a provider be freed under a live consumer. Robustness - ELF input is validated before use: bounded shstrtab/symtab/name checks, COMMON and LTO symbols rejected, 64-bit-safe relocation overflow masks, and REL sections rejected instead of quietly ignored. - Locking uses the KP-owned spinlock helper (kp_private_spin_lock) so the module list and export tables are consistent across CPUs. - Version-independent: 0.13.x KPMs load unchanged on both backends. Tests - checks/test_kpm.c covers the link arena, the resolution pipeline and the dependency policy on the host. - checks/symbol_compat.py is the cross-version gate: it resolves every symbol referenced by the shipped demos (and any older KPMs given on the command line) against both backends' export tables. - kpms/demo-exports and kpms/demo-import demonstrate the provider and importer sides end to end.
JavSaia
force-pushed
the
feat/kpm-symbol-system
branch
from
October 1, 2026 11:28
d92eee9 to
b526a66
Compare
Contributor
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
Dependency reference leaks, unload races, incomplete ELF bounds checks, and compatibility-gate false results must be corrected.
Review effort: Balanced
Findings: 5
Open (10)
Validate ELF section bounds without overflow before accessing data · New Protect dependency removal and export references with consistent locking · New Use IRQ-safe locking consistently for module_lock acquisitions · New Use overflow-safe bounds checks before scanning ELF sections · New Synchronize dependency removal with export lookup and reference updates · New Fail the gate when no demo KPM inputs are discovered · New Derive providers from KPM exports instead of global symbols · New Avoid incrementing provider references for existing dependency edges · New Update the root version source for the 0.14.0 release · New Increment provider references only for newly recorded dependencies · New
What changed in this PR
Adds LKM-style cross-KPM symbol exports, dependency tracking, and safer AArch64 relocation handling across both loader backends.
Changes:
- Introduces export resolution, dependency accounting, and PLT/GOT link arenas.
- Strengthens ELF validation and adds compatibility symbols.
- Adds demonstrations, documentation, and host compatibility tests.
| File | Description |
|---|---|
lkm/kpm/symbols.c |
Adds legacy ABI compatibility symbols. |
lkm/kpm/relo.c |
Supports PLT/GOT relocations and rejects REL sections. |
lkm/kpm/module.h |
Stores link, export, and dependency state. |
lkm/kpm/module.c |
Implements LKM-backend loading and dependency logic. |
lkm/Kbuild |
Changes fallback version values. |
kpms/demo-import/Makefile |
Builds the importer demonstration. |
kpms/demo-import/import.lds |
Defines importer linker sections. |
kpms/demo-import/import.c |
Demonstrates consuming an exported symbol. |
kpms/demo-exports/Makefile |
Builds the provider demonstration. |
kpms/demo-exports/exports.lds |
Defines provider linker sections. |
kpms/demo-exports/exports.c |
Demonstrates exporting a symbol. |
kernel/patch/module/relo.c |
Adds kpimg PLT/GOT relocation support. |
kernel/patch/module/module.c |
Implements kpimg exports and dependencies. |
kernel/patch/include/module.h |
Extends kpimg module state. |
kernel/include/kpmsymbol.h |
Defines the shared resolution pipeline. |
kernel/include/kpmodule.h |
Adds the public KPM_EXPORT API. |
kernel/include/kpmlink.h |
Implements the per-module link arena. |
kernel/include/kpmdep.h |
Provides dependency-table helpers. |
doc/zh-CN/module.md |
Documents exports in Chinese. |
doc/en/module.md |
Documents exports in English. |
checks/test_kpm.c |
Tests linking, resolution, and dependency helpers. |
checks/symbol_compat.py |
Checks symbol compatibility across backends. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Comment on lines
+547
to
+556
| Elf_Shdr *symbols = &info->sechdrs[info->index.sym]; | ||
| if (symbols->sh_size % sizeof(Elf_Sym) || symbols->sh_link >= info->hdr->e_shnum) { | ||
| set_load_error(info, "invalid ELF symbol table"); | ||
| return -ENOEXEC; | ||
| } | ||
| Elf_Shdr *strings = &info->sechdrs[symbols->sh_link]; | ||
| if (strings->sh_type != SHT_STRTAB || !strings->sh_size) { | ||
| set_load_error(info, "invalid ELF symbol strings"); | ||
| return -ENOEXEC; | ||
| } |
Comment on lines
+698
to
+702
| if (mod->export_refs) { | ||
| /* LKM rmmod semantics: refuse while other KPMs still import us. */ | ||
| logkfe("module %s is in use by %u other KPM(s)\n", name, mod->export_refs); | ||
| rc = -EBUSY; | ||
| goto out; |
| unsigned long found = 0; | ||
|
|
||
| if (!importer) return 0; | ||
| spin_lock(&module_lock); |
Comment on lines
+705
to
+714
| Elf_Shdr *symbols = &info->sechdrs[info->index.sym]; | ||
| if (symbols->sh_size % sizeof(Elf_Sym) || symbols->sh_link >= info->hdr->e_shnum) { | ||
| set_load_error(info, "invalid ELF symbol table"); | ||
| return -ENOEXEC; | ||
| } | ||
| Elf_Shdr *strings = &info->sechdrs[symbols->sh_link]; | ||
| if (strings->sh_type != SHT_STRTAB || !strings->sh_size) { | ||
| set_load_error(info, "invalid ELF symbol strings"); | ||
| return -ENOEXEC; | ||
| } |
Comment on lines
+1013
to
+1017
| if (mod->export_refs) { | ||
| /* LKM rmmod semantics: refuse while other KPMs still import us. */ | ||
| logkfe("module %s is in use by %u other KPM(s)\n", name, mod->export_refs); | ||
| rc = -EBUSY; | ||
| goto out; |
Comment on lines
+80
to
+86
| kpms = {} | ||
| for p in sorted((ROOT / 'kpms').glob('*/*.kpm')): | ||
| kpms[p] = 'current' | ||
| for arg in sys.argv[1:] or ([OLD_DEMOS] if OLD_DEMOS.exists() else []): | ||
| base = Path(arg) | ||
| for p in sorted(base.glob('*.kpm')): | ||
| kpms.setdefault(p, 'previous-generation') |
Comment on lines
+88
to
+90
| providers = set() | ||
| for p in kpms: | ||
| providers |= elf_syms(p, 'global') |
Comment on lines
+223
to
+230
| if (kpm_dep_record((void **)importer->deps, &importer->dep_count, KPM_DEP_MAX, pos) < 0) { | ||
| /* Table full: fail the resolution so the KPM load aborts. | ||
| * Silently succeeding here would allow the provider to be | ||
| * unloaded while the consumer still holds its symbols. */ | ||
| logke("dependency table full; cannot import %s from %s\n", name, pos->info.name); | ||
| goto out; | ||
| } | ||
| pos->export_refs++; |
Comment on lines
+57
to
+60
| KP_LKM_MINOR := 14 | ||
| endif | ||
| ifeq ($(strip $(KP_LKM_PATCH)),) | ||
| KP_LKM_PATCH := 3 | ||
| KP_LKM_PATCH := 0 |
Comment on lines
+388
to
+394
| if (kpm_dep_record((void **)importer->deps, &importer->dep_count, KPM_DEP_MAX, pos) < 0) { | ||
| /* Fail resolution: silently succeeding would allow the | ||
| * provider to be unloaded while consumer still uses it. */ | ||
| logke("dependency table full; cannot import %s from %s\n", name, pos->info.name); | ||
| goto out; | ||
| } | ||
| pos->export_refs++; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.


What
The KPM loader currently lets a module import symbols from the KP runtime and from the running kernel, but not from other KPMs, and it has no notion of a module dependency. This gives it the semantics of the kernel's own module loader.
Symbol resolution
One pipeline for both backends (kpimg and LKM):
.kpm.export)kf_/kv_pointer slotsA module-local PLT/GOT arena keeps out-of-range AArch64 branches reachable —
JUMP26/CALL26and the GOT-style relocations that a real kernel module build emits. An unresolvable symbol now fails the load and names the symbol, instead of breaking silently at the first call.Cross-module exports
KPM_EXPORT(sym)is theEXPORT_SYMBOLequivalent. The entry is emitted into the allocatable.kpm.exportsection, a later KPM references it with a plainextern, and the loader resolves it by name. Export entries are sanity-checked insetup_load_infoand recorded inmove_module.Dependencies
Tracked the way the kernel tracks them:
-EBUSY)The module list and export tables are protected by the KP-owned spinlock helper (
kp_private_spin_lock), which is the correct primitive here: it takes_raw_spin_lock_irqsavewhen the kernel symbol is available and otherwise masks interrupts locally, unlike a barespin_lockin this context.Robustness
ELF input is validated before use:
shstrtab/symtab/name checksCOMMONand LTO symbols rejectedRELsections rejected instead of quietly ignored0.13.x KPMs load unchanged on both backends.
Tests
checks/test_kpm.c— host tests for the link arena, the resolution pipeline and the dependency policy.checks/symbol_compat.py— cross-version gate: resolves every symbol referenced by the shipped demos (and any older KPMs passed on the command line) against both backends' export tables.kpms/demo-exportsandkpms/demo-import— provider and importer sides, end to end.Not covered
Runtime behaviour (load/unload, cross-KPM import,
-EBUSYrefusal) has not been exercised on a device yet.