Skip to content

[WIP] [Deepin-Kernel-SIG] [linux 7.2.y] Add klp support for LoongArch and some related enhancement#1967

Draft
Avenger-285714 wants to merge 16 commits into
deepin-community:linux-7.2.yfrom
Avenger-285714:TMP/LoongArch-KLP
Draft

[WIP] [Deepin-Kernel-SIG] [linux 7.2.y] Add klp support for LoongArch and some related enhancement#1967
Avenger-285714 wants to merge 16 commits into
deepin-community:linux-7.2.yfrom
Avenger-285714:TMP/LoongArch-KLP

Conversation

@Avenger-285714

Copy link
Copy Markdown
Member

A pretty rework to let it available really.

Known issues:

  1. commit messages are bull shit
  2. Too many comments

LoongArch currently dispatches syscalls by calling sys_*() functions
directly from do_syscall() with up to six unsigned long arguments
extracted from pt_regs.  This bypasses the type-safety and security
benefits of ARCH_HAS_SYSCALL_WRAPPER, which the kernel provides for
x86, arm64, riscv and others.

Switch LoongArch to the pt_regs-based syscall wrapper model used by
arm64 and riscv: define __loongarch_sys_*() stubs in a new
arch/loongarch/include/asm/syscall_wrapper.h, update the syscall table
to reference them, and simplify the do_syscall() dispatch to a single
fn(regs) call.  The wrapper extracts arguments from pt_regs (using
orig_a0 for the first argument, matching riscv's rationale for
ptrace safety), passes them through the sign-extension sanitizer
__se_sys_*(), and calls the real implementation __do_sys_*().

This isolates the syscall table entries from the calling convention
and improves type safety by routing all arguments through __SC_LONG /
__SC_CAST.  The __loongarch_sys_*() stubs defined here will later be
extracted into standalone __LOONGARCH_SYS_STUBx macros so that the
livepatch KLP helpers can reuse them.  COMPAT_SYSCALL_DEFINEx
placeholder macros are included (gated behind #if 0) as a starting
point for future CONFIG_COMPAT support.

Select ARCH_HAS_SYSCALL_WRAPPER from arch/loongarch/Kconfig to
activate the override of __SYSCALL_DEFINEx in include/linux/syscalls.h.

Signed-off-by: WangYuli <wangyl5933@chinaunicom.cn>
The LoongArch __SYSCALL_DEFINEx currently defines the pt_regs-based
syscall stub (__loongarch_sys_*) inline.  Extract this into standalone
__LOONGARCH_SYS_STUBx and __LOONGARCH_SYS_STUB0 macros so they can be
reused by both the arch's own __SYSCALL_DEFINEx/SYSCALL_DEFINE0 and the
livepatch KLP helpers.

__LOONGARCH_SYS_STUBx (x > 0) provides the complete stub including a
function body that calls __se_sys##name() after extracting arguments
from pt_regs via SC_LOONGARCH_REGS_TO_ARGS().  This follows the
general approach of x86's separate __X64_SYS_STUBx/__IA32_SYS_STUBx
macros, while hardcoding the argument extraction inside the macro
itself similar to arm64's __SYSCALL_DEFINEx -- a simplification that
is appropriate for a single-ABI architecture.

__LOONGARCH_SYS_STUB0 provides only the forward declaration and
ALLOW_ERROR_INJECTION, without a function body.  This differs from
x86's __SYS_STUB0 which uses __alias(__do_##name) to produce a
complete stub.  The difference is intentional: under
ARCH_HAS_SYSCALL_WRAPPER, the KLP (livepatch) path must define its
own __loongarch_sys_*() body that calls __klp_do_sys*() rather than
__do_sys_*(), so a simple __alias-based stub is not feasible for the
zero-argument case.  Both callers (SYSCALL_DEFINE0 and
KLP_SYSCALL_DEFINE0) consistently supply the function body after
invoking the macro.

This is a pure refactoring; no functional change intended.

Signed-off-by: WangYuli <wangyl5933@chinaunicom.cn>
The klp-build subsystem relies on architecture-specific relocation
hooks in objtool to correctly process relocations in livepatch target
objects.  Add the missing hooks for LoongArch:

  - arch_adjusted_addend(): returns the relocation addend unchanged.
    LoongArch is a pure RELA architecture where every relocation entry
    carries its addend explicitly in Elf64_Rela::r_addend, already in
    its final form.  This is fundamentally different from x86, where
    R_X86_64_PC32 stores the addend relative to the relocation point,
    forcing arch_adjusted_addend() to decode the instruction at the
    site and back-calculate the offset relative to the end of the next
    instruction:

      x86:  addend + insn_off + insn_len - reloc_offset
      LoongArch: addend   (no adjustment needed)

    The trivial passthrough is therefore correct by construction, not
    a placeholder.

  - arch_absolute_reloc(): identifies absolute relocation types.
    This follows the same design as x86, covering all absolute
    relocation types on LoongArch: R_LARCH_32 and R_LARCH_64
    correspond to R_X86_64_32 and R_X86_64_64; the LoongArch ELF ABI
    has no signed 32-bit absolute type analogous to R_X86_64_32S.
    Additionally, the split-immediate absolute types
    (R_LARCH_ABS_HI20, R_LARCH_ABS_LO12, R_LARCH_ABS64_LO20,
    R_LARCH_ABS64_HI12) are included for defense in depth, though they
    are not expected in -fPIC code.  This replaces the __weak default
    in check.c that only covers R_ABS64.

  - arch_pc_relative_reloc(): currently returns false for all
    relocation types.  This is sufficient because neither klp-diff.c
    nor the LoongArch arch_insn_adjusted_addend() require PC-relative
    classification -- klp-diff.c does not call this hook, and the pure
    RELA addend needs no instruction-level PC compensation.  A full
    PC-relative type list (R_LARCH_PCALA_*, R_LARCH_GOT_PC_*, etc.)
    can be added later if the objtool alternative-section validator
    needs it for LoongArch.

Both hooks are added to decode.o, which is always compiled, preserving
bisectability.  Their activation timing differs:

  - arch_adjusted_addend(): Its sole caller is convert_reloc_secsym_to_sym()
    in klp-diff.c, which is only linked when BUILD_KLP=y.  This hook is
    genuinely dormant until ARCH_HAS_KLP enables klp-build in a later patch.

  - arch_absolute_reloc(): This overrides the __weak default in check.c
    at link time immediately, regardless of BUILD_KLP.  Its caller
    check_abs_references() is gated by the --noabs command-line option
    rather than any KLP configuration knob.  In normal kernel builds
    --noabs is not passed, so the hook does not execute in practice,
    but it is architecturally active from the moment decode.o is linked
    into objtool.

Signed-off-by: WangYuli <wangyl5933@chinaunicom.cn>
GCC/LoongArch can emit special-section relocations against
assembler-local labels such as .L1 instead of section symbols.  That
happens for entries like __ex_table and .altinstructions, whose fields
are written as PC-relative expressions in assembly.

klp-diff only converted section-symbol relocations to the referenced
function or object symbol.  A relocation which already referenced a local
NOTYPE symbol bypassed convert_reloc_secsym_to_sym(), so
should_keep_special_sym() could fail to see that a special-section entry
referenced a cloned function.  Such entries could then be silently
dropped from the generated livepatch module.

Normalize assembler-local labels to their section symbols before the
regular conversion path.  The addend is adjusted with the local label's
section offset, after which the existing section-symbol conversion can
resolve the relocation to the containing function, object, or fake
special-section symbol.

GAS paired-relocation anchors (e.g. LoongArch L0^A, .L1^B1) are
recognized by their leading 'L' and embedded SOH (^A, \001) / STX (^B,
\002) control characters, wrapped in the named constants
GAS_PAIRED_ANCHOR_SOH / GAS_PAIRED_ANCHOR_STX for clarity.  Requiring the
'L' prefix avoids misidentifying unrelated NOTYPE symbols that happen to
contain those bytes.

Use the same symbol-plus-addend helper when reading
ANNOTATE_DATA_SPECIAL offsets from .discard.annotate_data.  This covers
local-label annotation relocations as well as section-symbol annotation
relocations.

This keeps architecture assembly macros free to use ordinary
label-relative expressions in any text subsection, avoiding fragile
hard-coded references to .text while still giving klp-diff the
section-relative form it needs.

Signed-off-by: WangYuli <wangyl5933@chinaunicom.cn>
The `local_label` bitfield in `struct symbol` has been write-only dead
code since the last reader was removed.

Historical trace:

  1. d5ab2bc ("objtool: Check local label in add_dead_ends()",
     Tiezhu Yang, 2024-03-11): Introduced the bitfield with one writer
     in classify_symbols() and one reader in add_dead_ends().  This
     was needed because GCC 14 on LoongArch started emitting .L*
     local label relocations in .discard.unreachable instead of the
     usual section+offset form.

  2. e91c5e4 ("objtool: Check local label in read_unwind_hints()"):
     Added a second reader in read_unwind_hints() following the same
     pattern — check reloc->sym->local_label to distinguish local
     labels from section symbols.

  3. e7a174f ("objtool: Convert {.UN}REACHABLE to ANNOTATE",
     Josh Poimboeuf, 2025): Removed add_dead_ends() entirely as part
     of the dead-end annotation refactoring, taking the first reader
     with it.  The bitfield now had one reader remaining.

  4. a040ab7 ("objtool: Simplify reloc offset calculation in
     unwind_read_hints()", Josh Poimboeuf, 2025-09-17): Simplified
     read_unwind_hints() to the single expression

         offset = reloc->sym->offset + reloc_addend(reloc);

     for all relocation types, removing the conditional branch that
     read reloc->sym->local_label.  This was the last reader.

After commit a040ab7, no code anywhere in tools/objtool/ reads
sym->local_label.  A tree-wide grep confirms:

  - sym->local_label is written exactly once: classify_symbols() in
    check.c sets func->local_label = true for NOTYPE LOCAL symbols
    whose name starts with ".L".

  - sym->local_label is read zero times.  The field is never consumed
    by any function, in any architecture backend, or in any KLP
    pipeline.

The inline helper is_local_label(), which inspects symbol name patterns
at call time rather than consulting a cached bit, has superseded the
bitfield for all practical local-label detection in both klp-diff.c
and klp-checksum.c.

Remove the dead bitfield from struct symbol and drop the dead
assignment in classify_symbols().  No functional change — the removed
code had no observable effect on any objtool code path.

Signed-off-by: WangYuli <wangyl5933@chinaunicom.cn>
The previous patch ("objtool/klp: Normalize local label relocs
before symbol conversion") added local label normalization to the
diff pipeline so that GCC/LoongArch .L* assembler-local labels are
resolved to their containing function before relocation processing.

The checksum pipeline has the same vulnerability: both
checksum_update_insn() and checksum_update_object() only check
is_sec_sym() when resolving relocation targets, skipping .L*
NOTYPE local symbols.  If such a label reaches the checksum
hashing path, its unstable name (".L1", ".L2", ...) and raw
section-relative offset would be hashed directly, making the
checksum fragile across recompilation and potentially causing
false-positive "changed function" detections.

Move is_local_label() from a static function in klp-diff.c to an
inline in include/objtool/elf.h so both files can share it.  Add
local-label normalization in both checksum functions before the
existing is_sec_sym() resolution path.  This ensures the checksum
and diff pipelines resolve relocation targets identically.

Unlike the diff path, which may legitimately need to create section
symbols for its output ELF, the checksum path is a read-only analysis
and must not mutate ELF internal state.  Resolve local labels directly
to their containing function or object symbol with
find_symbol_containing_inclusive() instead of creating an intermediate
STT_SECTION symbol via elf_create_section_symbol().  This achieves
identical hashing behavior without modifying the symbol table or the
list being iterated by for_each_sym().

When find_symbol_containing_inclusive() cannot resolve a local label, emit a
WARN() and fall back to hashing the section name and absolute offset
rather than silently dropping the relocation.  Similarly, when a
section symbol cannot be resolved to a named symbol (e.g. anonymous
.rodata data), keep the section symbol for stable hashing instead of
skipping it.  These fallbacks preserve checksum sensitivity in edge
cases such as discarded sections and compiler-generated anonymous
data sections.

Use find_symbol_containing_inclusive() rather than
find_symbol_containing() so that the checksum path matches the diff
path's boundary handling: when a local label's offset falls exactly
at a symbol's end address (e.g. sizeof() bounds comparisons), the
inclusive variant also checks offset-1 and still resolves to the
containing symbol.  This prevents an unnecessary fallback to the
section-name+offset hashing path for relocations at symbol
boundaries.

Align the is_sec_sym() fallback with the diff path's
convert_reloc_secsym_to_sym() three-way handling: skip empty
sections (matching the diff path's return 1), keep section
symbols for .rodata anonymous data (matching the diff path's
return 0), and WARN on non-.rodata unresolvable section symbols
for diagnostic visibility.  The checksum path is a read-only
analysis and cannot error out like the diff path, but the WARN()
preserves awareness of unexpected cases that the diff path would
reject.

Signed-off-by: WangYuli <wangyl5933@chinaunicom.cn>
… sizes

create_fake_symbols() computes the size of an ANNOTATE_DATA_SPECIAL
entry by looking ahead to the next annotation that targets the same
section and taking the difference of their target offsets.  This
assumes that relocations in .rela.discard.annotate_data are ordered
by ascending target offset within each target section -- a property
that is not guaranteed by the ELF specification or any ABI.

If a toolchain emits annotations in a different order (e.g., due to
SHF_MERGE reordering or assembler optimization), the computed size
can wrap to a huge value when next_offset < current_offset, or
entries can silently overlap, corrupting the generated livepatch
module.

Fix by collecting all ANNOTYPE_DATA_SPECIAL relocations, sorting them
by (target section, target offset), then computing sizes from
consecutive sorted entries.  The last entry in each group uses the
section end as the boundary.  This eliminates the ordering dependency
entirely.

The sorting is O(n log n) on the number of special section entries
per .o file, which is typically a few dozen at most.

Signed-off-by: WangYuli <wangyl5933@chinaunicom.cn>
klp-diff's is_special_section() recognizes __mcount_loc as a special
section whose entries must be extracted on a per-function basis for
livepatch module generation.  However, architectures that use the
compiler's -fpatchable-function-entry flag (arm64, LoongArch, powerpc)
emit __patchable_function_entries instead, and that section is missing
from the list.

When __patchable_function_entries is not treated as special, klp-diff
silently drops all its entries from the output.  This leaves patched
functions invisible to ftrace in the resulting livepatch module -- no
kernel crash, just a silent functional loss: function graph tracing,
trace_printk(), and any livepatch consistency model that relies on
ftrace stop working for those functions.

The root cause is that the specials[] array only contains __mcount_loc,
which on x86 is generated by objtool long after the .o files have been
processed by klp-diff.  On architectures using -fpatchable-function-entry
the compiler emits __patchable_function_entries directly into each .o,
so klp-diff must handle it at diff time.  The missing entry causes
three code paths to silently skip the section:

  - create_fake_symbols() does not create per-entry symbols,
  - clone_special_sections() does not clone it, and
  - dont_correlate() does not exclude its symbols from correlation.

Add "__patchable_function_entries" to the specials[] array.  No other
code changes are needed: the compiler does not set sh_entsize on this
section, so create_fake_symbols() falls back to arch_reloc_size() to
determine the entry size.  arch_reloc_size() is already implemented
for both LoongArch (returns 8 for R_LARCH_ABS64) and powerpc (default
case returns 8, matching ppc64 pointer size).  The sec_size ==
entry_size * num_relocs check holds for this pointer-array section,
fake symbols are created correctly, and should_keep_special_sym()
properly identifies entries referencing cloned functions.

Although powerpc does not currently select HAVE_KLP_BUILD, its
compiler already emits __patchable_function_entries for ftrace (see
arch/powerpc/Makefile lines 152-157 for the -fpatchable-function-entry
flags), and arch_reloc_size() is in place.  Adding the section name
now ensures correctness when powerpc enables klp-build in the future,
without requiring another update to klp-diff.

Signed-off-by: WangYuli <wangyl5933@chinaunicom.cn>
klp-build needs to extract individual entries from special sections such
as .altinstructions, __ex_table, __bug_table, and __jump_table.  Without
explicit entry boundaries, klp-diff cannot reliably decide which entries
belong to a changed function and which data must be copied into the
livepatch module.

LoongArch currently emits these sections as plain assembler data.  For
writable sections, using SHF_MERGE is not a portable way to communicate
entry size to objtool.  For readonly sections, entry-size based discovery
would also require asm-offsets plumbing.  Use ANNOTATE_DATA_SPECIAL
instead, which lets klp-diff create fake symbols for each annotated entry
independent of section flags.

Annotate LoongArch exception-table entries, alternative instruction
descriptors, BUG entries, and static-branch jump-table entries.  The
assembly continues to use ordinary label-relative expressions so the
macros work from .text, .init.text, .noinstr.text, and other text
subsections for non-alternative annotations; the preceding objtool change
normalizes local-label relocations before klp-diff makes keep/drop
decisions.

Place alternative replacement instructions in a dedicated
.altinstr_replacement section (with "ax" flags) rather than .subsection 1
of the parent text section.  This matches the x86 approach and allows
klp-diff's is_special_section_aux() to recognize the replacement section,
ensuring replacement code is properly extracted as a dependency of
.altinstructions entries during livepatch module generation.

For __bug_table, do not use a hardcoded BUG_ENTRY_SIZE with .org padding
to force a fixed entry size.  klp-build discovers individual bug entries
via the ANNOTATE_DATA_SPECIAL annotations, not by relying on sh_entsize
or a uniform entry size: create_fake_symbols() computes the size of each
entry dynamically as the distance to the next annotation (or to the
section end).  Instead, follow the arm64 approach
(arch/arm64/include/asm/asm-bug.h) and rely on the existing .align 2
directive to naturally pad each entry to a 4-byte boundary.  This
matches sizeof(struct bug_entry) under both CONFIG_DEBUG_BUGVERBOSE=y
and =n:

  - With CONFIG_DEBUG_BUGVERBOSE=y: the 12 bytes of data are already
    4-byte aligned, so .align 2 is a no-op and sizeof = 12 matches.

  - With CONFIG_DEBUG_BUGVERBOSE=n: the 6 bytes of data (bug_addr_disp +
    flags) are padded by .align 2 to 8 bytes, which matches sizeof = 8
    (4-byte bug_addr_disp + 2-byte flags + 2-byte tail padding for
    struct alignment).

A hardcoded BUG_ENTRY_SIZE = 12 would force 12-byte entries even in the
CONFIG_DEBUG_BUGVERBOSE=n case, where sizeof(struct bug_entry) is only 8,
breaking both find_bug() table walking (which uses C pointer arithmetic
with sizeof stride) and module_bug_finalize()'s num_bugs calculation
(which divides section size by sizeof).

This keeps the section annotations uniform and avoids adding fragile
asm-offsets dependencies or hard-coded .text expressions to generic
LoongArch assembly macros.

Signed-off-by: WangYuli <wangyl5933@chinaunicom.cn>
The __BUG_ENTRY assembly macro in arch/loongarch/include/asm/bug.h emits
bug_addr_disp (4 bytes), optional file_disp/line (0 or 6 bytes), and
flags (2 bytes), relying on .align 2 to pad entries to a 4-byte boundary.
The kernel iterates the __bug_table using C pointer arithmetic with
sizeof(struct bug_entry) as the stride, so the assembly layout must match.

Add a BUILD_BUG_ON to verify sizeof(struct bug_entry) at compile time:

  - CONFIG_DEBUG_BUGVERBOSE=y: sizeof must be 12 (4+4+2+2)
  - CONFIG_DEBUG_BUGVERBOSE=n: sizeof must be 8  (4+2+2 tail padding)

This guards against future changes to struct bug_entry or the assembly
macro that could silently break the layout consistency, as would happen
with a hardcoded padding size that does not track sizeof.

Signed-off-by: WangYuli <wangyl5933@chinaunicom.cn>
The LoongArch kernel is normally compiled with -fPIE
(CONFIG_RELOCATABLE=y) and -mdirect-extern-access / -fdirect-access-
external-data (CONFIG_AS_HAS_EXPLICIT_RELOCS=y).  While these are
correct for the base kernel, they prevent klp-diff from properly
redirecting data references in livepatch modules for two independent
reasons:

1. -fPIE still generates direct PC-relative relocations
   (R_LARCH_PCALA_HI20/LO12) for global data symbols defined in the same
   translation unit, bypassing the GOT.  Override with -fPIC, which
   guarantees GOT-based accesses (R_LARCH_GOT_PC_HI20/LO12) for all
   global data regardless of TU scope.

2. -mdirect-extern-access (GCC) / -fdirect-access-external-data (Clang)
   allow the compiler to access external data via direct PC-relative
   addressing, also bypassing the GOT.  Override with -mno-direct-extern-
   access / -fno-direct-access-external-data to restore GOT-based access
   for cross-TU data references.

Note that GCC rejects the combination of -mdirect-extern-access with
-fPIC (shared library model), so -mno-direct-extern-access must be
appended to negate the earlier -mdirect-extern-access.  Both flags are
wrapped in cc-option for compiler compatibility.

The flags are guarded by the KLP_REBUILD make variable, passed on the
command line by scripts/livepatch/klp-build, rather than by the
CONFIG_KLP_BUILD Kconfig symbol.  This ensures the -fPIC code generation
model is active only during the klp-build internal rebuild and never
affects the bootable production kernel.  A normal "make vmlinux" without
KLP_REBUILD=1 on the command line continues to use -fPIE.

klp-build's find_objects() collects both vmlinux.o (built-in objects)
and all module .ko archives (converted to .o) for comparison with
klp-diff.  Therefore, -fPIC and the associated flags are added to both
KBUILD_CFLAGS_KERNEL (built-in objects, overriding -fPIE) and
KBUILD_CFLAGS_MODULE (module objects, guaranteeing GOT indirection for
same-TU data).  They are not placed in the broader KBUILD_CFLAGS because
in the compilation command (c_flags = $(_c_flags) $(modkern_cflags)),
KBUILD_CFLAGS appears before KBUILD_CFLAGS_KERNEL, which would cause the
later -fPIE to override -fPIC instead of the intended direction.

Within klp-build, both the original and patched kernel builds receive
KLP_REBUILD=1, so both produce objects compiled with the identical
-fPIC code model.  The klp-build workflow guarantees this by calling
clean_kernel() before the first build (eliminating any pre-existing
-fPIE objects) and passing KLP_REBUILD=1 to every build_kernel()
invocation:

  clean_kernel                         # wipes all .o files
  build_kernel "original"              # KLP_REBUILD=1 -> all -fPIC
  copy_orig_objects                    # -> ORIG_DIR (all -fPIC)
  [apply patches]
  build_kernel "patched"               # KLP_REBUILD=1
  copy_patched_objects                 # -> PATCHED_DIR (all -fPIC)

Thus klp-diff compares -fPIC objects against -fPIC objects and sees only
the true code changes introduced by the livepatch, with zero spurious
differences from code model divergence.

Evidence from GCC 15.2.0 and Clang 23.0.0 on loongarch64:

- -mdirect-extern-access -fPIE emits R_LARCH_PCALA_* for both same-TU and
  cross-TU global data references.
- -fPIC -mno-direct-extern-access emits R_LARCH_GOT_PC_* for both same-TU
  and cross-TU global data references.

At the instruction level, -fPIC replaces the direct address computation
sequence (pcalau12i + addi.d + ldptr.w) with a GOT load sequence
(pcalau12i + ld.d + ldptr.w), which is the indirection that klp-diff
relies on to redirect data references.  static variables are unaffected
and continue to use direct PC-relative access, as expected.

Rust kernel objects (when CONFIG_RUST=y) suffer from the same GOT bypass
as their C counterparts.  The normal kernel build sets
-Crelocation-model=pie (same-TU data stays direct) and
-Zdirect-access-external-data=yes (cross-TU data bypasses GOT).  Under
KLP_REBUILD, override these with -Crelocation-model=pic and
-Zdirect-access-external-data=no in KBUILD_RUSTFLAGS_KERNEL and
KBUILD_RUSTFLAGS_MODULE so that klp-diff can redirect all Rust global
data references through the GOT, matching the C code generation model.
(Zdirect-access-external-data=no is already the default for
KBUILD_RUSTFLAGS_MODULE in the normal build; the assignment is kept for
symmetry and as defense against future reordering.)

The KLP flags in KBUILD_CFLAGS_KERNEL would otherwise leak into the EFI
stub build (whose objects are lib-y and receive KBUILD_CFLAGS_KERNEL),
overriding the stub's own -fpie code model.  Filter them out in
drivers/firmware/efi/libstub/Makefile by extending the existing
filter-out that already removes -fdata-sections for similar reasons.
This covers both the in-kernel EFI stub and the EFI zboot stub (whose C
objects are lib-$(CONFIG_EFI_ZBOOT) in the same Makefile).

All architecture-specific logic is placed in arch/loongarch/Makefile.
The scripts/livepatch/klp-build change is a single arch-neutral line
(KLP_REBUILD=1) with zero CONFIG_LOONGARCH hard-coding, satisfying the
constraint that the shared script remains architecture-agnostic.

Signed-off-by: WangYuli <wangyl5933@chinaunicom.cn>
klp-build rebuilds the kernel with -ffunction-sections and
-fdata-sections so objtool can compare individual functions and data
objects.  With -fdata-sections, zero-initialized init data may be
emitted into per-symbol .init.bss.* input sections instead of the single
.init.bss input section.

The LoongArch linker script currently collects only .init.bss into the
.init.bss output section.  Any .init.bss.* input section is therefore
left for orphan-section placement, which makes the rebuilt vmlinux
layout differ for reasons unrelated to the livepatch source change.

Collect .init.bss.* together with .init.bss.  This mirrors the convention
used by other kernel linker-script patterns such as .init.data.*,
.init.text.*, .exit.data.*, and the generic BSS patterns, while
preserving the existing .init.bss output section and init-data lifetime
boundaries.

Verified by preprocessing arch/loongarch/kernel/vmlinux.lds through
Kbuild and by linking a minimal LoongArch object containing both
.init.bss and .init.bss.foo: the old script leaves .init.bss.foo as a
separate orphan output section, while the new script folds it into
.init.bss.

Signed-off-by: WangYuli <wangyl5933@chinaunicom.cn>
klp-build computes per-function checksums after objtool has resolved
static call and jump destinations.  When a direct branch has no
relocation, its destination is encoded in the instruction itself.  That
encoded offset changes when the target moves, even if the branch still
has the same semantic destination.  The checksum code therefore asks the
architecture backend to hash only the branch opcode bytes and then hashes
an independent representation of the resolved destination.

LoongArch did not provide arch_jump_opcode_bytes().  The x86 helper
cannot be reused because LoongArch branch offsets are not trailing
whole-byte immediates.  The branch formats decoded by objtool are:

  reg0i26: b, bl
           opcode in bits[31:26], immediate in bits[25:0]
  reg1i21: beqz, bnez, bceqz/bcnez
           opcode in bits[31:26], rj in bits[9:5],
           immediate in bits[25:10] and bits[4:0]
  reg2i16: jirl, beq, bne, blt, bge, bltu, bgeu
           opcode in bits[31:26], rj in bits[9:5], rd in bits[4:0],
           immediate in bits[25:10]

Return a normalized four-byte instruction with only the PC-relative
immediate bits cleared.  This keeps the opcode and operand fields which
control the branch semantics.  In particular, bceqz and bcnez share the
main opcode 0x12, and the selector is carried in the reg1i21 rj field,
so the whole rj field must be preserved.

The mask derivation was checked directly against the LoongArch bitfield
layout in arch/loongarch/include/asm/inst.h and the synced objtool copy
in tools/arch/loongarch/include/asm/inst.h.  A small script encoded every
currently decoded branch class and asserted that changing only the offset
keeps the normalized word unchanged:

  b/bl:                    word & 0xfc000000
  beqz/bnez/bceqz/bcnez:   word & 0xfc0003e0
  jirl/beq/bne/blt/bge/
  bltu/bgeu:               word & 0xfc0003ff

The same script also asserted that changing preserved operands changes
the normalized word: beqz rj=1 and rj=2 differ, bceqz rj=0x0 and bcnez
rj=0x8 differ, and beq rd/rj changes differ.  This proves that the masks
remove the layout-dependent offset while retaining branch semantics.

The KLP checksum path was exercised with temporary LoongArch objects made
from raw .word encodings so that readelf reported no relocations.  Two
objects kept the caller function body unchanged except for the direct
branch offset and moved the target symbol from 0x8 to 0xc by inserting a
NOP outside caller.  For an unconditional branch, objdump showed
0x50000800 (b +8) versus 0x50000c00 (b +12), and

  objtool klp checksum --dry-run --debug-checksum=caller

reported the same digest sequence for caller in both objects:

  e46f972692bc762f -> b2069428bb22474e -> ec801e4ecb947639

The same test with reg2i16 conditional branches used 0x580008a4
(beq $a1, $a0, +8) and 0x58000ca4 (beq $a1, $a0, +12); both objects
again produced the same caller digest sequence:

  cd6b51730d38b982 -> 592a26cb5ac3420a -> b318262baebc657e

As a negative check, changing the preserved rj operand from $a1 to $a2
changed the checksum sequence to

  d776ff80d4be6269 -> b19e621a322e8aac -> 2797d9d558617721

which confirms that the checksum remains sensitive to branch semantics.
A normal LoongArch objtool W=1 build succeeds, and checkpatch.pl --strict
reports no warnings for this patch.

Signed-off-by: WangYuli <wangyl5933@chinaunicom.cn>
KLP_SYSCALL_DEFINE* currently provides an arch-specific syscall wrapper
implementation only for x86_64.  LoongArch now uses ARCH_HAS_SYSCALL_WRAPPER
with __loongarch_sys_*() pt_regs-based entry points in the syscall table.

Add a LoongArch __KLP_SYSCALL_DEFINEx that generates matching
__loongarch_sys_*() stubs, extracting arguments from pt_regs via
SC_LOONGARCH_REGS_TO_ARGS() and delegating the real work to
__klp_do_sys_*() to avoid colliding with the original __do_sys_*().
Include ALLOW_ERROR_INJECTION on the __loongarch_sys_*() stub.

Add a LoongArch KLP_SYSCALL_DEFINE0() that generates the zero-argument
pt_regs wrapper __loongarch_sys_sname(), matching SYSCALL_DEFINE0.
Keep the generic KLP_SYSCALL_DEFINE0() (sys_* alias) for architectures
without ARCH_HAS_SYSCALL_WRAPPER.

Signed-off-by: WangYuli <wangyl5933@chinaunicom.cn>
LoongArch already supports kernel livepatching, but klp-build needs
additional architecture support beyond HAVE_LIVEPATCH.  It relies on
objtool's livepatch diff/checksum path, RELA addend handling, stable
branch opcode normalization, special section discovery, syscall wrapper
support, and rebuild-time code generation constraints.

The required LoongArch pieces are now in place.  The klp-build rebuild
path is handled in arch/loongarch/Makefile under KLP_REBUILD, keeping
scripts/livepatch/klp-build architecture-independent.  The objtool hooks
and special section annotations provide the information needed to keep
livepatch-generated objects correct.

Select HAVE_KLP_BUILD only when RELOCATABLE and AS_HAS_EXPLICIT_RELOCS
are enabled and UNWINDER_ORC is the active unwinder.  RELOCATABLE
provides the relocation model expected by the klp-build rebuild flow;
explicit relocations keep the LoongArch relocation stream predictable
for objtool; UNWINDER_ORC provides the --orc action that objtool
requires for module validation, since STACK_VALIDATION does not apply
to LoongArch.

Keep this enablement as the final patch in the series so that every
prefix remains buildable: earlier patches add the required support, and
this patch is the point where LoongArch advertises klp-build support.

Signed-off-by: WangYuli <wangyl5933@chinaunicom.cn>
KLP_BUILD selects OBJTOOL which causes objtool to validate every
module object during the kernel build.  On LoongArch the only objtool
action that can satisfy the 'at least one action required' check is
--orc, which comes from CONFIG_UNWINDER_ORC.  STACK_VALIDATION is
permanently unavailable because it requires UNWINDER_FRAME_POINTER,
which LoongArch does not implement.

When a user enables LIVEPATCH without also switching to UNWINDER_ORC,
the kernel build fails for every module object:

  error: objtool: At least one action required

Make UNWINDER_ORC the default unwinder when LIVEPATCH=y so that
enabling livepatch automatically selects a compatible unwinder.
Users who need a different unwinder can still override the choice
manually; in that case HAVE_KLP_BUILD is gated on UNWINDER_ORC
(see the LOONGARCH Kconfig) and KLP_BUILD will be disabled.

The UNWINDER_ORC default is placed before the existing
UNWINDER_PROLOGUE default so that the LIVEPATCH condition is
evaluated first.

Signed-off-by: WangYuli <wangyl5933@chinaunicom.cn>

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry @Avenger-285714, you have reached your weekly rate limit of 500000 diff characters.

Please try again later or upgrade to continue using Sourcery

@deepin-ci-robot

Copy link
Copy Markdown

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by:

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR reworks livepatch (klp-build/objtool) support to enable LoongArch, including LoongArch-specific syscall wrapper plumbing, objtool KLP relocation/checksum normalization for assembler-local labels, and build/linker changes needed for alternatives/special sections and GOT-based data redirection during klp-build rebuilds.

Changes:

  • Enable objtool KLP path for LoongArch and improve objtool handling of assembler-local labels and special-section entry extraction.
  • Add LoongArch syscall wrapper support (pt_regs-based syscall table entries) and corresponding livepatch helper macros.
  • Introduce KLP_REBUILD build-mode overrides for LoongArch (GOT-based data access) and adjust linker/scripts/EFI stub flags to avoid klp-build flag leakage.

Reviewed changes

Copilot reviewed 24 out of 24 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
tools/objtool/Makefile Enables KLP/ORC-related knobs for LoongArch in objtool builds.
tools/objtool/klp-diff.c Improves relocation normalization (local labels/section symbols) and makes DATA_SPECIAL entry sizing deterministic via sorting.
tools/objtool/klp-checksum.c Normalizes local-label and section-symbol relocations for stable checksum hashing.
tools/objtool/include/objtool/elf.h Centralizes local-label detection (including GAS paired-reloc anchor patterns).
tools/objtool/check.c Removes per-symbol local_label bookkeeping in favor of centralized detection.
tools/objtool/arch/loongarch/include/arch/elf.h Adds missing LoongArch relocation constants needed by objtool.
tools/objtool/arch/loongarch/decode.c Implements LoongArch KLP-related relocation classification and stable jump opcode hashing.
scripts/livepatch/klp-build Passes KLP_REBUILD=1 into the klp-build internal kernel rebuild.
include/linux/livepatch_helpers.h Adds LoongArch-specific KLP syscall wrapper macros to match pt_regs syscall ABI.
drivers/firmware/efi/libstub/Makefile Filters out klp-build code-model flags (and -fdata-sections) from EFI stub build flags.
arch/loongarch/Makefile Adds KLP_REBUILD code-model overrides (PIC/GOT) for both C and Rust objects.
arch/loongarch/kernel/vmlinux.lds.S Places .altinstr_replacement and widens .init.bss matching for KLP/alternatives needs.
arch/loongarch/kernel/traps.c Adds compile-time validation of bug table entry sizing vs assembly layout.
arch/loongarch/kernel/syscall.c Switches syscall table to pt_regs-based wrappers and updates dispatch accordingly.
arch/loongarch/Kconfig.debug Defaults to ORC unwinder when LIVEPATCH is enabled.
arch/loongarch/Kconfig Selects ARCH_HAS_SYSCALL_WRAPPER and gates HAVE_KLP_BUILD on required prerequisites.
arch/loongarch/include/asm/syscall.h Defines syscall_fn_t and makes sys_call_table typed/const.
arch/loongarch/include/asm/syscall_wrapper.h Introduces LoongArch syscall wrapper implementation/macros (pt_regs ABI).
arch/loongarch/include/asm/jump_label.h Annotates __jump_table entries as DATA_SPECIAL for klp-build extraction.
arch/loongarch/include/asm/bug.h Annotates __bug_table entries as DATA_SPECIAL for klp-build extraction.
arch/loongarch/include/asm/asm.h Includes annotate helpers for assembly macros using objtool annotations.
arch/loongarch/include/asm/asm-extable.h Annotates __ex_table entries as DATA_SPECIAL (asm + C-string forms).
arch/loongarch/include/asm/alternative.h Switches alternatives replacement emission to .altinstr_replacement and annotates entries for klp-build.
arch/loongarch/include/asm/alternative-asm.h Assembler-side alternatives: emits replacements in .altinstr_replacement and annotates entries.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +314 to +320
* Assembler-local labels (.L* on most targets, L*^A/L*^B GAS
* paired-relocation anchors on some architectures) are NOTYPE
* LOCAL symbols that reference a position within a section.
* They are not present in kallsyms and must be normalized to
* their section symbol before relocation resolution in both
* the diff and checksum pipelines.
*
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants