Skip to content

[RFC] riscv64: add initial vmlinux-only kpatch-build support - #1529

Draft
qiruibd wants to merge 26 commits into
dynup:masterfrom
qiruibd:riscv_Dev
Draft

qiruibd wants to merge 26 commits into
dynup:masterfrom
qiruibd:riscv_Dev

Conversation

@qiruibd

@qiruibd qiruibd commented Oct 6, 2026 •

Copy link
Copy Markdown

This is a Draft/RFC PR to start review of initial riscv64 support for
kpatch-build.

Current scope and limitations

Important

This RFC does not provide full RISC-V feature parity with the other
supported architectures.

The currently supported RISC-V build path has the following limitations:

  • RISC-V patch builds must use --skip-vmlinux-final-link.
    The original and patched kernel trees are built through vmlinux.o and
    modpost, but the final vmlinux link is intentionally skipped.
  • Only source changes which can be verified to affect objects linked into
    vmlinux are supported.
  • Patching in-tree kernel modules is not supported.
  • Patching out-of-tree kernel modules is not supported.
  • --skip-vmlinux-final-link cannot be combined with a custom --target.
  • Patches which touch files other than C source files, or whose changed
    objects cannot be proven to belong to vmlinux, are rejected.
  • Changes which alter vmlinux.symvers are rejected.

Therefore, this series should be understood as initial support for building
livepatch modules which patch functions and data in vmlinux, using the
RISC-V-specific skip-final-link workflow.

It does not claim:

  • general RISC-V support for patching both vmlinux and kernel modules;
  • support for in-tree or out-of-tree kernel module targets;
  • support for the normal RISC-V build path with final original and patched
    vmlinux links.

Support for kernel module targets and the regular full-final-link workflow
remains future work.

Dependency

This PR depends on the corresponding unit-test object PR:

The test/unit/objs submodule currently points to the head commit of that
PR:

a482f758ab231426685ca8cbd1c0d1b557c61d08

The commit is currently available through the upstream repository's
refs/pull/58/head reference.

Once dynup/kpatch-unit-test-objs#58 is merged, I will refresh this PR so
the submodule points to the final commit reachable from the upstream
master branch.

Summary

This series adds initial, vmlinux-only riscv64 support to kpatch-build.

At this stage, RISC-V patch builds use --skip-vmlinux-final-link and
support only changes to objects linked into vmlinux. Kernel module
targets are intentionally rejected. See "Current scope and limitations"
above for the exact supported scope.

The series includes:

  • riscv64 architecture detection and build support;
  • RISC-V ftrace callsite handling;
  • RISC-V mapping symbol and local label handling;
  • RISC-V special section handling for .alternative, __jump_table,
    __bug_table, __ex_table, and related relocations;
  • RISC-V intermediate relocation metadata;
  • RISC-V KLP relocation slots;
  • RISC-V PC-relative address and load handling;
  • RISC-V syscall wrapper macro support;
  • a required RISC-V --skip-vmlinux-final-link build path for
    vmlinux-resident objects;
  • explicit rejection of in-tree and out-of-tree module patch targets in
    this mode;
  • Linux 6.12.95 integration test coverage;
  • riscv64 unit test harness support.

The series also includes a few prerequisite fixes which are useful outside
RISC-V support.

Build workflow

For the currently supported RISC-V configuration, kpatch-build is invoked
with:

--skip-vmlinux-final-link

In this mode, kpatch-build:

  1. builds the original kernel tree through vmlinux.o and modpost;
  2. saves the original vmlinux.symvers;
  3. applies the source patch;
  4. builds the patched tree through vmlinux.o and modpost;
  5. verifies that the changed objects belong to vmlinux;
  6. verifies that the patched vmlinux.symvers is unchanged;
  7. uses vmlinux.symvers as the symbol version source for the external
    livepatch module build.

The final original and patched vmlinux links are not performed.

This workflow is currently limited to vmlinux-resident objects. If the
patch touches an in-tree module, an out-of-tree module, a non-C source
file, or an object which cannot be verified as belonging to vmlinux,
kpatch-build terminates with an error rather than generating a potentially
invalid livepatch module.

Validation

I validated the series with the following checks.

Commit-by-commit build validation

For every commit in the series:

make ARCH=aarch64 all
make check

From the commit which introduces riscv64 architecture support onward:

make ARCH=riscv64 all
make check

Unit tests

On the final tree:

make unit

The unit tests use the riscv64 test objects from:

All six riscv64 unit test cases pass:

  • cmdline-patch
  • data-new
  • gcc-static-local-var-6
  • new-function
  • special-static
  • syscall

Existing unit tests for the other architectures remain passing.

RISC-V integration testing

The supported vmlinux-only Linux 6.12.95 integration test matrix completed
successfully on native riscv64 hardware using
--skip-vmlinux-final-link.

Test environment:

  • distro: Debian GNU/Linux 13 (trixie),
    DEBIAN_VERSION_FULL=13.2
  • architecture: riscv64
  • compiler: GCC 14.2.0, riscv64-linux-gnu,
    Debian 14.2.0-19+byted1
  • linker: GNU ld 2.44
  • kernel: Linux 6.12.95

The integration run covered the newly added Linux 6.12.95 test set and
RISC-V-specific behavior including:

  • syscall wrapper handling;
  • special section handling;
  • static keys;
  • PC-relative relocations and loads;
  • the --skip-vmlinux-final-link workflow;
  • symbol version consistency checking;
  • expected build failures.

This integration coverage validates patches against vmlinux-resident
objects only. It does not validate successful patching of in-tree or
out-of-tree kernel modules, because module patch targets are outside the
current supported scope.

A negative integration test verifies that an in-tree module target is
rejected before the patched build begins.

Non-goals of this RFC

The following are explicitly outside the scope of this RFC:

  • patching functions or data in in-tree kernel modules;
  • patching out-of-tree kernel modules;
  • using custom Kbuild targets with the RISC-V skip-final-link flow;
  • using the normal RISC-V workflow with final original and patched
    vmlinux links;
  • claiming complete RISC-V feature parity with the other supported
    architectures.

These capabilities require additional implementation and validation and
should not be inferred from the architecture and relocation support
introduced by this series.

Follow-up

Before this PR is marked ready for review:

Future work includes:

  • support for patching in-tree kernel modules;
  • support for patching out-of-tree kernel modules;
  • support for the regular full-final-link RISC-V build workflow;
  • additional RISC-V kernel and toolchain coverage.

Notes

This PR is intentionally marked as Draft because:

  • the unit-test object dependency has not yet been merged;
  • the current RISC-V support is intentionally limited to vmlinux-resident
    objects using --skip-vmlinux-final-link;
  • feedback is requested on the RISC-V architecture and relocation design,
    the skip-final-link workflow, and the proposed series structure.

qiruibd added 26 commits October 5, 2026 11:38
The _error_injection_whitelist section contains pointers to functions
annotated with ALLOW_ERROR_INJECTION().  Symbol indices can differ between
original and patched objects even when the content is semantically
unchanged.

Ignore this section to avoid false CHANGED detection.

Signed-off-by: Rui Qi <qirui.001@bytedance.com>
…tion

The string offset into .kpatch.ignore.sections must be computed as
sym->st_value + rela->addend, not just rela->addend.  Using addend alone is
only correct when the relocation references a section symbol (STT_SECTION,
where st_value is 0).  When the relocation references a regular symbol with
a non-zero st_value, the offset is wrong and ignored sections can fail
replacement symbol lookup.

On x86 and ARM64 this happens to work because GCC emits section symbols for
these relocations.  On RISC-V, GCC emits regular symbols with non-zero
st_value, exposing the bug.

This matches the pattern used for .modinfo string lookups in kpatch-elf.c
(kpatch_create_rela_list).

Signed-off-by: Rui Qi <qirui.001@bytedance.com>
Use modinfo -F name when .gnu.linkonce.this_module does not provide a
module name.

This keeps load and unload handling working for modules whose build does
not leave the module name in that section string.

Signed-off-by: Rui Qi <qirui.001@bytedance.com>
Kernels built with CONFIG_PRINTK_CALLER prepend a caller-id prefix to each
dmesg line, so the marker written to /dev/kmsg does not match the rendered
line read back from dmesg --notime.

Capture the actual rendered dmesg line after writing the marker and use it
for exact matching in new_dmesg(). Fall back to suffix matching when
recovering the line so the prefix does not interfere, and fail early if the
marker cannot be recovered from the printk buffer.

Signed-off-by: Rui Qi <qirui.001@bytedance.com>
The LLD variable was unconditionally derived as ${CROSS_COMPILE}ld.lld,
which only exists when the cross-toolchain ships a prefixed ld.lld.  Many
cross-toolchains (and native builds) provide only the unprefixed ld.lld,
so kpatch-build could not locate the linker there.

Allow LLD to be overridden via the environment; otherwise prefer the
prefixed ${CROSS_COMPILE}ld.lld when it exists and fall back to the
unprefixed ld.lld.  This makes cross-builds work on toolchains that do
not ship a prefixed lld.

Signed-off-by: Rui Qi <qirui.001@bytedance.com>
Add RISCV64 ELF architecture handling and make the build system accept
riscv64 targets.

Map kpatch's riscv64 architecture name to the kernel ARCH=riscv value, skip
RISC-V VDSO objects, and pass -fPIC for RISC-V kernel object builds.

Signed-off-by: Rui Qi <qirui.001@bytedance.com>
RISC-V kernels use -fpatchable-function-entry to reserve NOPs at function
entries for ftrace patching.

Detect 2-byte C.NOP and 4-byte regular NOP padding, use the detected
padding for symbol bundling, and create __mcount_loc or
__patchable_function_entries callsite sections for RISC-V functions.

Signed-off-by: Rui Qi <qirui.001@bytedance.com>
RISC-V uses ELF mapping symbols and compiler-generated local labels which
can be renamed or moved between builds.

Ignore unstable local symbols during symbol correlation and normalize
RISC-V local-label relocations in text and function-local .rodata sections
to section symbols.

This also generalizes local-label handling beyond RISC-V.  AArch64 now
ignores .L* local labels (except .LC*, which point to string literals and
are handled by kpatch_include_standard_elements()) during symbol
correlation and section-symbol replacement; previously only $d/$x mapping
symbols were ignored.  LoongArch local-label normalization is now scoped
to text sections and .rodata.*; previously it applied to all sections,
including special sections like __jump_table, __ex_table and .alternative,
where normalizing local labels to section symbols could corrupt field
offsets.  A missing section symbol is now created on demand.

Signed-off-by: Rui Qi <qirui.001@bytedance.com>
The insn_is_load_immediate() helper detects instructions that load an
immediate value into a register, which is how the compiler embeds __LINE__
in WARN() and might_sleep() macros.  When only the line number differs
between patched and original code, kpatch can safely ignore the change as a
false positive.

On RISC-V, __LINE__ can be loaded with a 32-bit addi encoding or with the
16-bit C.li compressed encoding when the C extension is enabled.  Detect
both forms for the syscall argument registers used by the kernel.

Previously RISCV64 fell through with "to be done", causing
kpatch_line_macro_change_only() to return false for RISC-V and produce
false positive CHANGED sections.

Signed-off-by: Rui Qi <qirui.001@bytedance.com>
Add RISCV64 to the generic special section architecture mask and ignore
patchable function entry sections for RISC-V objects.

Signed-off-by: Rui Qi <qirui.001@bytedance.com>
RISC-V uses .alternative instead of the generic .altinstructions section
for alternative instruction patching.

Register it as a special section and teach the build helper to read
alt_entry size data from DWARF so altinstructions_group_size() can process
RISC-V entries.

Signed-off-by: Rui Qi <qirui.001@bytedance.com>
RISC-V jump entries use paired ADD/SUB relocations, producing six
relocations per entry instead of the standard three.

Detect that format, select the code and key relocations from the ADD
entries, and reject unsafe unexported vmlinux keys before they can be
partially converted to livepatch relocations.

Signed-off-by: Rui Qi <qirui.001@bytedance.com>
RISC-V special sections encode self-relative fields with paired
R_RISCV_ADD/SUB relocations.  The SUB half can reference a local anchor
symbol whose st_value is the field offset in the base section.

When kpatch compacts .alternative, __jump_table, __bug_table, or
__ex_table, move retained in-section anchors with their groups and reject
any anchor left outside the regenerated section.  This keeps the generic
relocation range check meaningful instead of bypassing bad in-section
anchors later.

Signed-off-by: Rui Qi <qirui.001@bytedance.com>
Identify RISC-V HI20/LO12 paired relocations that need special livepatch
handling and record them in the intermediate metadata.

Mark paired LO12 relocations with riscv_paired_lo12 and set the local flag
on intermediate relocations so create-klp-module can emit the correct KLP
relocation type.

Signed-off-by: Rui Qi <qirui.001@bytedance.com>
Consume the intermediate relocation metadata emitted by create-diff-object
and generate livepatch relocation entries for RISC-V paired HI20/LO12
sequences. Create KLP data slots, local anchors, normalize simple
addi-based LO12 loads to direct ld instructions, and remove linker-only
RISC-V relocations that have no runtime counterpart.

Signed-off-by: Rui Qi <qirui.001@bytedance.com>
Extend patch_riscv_lo12_load() to handle PC-relative load instructions (ld,
lw, etc.) whose KLP data slot contains the symbol address rather than its
value.

For these loads an extra dereference is needed. Generate an out-of-line
trampoline that loads the address from the slot and then dereferences it,
redirecting the original AUIPC/LO12 pair through the trampoline with
AUIPC/JALR.

Signed-off-by: Rui Qi <qirui.001@bytedance.com>
kpatch_check_relocations() verifies that relocation targets fall within the
target section, but it only checked rela->sym->sec before reading
rela->sym->sec->data->d_size.  Some relocation targets have a valid section
object but no materialized data buffer, such as NOBITS/.bss or sections
omitted from the output ELF.  For those targets, sec->data is NULL, so the
range check dereferenced NULL and terminated kpatch-build before it could
produce a patch module.

There is no section data size against which to validate these targets. Skip
the range check when sec->data is absent, while leaving relocation
validation unchanged for materialized sections.

Signed-off-by: Rui Qi <qirui.001@bytedance.com>
Debug relocation sections can still reference symbols from sections which
are kept in the output object.  Mark those symbols as included before
pruning debug relocations to unchanged sections.

Signed-off-by: Rui Qi <qirui.001@bytedance.com>
RISC-V uses __riscv_sys_* naming for syscall entry points, unlike other
architectures that use sys_* directly.

Add the RISC-V specific __KPATCH_SYSCALL_DEFINEx macro that generates the
matching __riscv_sys_*, __se_sys_*, and __kpatch_do_sys_* symbols for the
kernel syscall wrapper conventions.

Signed-off-by: Rui Qi <qirui.001@bytedance.com>
Add --skip-vmlinux-final-link for riscv64 builds. In this mode, build the
original and patched trees through vmlinux.o and modpost instead of running
the final vmlinux link.

Use vmlinux.symvers as the symbol version source. Before the external
livepatch module build, copy vmlinux.symvers to Module.symvers because the
module build expects the kernel tree file name.

Signed-off-by: Rui Qi <qirui.001@bytedance.com>
Limit --skip-vmlinux-final-link to inputs whose effects can be verified as
vmlinux-only. Reject custom targets and out-of-tree modules, reject patches
touching non-C sources, and reject .c files or changed objects that resolve
to module objects.

Preserve the pre-build Module.symvers state, including the case where the
file was absent, while the skip-link flow temporarily replaces it with
vmlinux.symvers. Also compare original and patched vmlinux.symvers content
and fail with a cached diff if symbol versions change.

Signed-off-by: Rui Qi <qirui.001@bytedance.com>
Add a RISC-V-only integration test for the vmlinux-only boundary of
--skip-vmlinux-final-link.

The test uses an XFS in-tree module source, verifies that kpatch-build
rejects it before the patched build, and checks that Module.symvers is
restored after the expected failure. Other architectures and configurations
where XFS is not modular are skipped.

Signed-off-by: Rui Qi <qirui.001@bytedance.com>
Add pre-built riscv64 test objects for the unit test cases so the RISC-V
unit test target has matching fixture data.

Signed-off-by: Rui Qi <qirui.001@bytedance.com>
Add riscv64 to the unit test architecture list and skip RISC-V boot objects
in difftree, matching the existing generated-object exclusions for other
architectures.

Signed-off-by: Rui Qi <qirui.001@bytedance.com>
Add a linux-6.12.95 integration test directory containing the standard
livepatch scenario fixtures (data-new, new-function, new-globals,
shadow-newpid, special-static, macro-callbacks, gcc-static-local-var-6,
syscall, symvers-disagreement-FAIL, warn-detect-FAIL, multiple) and
their *-LOADED.test runtime checks, mirroring the set already used under
existing kernel-version directories such as linux-6.17.0 and amzn-2023.

This lets the integration suite run against the 6.12.95 stable kernel
baseline.

Signed-off-by: Rui Qi <qirui.001@bytedance.com>
assert_num_funcs used the letter printed by nm as a proxy for the ELF
symbol type.  That is not reliable on RISC-V: create-diff-object emits
local STT_NOTYPE .Lkpatch_riscv_hi20_* anchors for paired HI20/LO12
relocations, and nm versions which expose special symbols print those
text-section anchors as lowercase t.  The same object can therefore pass
or fail solely because of the installed binutils version or nm options.

Count symbols which objdump explicitly marks as functions and which are
defined in .text or .text.* instead.  Requiring the ELF STT_FUNC type
excludes the RISC-V relocation anchors, while restricting the section
preserves the assertion's original purpose of checking patch text rather
than executable dependency sections such as .init.text.  Newly added helper
functions remain covered because they are STT_FUNC symbols in .text.*.

Signed-off-by: Rui Qi <qirui.001@bytedance.com>
@qiruibd
qiruibd marked this pull request as draft October 6, 2026 08:56
@qiruibd qiruibd changed the title [RFC] add riscv64 kpatch-build support [RFC] riscv64: add initial vmlinux-only kpatch-build support Oct 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant