Map

Kernel self-patching

Wiki conceptlinuxkernelcpu-featuresperformance โ†ณ show in map Markdown
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