Kernel self-patching
- title
- Kernel self-patching
- type
- concept
- summary
- Three Linux runtime-rewriting mechanisms โ jump_label, static_call, alternative_instructions โ that let one vmlinux boot optimally on dozens of CPU generations
- tags
- linux, kernel, cpu-features, performance
- created
- 2026-05-14
- updated
- 2026-05-14
The Linux kernel rewrites its own text segment at boot to specialize a generic binary for the specific machine it landed on. Three mechanisms cooperate, each addressing a different cost:
jump_label โ feature flags for free
A static_branch_unlikely(&some_key) looks like if (some_flag) in source, but the compiler emits a NOP (or a JMP) at that site and registers a patch entry. jump_label_init() walks the registered table at boot and rewrites every site according to whether the key is currently enabled.
A disabled feature costs zero CPU cycles in hot paths โ no register read, no branch predictor entry, no cache miss. Switching the flag re-patches every site. The kernel uses this for tracing hooks, perf events, debug instrumentation, and anything else that wants to be invisible when off.
static_call โ direct calls instead of indirect
static_call(func)(args) looks like an indirect call but gets patched at boot into a direct call to the currently-bound function. Faster than a function pointer, no Spectre/Meltdown speculative-execution concern. Scheduler dispatch, KVM, tracepoints all use it.
alternative_instructions โ per-CPU instruction selection
The boldest of the three. The kernel ships with ALTERNATIVE macro sites that describe two (or more) variants of the same code โ for example, an AVX-512 memcpy and a fallback. alternative_instructions() runs in Phase 4 after CPUID has discovered the actual feature set, then patches every ALTERNATIVE site to keep only the variant that fits this CPU.
The same vmlinux Debian ships boots optimally on every x86_64 chip Debian supports: the boot-time patching produces a binary tailored to AVX-512 on a Zen 5, to plain SSE on a 15-year-old Atom, to the Spectre-mitigation flavor the chip needs, all from one source.
Why three separate mechanisms
They patch the same address space but solve different problems:
| Mechanism | Patches | Decides at | Cost when "off" |
|---|---|---|---|
jump_label |
NOP / JMP | runtime, mutable | zero |
static_call |
call target | runtime, mutable | direct-call cost |
alternative_instructions |
instruction sequence | boot, immutable | fallback variant runs |
jump_label and static_call can be flipped at runtime. alternative_instructions runs once at boot โ once you've discarded the AVX-512 path on a chip that doesn't have it, you can't get it back without rebooting.
Prerequisites
Self-patching needs poking_init() (the machinery that lets the kernel safely rewrite its own code despite rodata and W^X), which is why mm_core_init() brings it up early. And it needs to happen before mark_readonly() in Phase 6 โ once rodata is locked at the page-table level, the patching window closes.
Cross-references
- linux-kernel-startup โ where these three mechanisms run in the boot sequence
- linux-boot-phases โ the "lock the doors last" rule that depends on patching happening first