* [PATCH v3 07/21] crypto: arm64: Move data to .rodata
From: Josh Poimboeuf @ 2026-05-13 3:33 UTC (permalink / raw)
To: x86
Cc: linux-kernel, live-patching, Peter Zijlstra, Joe Lawrence,
Song Liu, Catalin Marinas, Will Deacon, linux-arm-kernel,
Mark Rutland, Miroslav Benes, Petr Mladek
In-Reply-To: <cover.1778642120.git.jpoimboe@kernel.org>
Data embedded in .text pollutes i-cache and confuses objtool and other
tools that try to disassemble it. Move it to .rodata.
Signed-off-by: Josh Poimboeuf <jpoimboe@kernel.org>
---
lib/crypto/arm64/sha2-armv8.pl | 18 ++++++++++++------
1 file changed, 12 insertions(+), 6 deletions(-)
diff --git a/lib/crypto/arm64/sha2-armv8.pl b/lib/crypto/arm64/sha2-armv8.pl
index 35ec9ae99fe16..e0ee2d5367e72 100644
--- a/lib/crypto/arm64/sha2-armv8.pl
+++ b/lib/crypto/arm64/sha2-armv8.pl
@@ -207,12 +207,13 @@ $func:
___
$code.=<<___ if ($SZ==4);
#ifndef __KERNEL__
+ adrp x17,.LOPENSSL_armcap_P
+ add x17,x17,:lo12:.LOPENSSL_armcap_P
# ifdef __ILP32__
- ldrsw x16,.LOPENSSL_armcap_P
+ ldrsw x16,[x17]
# else
- ldr x16,.LOPENSSL_armcap_P
+ ldr x16,[x17]
# endif
- adr x17,.LOPENSSL_armcap_P
add x16,x16,x17
ldr w16,[x16]
tst w16,#ARMV8_SHA256
@@ -237,7 +238,8 @@ $code.=<<___;
ldp $E,$F,[$ctx,#4*$SZ]
add $num,$inp,$num,lsl#`log(16*$SZ)/log(2)` // end of input
ldp $G,$H,[$ctx,#6*$SZ]
- adr $Ktbl,.LK$BITS
+ adrp $Ktbl,.LK$BITS
+ add $Ktbl,$Ktbl,:lo12:.LK$BITS
stp $ctx,$num,[x29,#96]
.Loop:
@@ -286,6 +288,7 @@ $code.=<<___;
ret
.size $func,.-$func
+.pushsection .rodata
.align 6
.type .LK$BITS,%object
.LK$BITS:
@@ -365,6 +368,7 @@ $code.=<<___;
#endif
.asciz "SHA$BITS block transform for ARMv8, CRYPTOGAMS by <appro\@openssl.org>"
.align 2
+.popsection
___
if ($SZ==4) {
@@ -385,7 +389,8 @@ sha256_block_armv8:
add x29,sp,#0
ld1.32 {$ABCD,$EFGH},[$ctx]
- adr $Ktbl,.LK256
+ adrp $Ktbl,.LK256
+ add $Ktbl,$Ktbl,:lo12:.LK256
.Loop_hw:
ld1 {@MSG[0]-@MSG[3]},[$inp],#64
@@ -648,7 +653,8 @@ sha256_block_neon:
mov x29, sp
sub sp,sp,#16*4
- adr $Ktbl,.LK256
+ adrp $Ktbl,.LK256
+ add $Ktbl,$Ktbl,:lo12:.LK256
add $num,$inp,$num,lsl#6 // len to point at the end of inp
ld1.8 {@X[0]},[$inp], #16
--
2.53.0
^ permalink raw reply related
* [PATCH v3 06/21] arm64: Annotate special section entries
From: Josh Poimboeuf @ 2026-05-13 3:33 UTC (permalink / raw)
To: x86
Cc: linux-kernel, live-patching, Peter Zijlstra, Joe Lawrence,
Song Liu, Catalin Marinas, Will Deacon, linux-arm-kernel,
Mark Rutland, Miroslav Benes, Petr Mladek
In-Reply-To: <cover.1778642120.git.jpoimboe@kernel.org>
In preparation for adding arm64 support for "objtool klp checksum/diff"
to enable livepatch module generation, annotate special section entries.
This will allow objtool to determine the size and location of the
entries and to extract them when needed.
A new ANNOTATE_DATA_SPECIAL_END annotation is added to mark the end of
special data blocks, which is needed because arm64's replacement
instructions are emitted in .text rather than .altinstr_replacement, so
there's otherwise no way to determine where the last replacement block
ends.
Signed-off-by: Josh Poimboeuf <jpoimboe@kernel.org>
---
arch/arm64/include/asm/alternative-macros.h | 27 ++++++++++++++++-----
arch/arm64/include/asm/asm-bug.h | 2 ++
arch/arm64/include/asm/asm-extable.h | 21 ++++++++++------
arch/arm64/include/asm/jump_label.h | 2 ++
arch/arm64/kernel/asm-offsets.c | 5 ++++
include/linux/annotate.h | 14 ++++++++++-
include/linux/objtool_types.h | 1 +
tools/include/linux/objtool_types.h | 1 +
tools/objtool/klp-diff.c | 5 +++-
9 files changed, 62 insertions(+), 16 deletions(-)
diff --git a/arch/arm64/include/asm/alternative-macros.h b/arch/arm64/include/asm/alternative-macros.h
index 8624166248528..ba86d655af1d7 100644
--- a/arch/arm64/include/asm/alternative-macros.h
+++ b/arch/arm64/include/asm/alternative-macros.h
@@ -3,11 +3,16 @@
#define __ASM_ALTERNATIVE_MACROS_H
#include <linux/const.h>
+#include <linux/annotate.h>
#include <vdso/bits.h>
#include <asm/cpucaps.h>
#include <asm/insn-def.h>
+#ifndef COMPILE_OFFSETS
+#include <asm/asm-offsets.h>
+#endif
+
/*
* Binutils 2.27.0 can't handle a 'UL' suffix on constants, so for the assembly
* macros below we must use we must use `(1 << ARM64_CB_SHIFT)`.
@@ -58,15 +63,18 @@
"661:\n\t" \
oldinstr "\n" \
"662:\n" \
- ".pushsection .altinstructions,\"a\"\n" \
+ ".pushsection .altinstructions,\"aM\", @progbits, " \
+ __stringify(ALT_INSTR_SIZE) "\n" \
ALTINSTR_ENTRY(cpucap) \
".popsection\n" \
".subsection 1\n" \
+ ANNOTATE_DATA_SPECIAL "\n" \
"663:\n\t" \
newinstr "\n" \
"664:\n\t" \
".org . - (664b-663b) + (662b-661b)\n\t" \
".org . - (662b-661b) + (664b-663b)\n\t" \
+ ANNOTATE_DATA_SPECIAL_END "\n\t" \
".previous\n" \
".endif\n"
@@ -75,7 +83,8 @@
"661:\n\t" \
oldinstr "\n" \
"662:\n" \
- ".pushsection .altinstructions,\"a\"\n" \
+ ".pushsection .altinstructions,\"aM\", @progbits, " \
+ __stringify(ALT_INSTR_SIZE) "\n" \
ALTINSTR_ENTRY_CB(cpucap, cb) \
".popsection\n" \
"663:\n\t" \
@@ -102,13 +111,15 @@
.macro alternative_insn insn1, insn2, cap, enable = 1
.if \enable
661: \insn1
-662: .pushsection .altinstructions, "a"
+662: .pushsection .altinstructions, "aM", @progbits, ALT_INSTR_SIZE
altinstruction_entry 661b, 663f, \cap, 662b-661b, 664f-663f
.popsection
.subsection 1
+ ANNOTATE_DATA_SPECIAL
663: \insn2
664: .org . - (664b-663b) + (662b-661b)
.org . - (662b-661b) + (664b-663b)
+ ANNOTATE_DATA_SPECIAL_END
.previous
.endif
.endm
@@ -137,7 +148,7 @@
*/
.macro alternative_if_not cap
.set .Lasm_alt_mode, 0
- .pushsection .altinstructions, "a"
+ .pushsection .altinstructions, "aM", @progbits, ALT_INSTR_SIZE
altinstruction_entry 661f, 663f, \cap, 662f-661f, 664f-663f
.popsection
661:
@@ -145,17 +156,18 @@
.macro alternative_if cap
.set .Lasm_alt_mode, 1
- .pushsection .altinstructions, "a"
+ .pushsection .altinstructions, "aM", @progbits, ALT_INSTR_SIZE
altinstruction_entry 663f, 661f, \cap, 664f-663f, 662f-661f
.popsection
.subsection 1
.align 2 /* So GAS knows label 661 is suitably aligned */
+ ANNOTATE_DATA_SPECIAL
661:
.endm
.macro alternative_cb cap, cb
.set .Lasm_alt_mode, 0
- .pushsection .altinstructions, "a"
+ .pushsection .altinstructions, "aM", @progbits, ALT_INSTR_SIZE
altinstruction_entry 661f, \cb, (1 << ARM64_CB_SHIFT) | \cap, 662f-661f, 0
.popsection
661:
@@ -168,7 +180,9 @@
662:
.if .Lasm_alt_mode==0
.subsection 1
+ ANNOTATE_DATA_SPECIAL
.else
+ ANNOTATE_DATA_SPECIAL_END
.previous
.endif
663:
@@ -182,6 +196,7 @@
.org . - (664b-663b) + (662b-661b)
.org . - (662b-661b) + (664b-663b)
.if .Lasm_alt_mode==0
+ ANNOTATE_DATA_SPECIAL_END
.previous
.endif
.endm
diff --git a/arch/arm64/include/asm/asm-bug.h b/arch/arm64/include/asm/asm-bug.h
index a5f13801b7840..22e1a9df9851d 100644
--- a/arch/arm64/include/asm/asm-bug.h
+++ b/arch/arm64/include/asm/asm-bug.h
@@ -5,6 +5,7 @@
*/
#define __ASM_ASM_BUG_H
+#include <linux/annotate.h>
#include <asm/brk-imm.h>
#ifdef CONFIG_DEBUG_BUGVERBOSE
@@ -24,6 +25,7 @@
#define __BUG_ENTRY_START \
.pushsection __bug_table,"aw"; \
.align 2; \
+ __ANNOTATE_DATA_SPECIAL; \
14470: .long 14471f - .; \
#define __BUG_ENTRY_END \
diff --git a/arch/arm64/include/asm/asm-extable.h b/arch/arm64/include/asm/asm-extable.h
index d67e2fdd1aee5..e81700edbb936 100644
--- a/arch/arm64/include/asm/asm-extable.h
+++ b/arch/arm64/include/asm/asm-extable.h
@@ -5,6 +5,10 @@
#include <linux/bits.h>
#include <asm/gpr-num.h>
+#ifndef COMPILE_OFFSETS
+#include <asm/asm-offsets.h>
+#endif
+
#define EX_TYPE_NONE 0
#define EX_TYPE_BPF 1
#define EX_TYPE_UACCESS_ERR_ZERO 2
@@ -29,13 +33,13 @@
#ifdef __ASSEMBLER__
-#define __ASM_EXTABLE_RAW(insn, fixup, type, data) \
- .pushsection __ex_table, "a"; \
- .align 2; \
- .long ((insn) - .); \
- .long ((fixup) - .); \
- .short (type); \
- .short (data); \
+#define __ASM_EXTABLE_RAW(insn, fixup, type, data) \
+ .pushsection __ex_table, "aM", @progbits, EXTABLE_SIZE; \
+ .align 2; \
+ .long ((insn) - .); \
+ .long ((fixup) - .); \
+ .short (type); \
+ .short (data); \
.popsection;
#define EX_DATA_REG(reg, gpr) \
@@ -82,7 +86,8 @@
#include <linux/stringify.h>
#define __ASM_EXTABLE_RAW(insn, fixup, type, data) \
- ".pushsection __ex_table, \"a\"\n" \
+ ".pushsection __ex_table, \"aM\", @progbits, "\
+ __stringify(EXTABLE_SIZE) "\n" \
".align 2\n" \
".long ((" insn ") - .)\n" \
".long ((" fixup ") - .)\n" \
diff --git a/arch/arm64/include/asm/jump_label.h b/arch/arm64/include/asm/jump_label.h
index 0cb211d3607d3..4dacb28641d72 100644
--- a/arch/arm64/include/asm/jump_label.h
+++ b/arch/arm64/include/asm/jump_label.h
@@ -11,6 +11,7 @@
#ifndef __ASSEMBLER__
#include <linux/types.h>
+#include <linux/annotate.h>
#include <asm/insn.h>
#define HAVE_JUMP_LABEL_BATCH
@@ -19,6 +20,7 @@
#define JUMP_TABLE_ENTRY(key, label) \
".pushsection __jump_table, \"aw\"\n\t" \
".align 3\n\t" \
+ ANNOTATE_DATA_SPECIAL "\n\t" \
".long 1b - ., " label " - .\n\t" \
".quad " key " - .\n\t" \
".popsection\n\t"
diff --git a/arch/arm64/kernel/asm-offsets.c b/arch/arm64/kernel/asm-offsets.c
index 44b92f582c127..76251586e31c7 100644
--- a/arch/arm64/kernel/asm-offsets.c
+++ b/arch/arm64/kernel/asm-offsets.c
@@ -23,6 +23,8 @@
#include <asm/suspend.h>
#include <linux/kbuild.h>
#include <linux/arm-smccc.h>
+#include <asm/alternative.h>
+#include <asm/extable.h>
int main(void)
{
@@ -185,5 +187,8 @@ int main(void)
#endif
DEFINE(PIE_E0_ASM, PIE_E0);
DEFINE(PIE_E1_ASM, PIE_E1);
+ BLANK();
+ DEFINE(ALT_INSTR_SIZE, sizeof(struct alt_instr));
+ DEFINE(EXTABLE_SIZE, sizeof(struct exception_table_entry));
return 0;
}
diff --git a/include/linux/annotate.h b/include/linux/annotate.h
index 2f1599c9e5732..7f5aa15f353d6 100644
--- a/include/linux/annotate.h
+++ b/include/linux/annotate.h
@@ -3,6 +3,7 @@
#define _LINUX_ANNOTATE_H
#include <linux/objtool_types.h>
+#include <linux/stringify.h>
#ifdef CONFIG_OBJTOOL
@@ -11,6 +12,10 @@
.long label - ., type; \
.popsection
+#define __ASM_ANNOTATE_DATA(type) \
+912: \
+ __ASM_ANNOTATE(.discard.annotate_data, 912b, type)
+
#ifndef __ASSEMBLY__
#define ASM_ANNOTATE_LABEL(label, type) \
@@ -39,6 +44,9 @@
#endif /* __ASSEMBLY__ */
#else /* !CONFIG_OBJTOOL */
+
+#define __ASM_ANNOTATE_DATA(type)
+
#ifndef __ASSEMBLY__
#define ASM_ANNOTATE_LABEL(label, type) ""
#define ASM_ANNOTATE(type)
@@ -106,10 +114,12 @@
#define ANNOTATE_NOCFI_SYM(sym) asm(ASM_ANNOTATE_LABEL(sym, ANNOTYPE_NOCFI))
/*
- * Annotate a special section entry. This emables livepatch module generation
+ * Annotate a special section entry. This enables livepatch module generation
* to find and extract individual special section entries as needed.
*/
#define ANNOTATE_DATA_SPECIAL ASM_ANNOTATE_DATA(ANNOTYPE_DATA_SPECIAL)
+#define __ANNOTATE_DATA_SPECIAL __ASM_ANNOTATE_DATA(ANNOTYPE_DATA_SPECIAL)
+#define ANNOTATE_DATA_SPECIAL_END ASM_ANNOTATE_DATA(ANNOTYPE_DATA_SPECIAL_END)
#else /* __ASSEMBLY__ */
#define ANNOTATE_NOENDBR ANNOTATE type=ANNOTYPE_NOENDBR
@@ -122,6 +132,8 @@
#define ANNOTATE_REACHABLE ANNOTATE type=ANNOTYPE_REACHABLE
#define ANNOTATE_NOCFI_SYM ANNOTATE type=ANNOTYPE_NOCFI
#define ANNOTATE_DATA_SPECIAL ANNOTATE_DATA type=ANNOTYPE_DATA_SPECIAL
+#define __ANNOTATE_DATA_SPECIAL __ASM_ANNOTATE_DATA(ANNOTYPE_DATA_SPECIAL)
+#define ANNOTATE_DATA_SPECIAL_END ANNOTATE_DATA type=ANNOTYPE_DATA_SPECIAL_END
#endif /* __ASSEMBLY__ */
#endif /* _LINUX_ANNOTATE_H */
diff --git a/include/linux/objtool_types.h b/include/linux/objtool_types.h
index c6def4049b1ae..744118ffd025f 100644
--- a/include/linux/objtool_types.h
+++ b/include/linux/objtool_types.h
@@ -68,5 +68,6 @@ struct unwind_hint {
#define ANNOTYPE_NOCFI 9
#define ANNOTYPE_DATA_SPECIAL 1
+#define ANNOTYPE_DATA_SPECIAL_END 2
#endif /* _LINUX_OBJTOOL_TYPES_H */
diff --git a/tools/include/linux/objtool_types.h b/tools/include/linux/objtool_types.h
index c6def4049b1ae..744118ffd025f 100644
--- a/tools/include/linux/objtool_types.h
+++ b/tools/include/linux/objtool_types.h
@@ -68,5 +68,6 @@ struct unwind_hint {
#define ANNOTYPE_NOCFI 9
#define ANNOTYPE_DATA_SPECIAL 1
+#define ANNOTYPE_DATA_SPECIAL_END 2
#endif /* _LINUX_OBJTOOL_TYPES_H */
diff --git a/tools/objtool/klp-diff.c b/tools/objtool/klp-diff.c
index f8787d7d14547..6a1cec57dc6a3 100644
--- a/tools/objtool/klp-diff.c
+++ b/tools/objtool/klp-diff.c
@@ -1667,7 +1667,10 @@ static int create_fake_symbols(struct elf *elf)
size = 0;
next_reloc = reloc;
for_each_reloc_continue(sec->rsec, next_reloc) {
- if (annotype(elf, sec, next_reloc) != ANNOTYPE_DATA_SPECIAL ||
+ unsigned int next_type = annotype(elf, sec, next_reloc);
+
+ if ((next_type != ANNOTYPE_DATA_SPECIAL &&
+ next_type != ANNOTYPE_DATA_SPECIAL_END) ||
next_reloc->sym->sec != reloc->sym->sec)
continue;
--
2.53.0
^ permalink raw reply related
* [PATCH v3 05/21] arm64: vdso: Discard .discard.* sections
From: Josh Poimboeuf @ 2026-05-13 3:33 UTC (permalink / raw)
To: x86
Cc: linux-kernel, live-patching, Peter Zijlstra, Joe Lawrence,
Song Liu, Catalin Marinas, Will Deacon, linux-arm-kernel,
Mark Rutland, Miroslav Benes, Petr Mladek
In-Reply-To: <cover.1778642120.git.jpoimboe@kernel.org>
In preparation for enabling objtool on arm64, add .discard.* to the
vDSO's /DISCARD/ section so objtool annotations don't cause orphan
section warnings or leak into the final vDSO binary.
Signed-off-by: Josh Poimboeuf <jpoimboe@kernel.org>
---
arch/arm64/kernel/vdso/vdso.lds.S | 1 +
1 file changed, 1 insertion(+)
diff --git a/arch/arm64/kernel/vdso/vdso.lds.S b/arch/arm64/kernel/vdso/vdso.lds.S
index 52314be291912..d5f96fa17e605 100644
--- a/arch/arm64/kernel/vdso/vdso.lds.S
+++ b/arch/arm64/kernel/vdso/vdso.lds.S
@@ -39,6 +39,7 @@ SECTIONS
/DISCARD/ : {
*(.note.GNU-stack .note.gnu.property)
*(.ARM.attributes)
+ *(.discard.*)
}
.note : { *(.note.*) } :text :note
--
2.53.0
^ permalink raw reply related
* [PATCH v3 04/21] arm64: Rename TRAMP_VALIAS -> TRAMP_VALIAS_ASM in asm-offsets
From: Josh Poimboeuf @ 2026-05-13 3:33 UTC (permalink / raw)
To: x86
Cc: linux-kernel, live-patching, Peter Zijlstra, Joe Lawrence,
Song Liu, Catalin Marinas, Will Deacon, linux-arm-kernel,
Mark Rutland, Miroslav Benes, Petr Mladek
In-Reply-To: <cover.1778642120.git.jpoimboe@kernel.org>
Rename the asm-offsets TRAMP_VALIAS macro to TRAMP_VALIAS_ASM, following
the naming convention already used by PIE_E0_ASM and PIE_E1_ASM. This
disambiguates the asm-offsets-generated constant from the C macro of the
same name defined in fixmap.h and vectors.h.
This is needed by a later patch which adds new includes to asm-offsets.c
that would otherwise conflict with the C version.
Signed-off-by: Josh Poimboeuf <jpoimboe@kernel.org>
---
arch/arm64/kernel/asm-offsets.c | 2 +-
arch/arm64/kernel/entry.S | 8 ++++----
2 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/arch/arm64/kernel/asm-offsets.c b/arch/arm64/kernel/asm-offsets.c
index b6367ff3a49ca..44b92f582c127 100644
--- a/arch/arm64/kernel/asm-offsets.c
+++ b/arch/arm64/kernel/asm-offsets.c
@@ -153,7 +153,7 @@ int main(void)
DEFINE(ARM64_FTR_SYSVAL, offsetof(struct arm64_ftr_reg, sys_val));
BLANK();
#ifdef CONFIG_UNMAP_KERNEL_AT_EL0
- DEFINE(TRAMP_VALIAS, TRAMP_VALIAS);
+ DEFINE(TRAMP_VALIAS_ASM, TRAMP_VALIAS);
#endif
#ifdef CONFIG_ARM_SDE_INTERFACE
DEFINE(SDEI_EVENT_INTREGS, offsetof(struct sdei_registered_event, interrupted_regs));
diff --git a/arch/arm64/kernel/entry.S b/arch/arm64/kernel/entry.S
index d4cbdfb23d733..85f6305c1f568 100644
--- a/arch/arm64/kernel/entry.S
+++ b/arch/arm64/kernel/entry.S
@@ -102,7 +102,7 @@
.endm
.macro tramp_alias, dst, sym
- .set .Lalias\@, TRAMP_VALIAS + \sym - .entry.tramp.text
+ .set .Lalias\@, TRAMP_VALIAS_ASM + \sym - .entry.tramp.text
movz \dst, :abs_g2_s:.Lalias\@
movk \dst, :abs_g1_nc:.Lalias\@
movk \dst, :abs_g0_nc:.Lalias\@
@@ -626,10 +626,10 @@ SYM_CODE_END(ret_to_user)
#ifdef CONFIG_QCOM_FALKOR_ERRATUM_1003
alternative_if ARM64_WORKAROUND_QCOM_FALKOR_E1003
/* ASID already in \tmp[63:48] */
- movk \tmp, #:abs_g2_nc:(TRAMP_VALIAS >> 12)
- movk \tmp, #:abs_g1_nc:(TRAMP_VALIAS >> 12)
+ movk \tmp, #:abs_g2_nc:(TRAMP_VALIAS_ASM >> 12)
+ movk \tmp, #:abs_g1_nc:(TRAMP_VALIAS_ASM >> 12)
/* 2MB boundary containing the vectors, so we nobble the walk cache */
- movk \tmp, #:abs_g0_nc:((TRAMP_VALIAS & ~(SZ_2M - 1)) >> 12)
+ movk \tmp, #:abs_g0_nc:((TRAMP_VALIAS_ASM & ~(SZ_2M - 1)) >> 12)
isb
tlbi vae1, \tmp
dsb nsh
--
2.53.0
^ permalink raw reply related
* [PATCH v3 03/21] arm64: Fix EFI linking with -fdata-sections
From: Josh Poimboeuf @ 2026-05-13 3:33 UTC (permalink / raw)
To: x86
Cc: linux-kernel, live-patching, Peter Zijlstra, Joe Lawrence,
Song Liu, Catalin Marinas, Will Deacon, linux-arm-kernel,
Mark Rutland, Miroslav Benes, Petr Mladek
In-Reply-To: <cover.1778642120.git.jpoimboe@kernel.org>
When building with -fdata-sections, the .init.bss section gets split up
into a bunch of .init.bss.<var> sections. Make sure they get linked
into .init.data.
Acked-by: Song Liu <song@kernel.org>
Signed-off-by: Josh Poimboeuf <jpoimboe@kernel.org>
---
arch/arm64/kernel/vmlinux.lds.S | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/arch/arm64/kernel/vmlinux.lds.S b/arch/arm64/kernel/vmlinux.lds.S
index e1ac876200a3d..1ad7e3dba460a 100644
--- a/arch/arm64/kernel/vmlinux.lds.S
+++ b/arch/arm64/kernel/vmlinux.lds.S
@@ -281,7 +281,7 @@ SECTIONS
INIT_CALLS
CON_INITCALL
INIT_RAM_FS
- *(.init.altinstructions .init.bss) /* from the EFI stub */
+ *(.init.altinstructions .init.bss .init.bss.*) /* from the EFI stub */
}
.exit.data : {
EXIT_DATA
--
2.53.0
^ permalink raw reply related
* [PATCH v3 02/21] arm64: Annotate intra-function calls
From: Josh Poimboeuf @ 2026-05-13 3:33 UTC (permalink / raw)
To: x86
Cc: linux-kernel, live-patching, Peter Zijlstra, Joe Lawrence,
Song Liu, Catalin Marinas, Will Deacon, linux-arm-kernel,
Mark Rutland, Miroslav Benes, Petr Mladek
In-Reply-To: <cover.1778642120.git.jpoimboe@kernel.org>
In preparation for enabling objtool on arm64, annotate intra-function
calls.
Acked-by: Song Liu <song@kernel.org>
Signed-off-by: Josh Poimboeuf <jpoimboe@kernel.org>
---
arch/arm64/kernel/entry.S | 2 ++
arch/arm64/kernel/proton-pack.c | 12 +++++++-----
2 files changed, 9 insertions(+), 5 deletions(-)
diff --git a/arch/arm64/kernel/entry.S b/arch/arm64/kernel/entry.S
index e0db14e9c843a..d4cbdfb23d733 100644
--- a/arch/arm64/kernel/entry.S
+++ b/arch/arm64/kernel/entry.S
@@ -10,6 +10,7 @@
#include <linux/arm-smccc.h>
#include <linux/init.h>
#include <linux/linkage.h>
+#include <linux/annotate.h>
#include <asm/alternative.h>
#include <asm/assembler.h>
@@ -705,6 +706,7 @@ alternative_else_nop_endif
* entry onto the return stack and using a RET instruction to
* enter the full-fat kernel vectors.
*/
+ ANNOTATE_INTRA_FUNCTION_CALL
bl 2f
b .
2:
diff --git a/arch/arm64/kernel/proton-pack.c b/arch/arm64/kernel/proton-pack.c
index b3801f532b10b..b63887a1b8234 100644
--- a/arch/arm64/kernel/proton-pack.c
+++ b/arch/arm64/kernel/proton-pack.c
@@ -24,6 +24,7 @@
#include <linux/nospec.h>
#include <linux/prctl.h>
#include <linux/sched/task_stack.h>
+#include <linux/annotate.h>
#include <asm/debug-monitors.h>
#include <asm/insn.h>
@@ -245,11 +246,12 @@ static noinstr void qcom_link_stack_sanitisation(void)
{
u64 tmp;
- asm volatile("mov %0, x30 \n"
- ".rept 16 \n"
- "bl . + 4 \n"
- ".endr \n"
- "mov x30, %0 \n"
+ asm volatile("mov %0, x30 \n"
+ ".rept 16 \n"
+ ANNOTATE_INTRA_FUNCTION_CALL " \n"
+ "bl . + 4 \n"
+ ".endr \n"
+ "mov x30, %0 \n"
: "=&r" (tmp));
}
--
2.53.0
^ permalink raw reply related
* [PATCH v3 01/21] klp-build: Reject patches to init/*.c
From: Josh Poimboeuf @ 2026-05-13 3:33 UTC (permalink / raw)
To: x86
Cc: linux-kernel, live-patching, Peter Zijlstra, Joe Lawrence,
Song Liu, Catalin Marinas, Will Deacon, linux-arm-kernel,
Mark Rutland, Miroslav Benes, Petr Mladek
In-Reply-To: <cover.1778642120.git.jpoimboe@kernel.org>
init/Makefile hard-codes -fno-function-sections and -fno-data-sections,
overriding the klp-build flags needed for patch generation.
Don't allow any changes to those files; being init code they aren't
really patchable anyway.
Signed-off-by: Josh Poimboeuf <jpoimboe@kernel.org>
---
scripts/livepatch/klp-build | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/scripts/livepatch/klp-build b/scripts/livepatch/klp-build
index c4a7acf8edc3f..911ada05673c2 100755
--- a/scripts/livepatch/klp-build
+++ b/scripts/livepatch/klp-build
@@ -362,7 +362,7 @@ check_unsupported_patches() {
for file in "${files[@]}"; do
case "$file" in
- lib/*|*/vdso/*|*/realmode/rm/*|*.S)
+ lib/*|*/vdso/*|*/realmode/rm/*|init/*|*.S)
die "${patch}: unsupported patch to $file"
;;
esac
--
2.53.0
^ permalink raw reply related
* [PATCH v3 00/21] objtool/arm64: Port klp-build to arm64
From: Josh Poimboeuf @ 2026-05-13 3:33 UTC (permalink / raw)
To: x86
Cc: linux-kernel, live-patching, Peter Zijlstra, Joe Lawrence,
Song Liu, Catalin Marinas, Will Deacon, linux-arm-kernel,
Mark Rutland, Miroslav Benes, Petr Mladek
Based on tip/objtool/core.
v3:
- Too many changes to list. Did a lot of testing and fixed a bunch of
issues (many of which have already been merged in tip/objtool/core).
v2: https://lore.kernel.org/cover.1773787568.git.jpoimboe@kernel.org
- patches 1-2 were merged, rebase on tip/master
- improve commit message for "objtool: Extricate checksum calculation from validate_branch()"
- add review tags
v1: https://lore.kernel.org/cover.1772681234.git.jpoimboe@kernel.org
Port objtool and the klp-build tooling (for building livepatch modules)
to arm64.
Note this doesn't bring all the objtool bells and whistles to arm64, nor
any of the CFG reverse engineering. This only adds the bare minimum
needed for 'objtool --checksum'.
And note that objtool still doesn't get enabled at all for normal arm64
kernel builds, so this doesn't affect any users other than those running
klp-build directly.
Josh Poimboeuf (21):
klp-build: Reject patches to init/*.c
arm64: Annotate intra-function calls
arm64: Fix EFI linking with -fdata-sections
arm64: Rename TRAMP_VALIAS -> TRAMP_VALIAS_ASM in asm-offsets
arm64: vdso: Discard .discard.* sections
arm64: Annotate special section entries
crypto: arm64: Move data to .rodata
objtool: Allow setting --mnop without --mcount
kbuild: Only run objtool if there is at least one command
objtool: Ignore jumps to the end of the function for checksum runs
objtool: Allow empty alternatives
objtool: Refactor elf_add_data() to use a growable data buffer
objtool: Reuse string references
objtool: Prevent kCFI hashes from being decoded as instructions
objtool/klp: Add arm64 support for prefix/PFE detection
objtool/klp: Filter arm64 mapping symbols in find_symbol_by_offset()
objtool/klp: Don't correlate arm64 mapping symbols
objtool/klp: Clone inline alternative replacements
objtool/klp: Introduce objtool for arm64
klp-build: Support cross-compilation
klp-build: Add arm64 syscall patching macro
arch/arm64/Kconfig | 2 +
arch/arm64/include/asm/alternative-macros.h | 27 +-
arch/arm64/include/asm/asm-bug.h | 2 +
arch/arm64/include/asm/asm-extable.h | 21 +-
arch/arm64/include/asm/jump_label.h | 2 +
arch/arm64/kernel/asm-offsets.c | 7 +-
arch/arm64/kernel/entry.S | 10 +-
arch/arm64/kernel/proton-pack.c | 12 +-
arch/arm64/kernel/vdso/vdso.lds.S | 1 +
arch/arm64/kernel/vmlinux.lds.S | 2 +-
arch/x86/boot/startup/Makefile | 2 +-
include/linux/annotate.h | 14 +-
include/linux/livepatch_helpers.h | 19 ++
include/linux/objtool_types.h | 1 +
lib/crypto/arm64/sha2-armv8.pl | 18 +-
scripts/Makefile.build | 4 +-
scripts/Makefile.lib | 52 ++--
scripts/Makefile.vmlinux_o | 15 +-
scripts/livepatch/klp-build | 24 +-
tools/include/linux/objtool_types.h | 1 +
tools/objtool/Makefile | 4 +
tools/objtool/arch/arm64/Build | 2 +
tools/objtool/arch/arm64/decode.c | 177 +++++++++++++
.../arch/arm64/include/arch/cfi_regs.h | 11 +
tools/objtool/arch/arm64/include/arch/elf.h | 15 ++
.../objtool/arch/arm64/include/arch/special.h | 21 ++
tools/objtool/arch/arm64/special.c | 21 ++
tools/objtool/arch/x86/include/arch/elf.h | 2 +
tools/objtool/builtin-check.c | 5 -
tools/objtool/check.c | 65 +++--
tools/objtool/elf.c | 170 +++++++------
tools/objtool/include/objtool/elf.h | 48 +++-
tools/objtool/klp-diff.c | 237 ++++++++++++++++--
33 files changed, 819 insertions(+), 195 deletions(-)
create mode 100644 tools/objtool/arch/arm64/Build
create mode 100644 tools/objtool/arch/arm64/decode.c
create mode 100644 tools/objtool/arch/arm64/include/arch/cfi_regs.h
create mode 100644 tools/objtool/arch/arm64/include/arch/elf.h
create mode 100644 tools/objtool/arch/arm64/include/arch/special.h
create mode 100644 tools/objtool/arch/arm64/special.c
--
2.53.0
^ permalink raw reply
* Re: [RFC 0/4] klp-build: simple OOT module support
From: Song Liu @ 2026-05-12 23:30 UTC (permalink / raw)
To: Joe Lawrence; +Cc: live-patching, Josh Poimboeuf, Miroslav Benes, Petr Mladek
In-Reply-To: <20260512221102.2720763-1-joe.lawrence@redhat.com>
Hi Joe,
On Tue, May 12, 2026 at 3:11 PM Joe Lawrence <joe.lawrence@redhat.com> wrote:
>
> This patchset introduces support for patching basic out-of-tree (OOT)
> modules. The primary motivation is to streamline testing for objtool klp
> diff by providing a flexible and stable environment.
Thanks for this work!
I would like to add to the motivation here:
It is actually useful to livepatch OOT modules in production.
When using kpatch-build, we (Meta) generally do not patch OOT
modules. But there was one case where we had to do it, and it
required quite some manual work.
If we can make building livepatch for OOT easy, we are likely to
use it regularly in the future.
Thanks,
Song
[...]
^ permalink raw reply
* [RFC 4/4] livepatch/klp-build: add basic out-of-tree module patching support
From: Joe Lawrence @ 2026-05-12 22:11 UTC (permalink / raw)
To: live-patching; +Cc: Josh Poimboeuf, Song Liu, Miroslav Benes, Petr Mladek
In-Reply-To: <20260512221102.2720763-1-joe.lawrence@redhat.com>
klp-build is currently limited to patching in-tree kernel modules.
Introduce a -M/--module-dir option to enable livepatch generation for
basic out-of-tree (OOT) modules. This requires the associated kernel
tree to be pre-configured (e.g., 'make modules_prepare').
The OOT workflow is as follows:
cd /path/to/kernel
./scripts/livepatch/klp-build -M /path/to/mymodule my-fix.patch
With this option, klp-build performs two builds (original and patched)
of the OOT module via 'make M=...' instead of a full kernel rebuild.
The resulting objects are then processed and diffed to produce the
final livepatch .ko.
While this enhancement does not yet cover every OOT scenario, it
provides a functional baseline and enables OOT unit-testing for
the 'objtool klp diff' command.
Current limitations include:
- Separate build directories (make O=) are not yet supported.
- No passthrough for extra Kbuild variables (e.g., EXTRA_CFLAGS or
specific CONFIG_* overrides like DKMS supports).
- OOT module source must be contained within a single directory.
Multi-directory layouts are not handled.
Assisted-by: Cursor:claude-4.6-opus
Signed-off-by: Joe Lawrence <joe.lawrence@redhat.com>
---
scripts/livepatch/klp-build | 90 ++++++++++++++++++++++++++++---------
1 file changed, 69 insertions(+), 21 deletions(-)
diff --git a/scripts/livepatch/klp-build b/scripts/livepatch/klp-build
index 10145b1dd089..db4da64f2b9f 100755
--- a/scripts/livepatch/klp-build
+++ b/scripts/livepatch/klp-build
@@ -21,6 +21,7 @@ shopt -s lastpipe
unset DEBUG_CLONE DIFF_CHECKSUM SKIP_CLEANUP VERBOSE XTRACE
+MODULE_DIR=""
REPLACE=1
SHORT_CIRCUIT=0
JOBS="$(getconf _NPROCESSORS_ONLN)"
@@ -137,6 +138,7 @@ Options:
Advanced Options:
-d, --debug Show symbol/reloc cloning decisions
+ -M, --module-dir=<DIR> Out-of-tree module source directory
-S, --short-circuit=STEP Start at build step (requires prior --keep-tmp)
1|orig Build original kernel (default)
2|patched Build patched kernel
@@ -159,8 +161,8 @@ process_args() {
local args
local patch
- short="hfj:o:vdS:T"
- long="help,show-first-changed,jobs:,output:,no-replace,verbose,debug,short-circuit:,keep-tmp"
+ short="hfj:M:o:vdS:T"
+ long="help,show-first-changed,jobs:,module-dir:,output:,no-replace,verbose,debug,short-circuit:,keep-tmp"
args=$(getopt --options "$short" --longoptions "$long" -- "$@") || {
echo; usage; exit
@@ -202,6 +204,10 @@ process_args() {
keep_tmp=1
shift
;;
+ -M | --module-dir)
+ MODULE_DIR="$2"
+ shift 2
+ ;;
-S | --short-circuit)
[[ ! -d "$TMP_DIR" ]] && die "--short-circuit requires preserved klp-tmp dir"
keep_tmp=1
@@ -361,11 +367,21 @@ check_unsupported_patches() {
get_patch_files "$patch" | mapfile -t files
for file in "${files[@]}"; do
+ # In and out-of-tree paths to reject
case "$file" in
- lib/*|*/vdso/*|*/realmode/rm/*|*.S)
+ *.S)
die "${patch}: unsupported patch to $file"
;;
esac
+
+ # In-tree paths to reject (based on naming convention)
+ if [[ -z "$MODULE_DIR" ]]; then
+ case "$file" in
+ lib/*|*/vdso/*|*/realmode/rm/*)
+ die "${patch}: unsupported patch to $file"
+ ;;
+ esac
+ fi
done
done
}
@@ -374,13 +390,14 @@ apply_patch() {
local patch="$1"
shift
local extra_args=("$@")
+ local patch_target="${MODULE_DIR:-$PWD}"
local drift_regex="with fuzz|offset [0-9]+ line"
local output
local status
[[ ! -f "$patch" ]] && die "$patch doesn't exist"
status=0
- output=$(patch -p1 --dry-run --no-backup-if-mismatch -r /dev/null "${extra_args[@]}" < "$patch" 2>&1) || status=$?
+ output=$(patch -d "$patch_target" -p1 --dry-run --no-backup-if-mismatch -r /dev/null "${extra_args[@]}" < "$patch" 2>&1) || status=$?
if [[ "$status" -ne 0 ]]; then
echo "$output" >&2
die "$patch did not apply"
@@ -390,14 +407,15 @@ apply_patch() {
fi
APPLIED_PATCHES+=("$patch")
- patch -p1 --no-backup-if-mismatch -r /dev/null "${extra_args[@]}" --silent < "$patch"
+ patch -d "$patch_target" -p1 --no-backup-if-mismatch -r /dev/null "${extra_args[@]}" --silent < "$patch"
}
revert_patch() {
local patch="$1"
+ local patch_target="${MODULE_DIR:-$PWD}"
local tmp=()
- patch -p1 -R --force --no-backup-if-mismatch -r /dev/null &> /dev/null < "$patch" || true
+ patch -d "$patch_target" -p1 -R --force --no-backup-if-mismatch -r /dev/null &> /dev/null < "$patch" || true
for p in "${APPLIED_PATCHES[@]}"; do
[[ "$p" == "$patch" ]] && continue
@@ -452,8 +470,6 @@ cross_compile_init() {
}
do_init() {
- # We're not yet smart enough to handle anything other than in-tree
- # builds in pwd.
[[ ! "$PWD" -ef "$SCRIPT_DIR/../.." ]] && die "please run from the kernel root directory"
if (( SHORT_CIRCUIT >= 2 )); then
@@ -470,6 +486,15 @@ do_init() {
[[ -f "$DIFF_DIR/.complete" ]] || die "-S $SHORT_CIRCUIT requires completed $DIFF_DIR"
fi
+ if [[ -n "$MODULE_DIR" ]]; then
+ [[ -d "$MODULE_DIR" ]] || die "module directory not found: $MODULE_DIR"
+ MODULE_DIR="$(realpath "$MODULE_DIR")"
+ [[ -f "$MODULE_DIR/Kbuild" || -f "$MODULE_DIR/Makefile" ]] ||
+ die "no Kbuild or Makefile in $MODULE_DIR"
+ [[ -f "$PWD/Module.symvers" ]] ||
+ die "kernel must be built first (no Module.symvers in $PWD)"
+ fi
+
(( SHORT_CIRCUIT <= 1 )) && rm -rf "$TMP_DIR"
mkdir -p "$TMP_DIR"
@@ -488,6 +513,7 @@ do_init() {
refresh_patch() {
local patch="$1"
local tmpdir="$PATCH_TMP_DIR"
+ local patch_target="${MODULE_DIR:-$PWD}"
local input_files=()
local output_files=()
@@ -500,11 +526,11 @@ refresh_patch() {
get_patch_output_files "$patch" | mapfile -t output_files
# Copy orig source files to 'a'
- echo "${input_files[@]}" | xargs cp --parents --target-directory="$tmpdir/a"
+ ( cd "$patch_target" && echo "${input_files[@]}" | xargs cp --parents --target-directory="$tmpdir/a" )
# Copy patched source files to 'b'
apply_patch "$patch" "--silent"
- echo "${output_files[@]}" | xargs cp --parents --target-directory="$tmpdir/b"
+ ( cd "$patch_target" && echo "${output_files[@]}" | xargs cp --parents --target-directory="$tmpdir/b" )
revert_patch "$patch"
# Diff 'a' and 'b' to make a clean patch
@@ -546,6 +572,9 @@ clean_kernel() {
cmd=("make")
cmd+=("--silent")
cmd+=("-j$JOBS")
+ if [[ -n "$MODULE_DIR" ]]; then
+ cmd+=("M=$MODULE_DIR")
+ fi
cmd+=("clean")
"${cmd[@]}"
@@ -582,7 +611,11 @@ build_kernel() {
fi
cmd+=("-j$JOBS")
cmd+=("KCFLAGS=-ffunction-sections -fdata-sections")
- cmd+=("vmlinux")
+ if [[ -n "$MODULE_DIR" ]]; then
+ cmd+=("M=$MODULE_DIR")
+ else
+ cmd+=("vmlinux")
+ fi
cmd+=("modules")
"${cmd[@]}" \
@@ -594,12 +627,19 @@ build_kernel() {
find_objects() {
local opts=("$@")
- # Find root-level vmlinux.o and non-root-level .ko files,
- # excluding klp-tmp/ and .git/
- find "$PWD" \( -path "$TMP_DIR" -o -path "$PWD/.git" -o -regex "$PWD/[^/][^/]*\.ko" \) -prune -o \
- -type f "${opts[@]}" \
- \( -name "*.ko" -o -path "$PWD/vmlinux.o" \) \
- -printf '%P\n'
+ if [[ -n "$MODULE_DIR" ]]; then
+ # OOT: find .ko at any depth under the module dir
+ find "$MODULE_DIR" -path "$MODULE_DIR/.git" -prune -o \
+ -type f "${opts[@]}" \
+ -name "*.ko" -printf '%P\n'
+ else
+ # In-tree: find root-level vmlinux.o and non-root-level .ko files,
+ # excluding klp-tmp/ and .git/
+ find "$PWD" \( -path "$TMP_DIR" -o -path "$PWD/.git" -o -regex "$PWD/[^/][^/]*\.ko" \) -prune -o \
+ -type f "${opts[@]}" \
+ \( -name "*.ko" -o -path "$PWD/vmlinux.o" \) \
+ -printf '%P\n'
+ fi
}
# Copy all .o archives to $ORIG_DIR
@@ -611,10 +651,11 @@ copy_orig_objects() {
find_objects | mapfile -t files
+ local obj_root="${MODULE_DIR:-$PWD}"
xtrace_save "copying original objects"
for _file in "${files[@]}"; do
local rel_file="${_file/.ko/.o}"
- local file="$PWD/$rel_file"
+ local file="$obj_root/$rel_file"
local orig_file="$ORIG_DIR/$rel_file"
local orig_dir="$(dirname "$orig_file")"
@@ -645,10 +686,11 @@ copy_patched_objects() {
find_objects "${opts[@]}" | mapfile -t files
+ local obj_root="${MODULE_DIR:-$PWD}"
xtrace_save "copying changed objects"
for _file in "${files[@]}"; do
local rel_file="${_file/.ko/.o}"
- local file="$PWD/$rel_file"
+ local file="$obj_root/$rel_file"
local orig_file="$ORIG_DIR/$rel_file"
local patched_file="$PATCHED_DIR/$rel_file"
local patched_dir="$(dirname "$patched_file")"
@@ -727,6 +769,9 @@ diff_objects() {
cmd+=("klp")
cmd+=("diff")
(( ${#opts[@]} > 0 )) && cmd+=("${opts[@]}")
+
+ [[ -n "$MODULE_DIR" ]] && cmd+=("--symvers" "$PWD/Module.symvers")
+
cmd+=("$orig_file")
cmd+=("$patched_file")
cmd+=("$out_file")
@@ -905,13 +950,16 @@ build_patch_module() {
process_args "$@"
do_init
+BUILD_TARGET="kernel"
+[[ -n "$MODULE_DIR" ]] && BUILD_TARGET="module ${MODULE_DIR##*/}"
+
if (( SHORT_CIRCUIT <= 2 )); then
status "Validating patch(es)"
validate_patches
fi
if (( SHORT_CIRCUIT <= 1 )); then
- status "Building original kernel"
+ status "Building original $BUILD_TARGET"
clean_kernel
build_kernel "original"
status "Copying original object files"
@@ -922,7 +970,7 @@ if (( SHORT_CIRCUIT <= 2 )); then
status "Fixing patch(es)"
fix_patches
apply_patches "--silent"
- status "Building patched kernel"
+ status "Building patched $BUILD_TARGET"
build_kernel "patched"
revert_patches
status "Copying patched object files"
--
2.53.0
^ permalink raw reply related
* [RFC 2/4] objtool/klp: allow special section entry size overrides
From: Joe Lawrence @ 2026-05-12 22:11 UTC (permalink / raw)
To: live-patching; +Cc: Josh Poimboeuf, Song Liu, Miroslav Benes, Petr Mladek
In-Reply-To: <20260512221102.2720763-1-joe.lawrence@redhat.com>
Special section entry sizes (ALT_ENTRY_SIZE, JUMP_ENTRY_SIZE,
EX_ENTRY_SIZE) are built into objtool from arch-specific headers.
When processing cached unit test objects that were built from a
different kernel version, these compiled-in sizes may not match the
objects' actual entry sizes, causing create_fake_symbols() to
incorrectly parse special sections.
Allow the user to override the compiled-in defaults via environment
variables of the same name. When unset, behavior is unchanged. This
will enable a klp-diff unit test runner to pass the correct entry sizes
from test metadata.
Assisted-by: Cursor:claude-4.6-opus
Signed-off-by: Joe Lawrence <joe.lawrence@redhat.com>
---
tools/objtool/klp-diff.c | 31 +++++++++++++++++++++++++++++++
1 file changed, 31 insertions(+)
diff --git a/tools/objtool/klp-diff.c b/tools/objtool/klp-diff.c
index bd8d64f2f3f6..ebe4a2a087ca 100644
--- a/tools/objtool/klp-diff.c
+++ b/tools/objtool/klp-diff.c
@@ -1749,6 +1749,22 @@ static int clone_sym_relocs(struct elfs *e, struct symbol *patched_sym)
}
+static unsigned int entry_size_from_env(const char *name, unsigned int def)
+{
+ const char *str = getenv(name);
+ char *end;
+ unsigned long val;
+
+ if (!str)
+ return def;
+
+ val = strtoul(str, &end, 10);
+ if (*end || !val)
+ return def;
+
+ return val;
+}
+
static int create_fake_symbol(struct elf *elf, struct section *sec,
unsigned long offset, size_t size)
{
@@ -1871,6 +1887,21 @@ static int create_fake_symbols(struct elf *elf)
}
entry_size = sec->sh.sh_entsize;
+
+ /*
+ * Some special sections have multiple relocs per entry,
+ * so the reloc-based heuristic below doesn't work. Use
+ * the arch-defined entry sizes for known special sections.
+ */
+ if (!entry_size) {
+ if (!strcmp(sec->name, ".altinstructions"))
+ entry_size = entry_size_from_env("ALT_ENTRY_SIZE", ALT_ENTRY_SIZE);
+ else if (!strcmp(sec->name, "__jump_table"))
+ entry_size = entry_size_from_env("JUMP_ENTRY_SIZE", JUMP_ENTRY_SIZE);
+ else if (!strcmp(sec->name, "__ex_table"))
+ entry_size = entry_size_from_env("EX_ENTRY_SIZE", EX_ENTRY_SIZE);
+ }
+
if (!entry_size) {
entry_size = arch_reloc_size(sec->rsec->relocs);
if (sec_size(sec) != entry_size * sec_num_entries(sec->rsec)) {
--
2.53.0
^ permalink raw reply related
* [RFC 3/4] objtool/klp: add --arch option to display target architecture
From: Joe Lawrence @ 2026-05-12 22:11 UTC (permalink / raw)
To: live-patching; +Cc: Josh Poimboeuf, Song Liu, Miroslav Benes, Petr Mladek
In-Reply-To: <20260512221102.2720763-1-joe.lawrence@redhat.com>
objtool is built for a specific architecture determined by SRCARCH at
compile time. However, the binary currently provides no way to report
this target architecture, making it difficult for scripts or test suites
to verify compatibility before processing object files.
Pass SRCARCH as a preprocessor define and add a --arch (-a) option to
klp diff that prints the target architecture and exits:
$ objtool klp diff --arch
x86
This is mutually exclusive with normal operation: when --arch is given,
all other options and arguments are ignored.
Assisted-by: Cursor:claude-4.6-opus
Signed-off-by: Joe Lawrence <joe.lawrence@redhat.com>
---
tools/objtool/Makefile | 3 ++-
tools/objtool/klp-diff.c | 9 ++++++++-
2 files changed, 10 insertions(+), 2 deletions(-)
diff --git a/tools/objtool/Makefile b/tools/objtool/Makefile
index 94aabeee9736..79694444053c 100644
--- a/tools/objtool/Makefile
+++ b/tools/objtool/Makefile
@@ -65,7 +65,8 @@ INCLUDES := -I$(srctree)/tools/include \
-I$(srctree)/tools/lib
OBJTOOL_CFLAGS := -std=gnu11 -fomit-frame-pointer -O2 -g $(WARNINGS) \
- $(INCLUDES) $(LIBELF_FLAGS) $(LIBXXHASH_CFLAGS) $(HOSTCFLAGS)
+ $(INCLUDES) $(LIBELF_FLAGS) $(LIBXXHASH_CFLAGS) $(HOSTCFLAGS) \
+ -DOBJTOOL_ARCH=$(SRCARCH)
OBJTOOL_LDFLAGS := $(LIBSUBCMD) $(LIBELF_LIBS) $(LIBXXHASH_LIBS) $(HOSTLDFLAGS)
diff --git a/tools/objtool/klp-diff.c b/tools/objtool/klp-diff.c
index ebe4a2a087ca..8710ae8708df 100644
--- a/tools/objtool/klp-diff.c
+++ b/tools/objtool/klp-diff.c
@@ -35,7 +35,7 @@ struct export {
};
static const char *symvers_path = "Module.symvers";
-bool debug, debug_correlate, debug_clone;
+bool show_arch, debug, debug_correlate, debug_clone;
int indent;
static const char * const klp_diff_usage[] = {
@@ -45,6 +45,7 @@ static const char * const klp_diff_usage[] = {
static const struct option klp_diff_options[] = {
OPT_GROUP("Options:"),
+ OPT_BOOLEAN('a', "arch", &show_arch, "display target architecture"),
OPT_BOOLEAN('d', "debug", &debug, "enable all debug output"),
OPT_BOOLEAN(0, "debug-correlate", &debug_correlate, "enable correlation debug output"),
OPT_BOOLEAN(0, "debug-clone", &debug_clone, "enable cloning debug output"),
@@ -2366,6 +2367,12 @@ int cmd_klp_diff(int argc, const char **argv)
int ret;
argc = parse_options(argc, argv, klp_diff_options, klp_diff_usage, 0);
+
+ if (show_arch) {
+ printf("%s\n", __stringify(OBJTOOL_ARCH));
+ return 0;
+ }
+
if (argc != 3)
usage_with_options(klp_diff_usage, klp_diff_options);
--
2.53.0
^ permalink raw reply related
* [RFC 1/4] objtool/klp: add --symvers option to klp diff
From: Joe Lawrence @ 2026-05-12 22:10 UTC (permalink / raw)
To: live-patching; +Cc: Josh Poimboeuf, Song Liu, Miroslav Benes, Petr Mladek
In-Reply-To: <20260512221102.2720763-1-joe.lawrence@redhat.com>
Add a --symvers (-s) option that accepts a path to Module.symvers. When
provided, it replaces the default "Module.symvers" filename used by the
auto-detection logic.
This decoupling helps to enable scenarios like out-of-tree module
patching and unit testing of klp-diff, where object files /
Module.symvers live outside the kernel tree.
The existing auto-detection behavior (try cwd, then top_level_dir()
fallback) is preserved regardless of whether --symvers is specified.
Assisted-by: Cursor:claude-4.6-opus
Signed-off-by: Joe Lawrence <joe.lawrence@redhat.com>
---
tools/objtool/klp-diff.c | 9 +++++----
1 file changed, 5 insertions(+), 4 deletions(-)
diff --git a/tools/objtool/klp-diff.c b/tools/objtool/klp-diff.c
index b9624bd9439b..bd8d64f2f3f6 100644
--- a/tools/objtool/klp-diff.c
+++ b/tools/objtool/klp-diff.c
@@ -34,6 +34,7 @@ struct export {
char *mod, *sym;
};
+static const char *symvers_path = "Module.symvers";
bool debug, debug_correlate, debug_clone;
int indent;
@@ -47,6 +48,7 @@ static const struct option klp_diff_options[] = {
OPT_BOOLEAN('d', "debug", &debug, "enable all debug output"),
OPT_BOOLEAN(0, "debug-correlate", &debug_correlate, "enable correlation debug output"),
OPT_BOOLEAN(0, "debug-clone", &debug_clone, "enable cloning debug output"),
+ OPT_STRING('s', "symvers", &symvers_path, "path", "path to Module.symvers (default: Module.symvers)"),
OPT_END(),
};
@@ -86,16 +88,15 @@ static char *escape_str(const char *orig)
static int read_exports(void)
{
- const char *symvers = "Module.symvers";
char line[1024], *path = NULL;
unsigned int line_num = 1;
FILE *file;
- file = fopen(symvers, "r");
+ file = fopen(symvers_path, "r");
if (!file) {
- path = top_level_dir(symvers);
+ path = top_level_dir(symvers_path);
if (!path) {
- ERROR("can't open '%s', \"objtool diff\" should be run from the kernel tree", symvers);
+ ERROR("can't open '%s', \"objtool diff\" should be run from the kernel tree", symvers_path);
return -1;
}
--
2.53.0
^ permalink raw reply related
* [RFC 0/4] klp-build: simple OOT module support
From: Joe Lawrence @ 2026-05-12 22:10 UTC (permalink / raw)
To: live-patching; +Cc: Josh Poimboeuf, Song Liu, Miroslav Benes, Petr Mladek
This patchset introduces support for patching basic out-of-tree (OOT)
modules. The primary motivation is to streamline testing for objtool klp
diff by providing a flexible and stable environment.
While much of the objtool klp diff logic can be validated using standard
in-tree modules (avoiding the overhead of full kernel rebuilds), OOT
support provides significant value for reproducing and fixing specific
kernel code patterns. Standard in-tree drivers are subject to frequent
refactoring and API updates, making them unreliable for producing the
consistent binary patterns required for stable testing. While dedicated
test modules could be introduced into the kernel tree to ensure
stability, starting with OOT modules allows for faster iteration before
committing to a permanent in-tree landing spot.
For an example, I have inlined a module below used to verify Josh's
latest commit ("objtool/klp: Rewrite symbol correlation algorithm")
[1]. This specific test case confirms that his patch resolves the
thinLTO ambiguity issue originally reported by Song Liu in February [2].
[1] https://lore.kernel.org/live-patching/cq5uytz6edj75w53f2eubypvqm66hgh4eag7ec2vgqjefzzqts@lcnvt7fcrtmd/T/#mbfaf71b314b6600424f9c5504c415a2e3a87ade3
[2] https://lore.kernel.org/live-patching/20260226005436.379303-9-song@kernel.org
To make full use of the example OOT module, I have additional test-
harness code that automates fetching pre-built object files, integrating
with kselftests, etc. I chose to spin out this basic OOT support
first since it mostly stands alone and may have other helpful contexts.
-->8-- -->8-- -->8-- -->8-- -->8-- -->8-- -->8-- -->8-- -->8-- -->8--
==> Kbuild <==
obj-m := klp_test_ambig.o
klp_test_ambig-y := klp_test_ambig_a.o klp_test_ambig_b.o klp_test_ambig_c.o
==> klp_test_ambig_a.c <==
#include <linux/module.h>
#include <linux/kernel.h>
#include "klp_test_ambig.h"
static noinline int __helper(int x, int len)
{
int i, sum = x;
for (i = 0; i < len; i++)
sum += i + 5;
if (sum > 1000)
sum = 0;
return sum;
}
static int value_a;
int klp_test_ambig_func_a(int x)
{
value_a = __helper(value_a, x);
return value_a;
}
EXPORT_SYMBOL_GPL(klp_test_ambig_func_a);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("ThinLTO demangled ambiguity test module");
==> klp_test_ambig_b.c <==
#include <linux/module.h>
#include <linux/kernel.h>
#include "klp_test_ambig.h"
static noinline int __helper(int x, int len)
{
int i, sum = x;
for (i = 0; i < len; i++)
sum += i + 10;
if (sum > 1000)
sum = 0;
return sum;
}
static int value_b;
int klp_test_ambig_func_b(int x)
{
value_b = __helper(value_b, x);
return value_b;
}
EXPORT_SYMBOL_GPL(klp_test_ambig_func_b);
==> klp_test_ambig_c.c <==
#include <linux/module.h>
#include <linux/kernel.h>
#include "klp_test_ambig.h"
static int value_c;
int klp_test_ambig_func_c(int x)
{
value_c = klp_test_ambig_func_a(x) + klp_test_ambig_func_b(x);
return value_c;
}
EXPORT_SYMBOL_GPL(klp_test_ambig_func_c);
==> klp_test_ambig.h <==
#ifndef _KLP_TEST_AMBIG_H
#define _KLP_TEST_AMBIG_H
int klp_test_ambig_func_a(int x);
int klp_test_ambig_func_b(int x);
int klp_test_ambig_func_c(int x);
#endif
==> 0001-thin-lto-demangled-ambiguity.patch <==
From 2d208e686739b1ccccfe385e837b5a4a04a9526f Mon Sep 17 00:00:00 2001
From: klp-build-test <test@example.com>
Date: Sat, 28 Mar 2026 13:55:56 +0000
Subject: [PATCH] thin-lto-demangled-ambiguity
ThinLTO two __helper.llvm.* symbols with disjoint hashes; objtool correlates
---
klp_test_ambig_a.c | 2 +-
klp_test_ambig_b.c | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/klp_test_ambig_a.c b/klp_test_ambig_a.c
index cc4db70..4ee26a5 100644
--- a/klp_test_ambig_a.c
+++ b/klp_test_ambig_a.c
@@ -7,7 +7,7 @@ static noinline int __helper(int x, int len)
int i, sum = x;
for (i = 0; i < len; i++)
- sum += i + 5;
+ sum += i * 2 + 5;
if (sum > 1000)
sum = 0;
return sum;
diff --git a/klp_test_ambig_b.c b/klp_test_ambig_b.c
index ca114b0..1b7ce1a 100644
--- a/klp_test_ambig_b.c
+++ b/klp_test_ambig_b.c
@@ -7,7 +7,7 @@ static noinline int __helper(int x, int len)
int i, sum = x;
for (i = 0; i < len; i++)
- sum += i + 10;
+ sum += i * 2 + 10;
if (sum > 1000)
sum = 0;
return sum;
--
2.53.0
-->8-- -->8-- -->8-- -->8-- -->8-- -->8-- -->8-- -->8-- -->8-- -->8--
Applies on top of:
tree: git://git.kernel.org/pub/scm/linux/kernel/git/jpoimboe/linux.git
branch: klp-build-arm64
commit: fffce0ac08e0 ("klp-build: Add arm64 syscall patching macro")
Joe Lawrence (4):
objtool/klp: add --symvers option to klp diff
objtool/klp: allow special section entry size overrides
objtool/klp: add --arch option to display target architecture
livepatch/klp-build: add basic out-of-tree module patching support
scripts/livepatch/klp-build | 90 ++++++++++++++++++++++++++++---------
tools/objtool/Makefile | 3 +-
tools/objtool/klp-diff.c | 49 +++++++++++++++++---
3 files changed, 115 insertions(+), 27 deletions(-)
--
2.53.0
^ permalink raw reply related
* Re: [PATCH v5 8/8] unwind: arm64: Use sframe to unwind interrupt frames
From: Mark Rutland @ 2026-05-12 10:18 UTC (permalink / raw)
To: Jens Remus
Cc: Dylan Hatch, Roman Gushchin, Weinan Liu, Will Deacon,
Josh Poimboeuf, Indu Bhagat, Peter Zijlstra, Steven Rostedt,
Catalin Marinas, Jiri Kosina, Prasanna Kumar T S M,
Puranjay Mohan, Song Liu, joe.lawrence, linux-toolchains,
linux-kernel, live-patching, linux-arm-kernel, Randy Dunlap,
Heiko Carstens
In-Reply-To: <0542f042-14fb-4588-bc3a-5031249d9834@linux.ibm.com>
On Tue, May 12, 2026 at 10:55:28AM +0200, Jens Remus wrote:
> On 5/12/2026 5:00 AM, Dylan Hatch wrote:
> > On Fri, May 1, 2026 at 9:46 AM Mark Rutland <mark.rutland@arm.com>
> > wrote:
>
> >> (1) For correctness, we'll need to address a latent issue with
> >> unwinding across an fgraph return trampoline, where the return
> >> address is transiently unrecoverable.
> >> I think we can solve that with some restructuring of that code,
> >> restoring the original address *before* removing that from the
> >> fgraph return stack, and ensuring that the unwinder can find it.
> >
> > If my understanding is correct, the issue arrises in
> > return_to_handler as the return address is recovered:
> >
> > mov x0, sp bl ftrace_return_to_handler // addr =
> > ftrace_return_to_hander(fregs); mov x30, x0 // restore the original
> > return address
> >
> > Because ftrace_return_to_handler pops the return address from the
> > return stack before it can be restored into the LR, it cannot be
> > recovered.
>
> Based on reliable-stacktrace.rst section "4.4 Rewriting of return
> addresses" I wonder whether the following might work:
>
> - If an unwound RA points at return_to_handler the actual RA needs to
> be obtained using ftrace_graph_ret_addr(). This might already be
> taken into account if ftrace_graph_ret_addr() is used unconditionally.
>
> - If an unwound RA points into return_to_handler() mark the stack trace
> as unreliable. This could be accomplished by marking LR in
> return_to_handler() as undefined (i.e. .cfi_undefined 30) to use
> SFrame's outermost frame indication to stop and mark the stack trace
> as unreliable:
We don't currently have any CFI annotations for return_to_handler(), so
if we interrupt that, any unwind will naturally be marked as unreliable.
The problem is that we can try an unwind from an interrupted *callee* of
return_to_handler(). In that case, we'll unwind through
return_to_handler() using the frame pointer, without consulting SFrame.
In that case, the PC will be part-way through return_to_handler(), but
we only call ftrace_graph_ret_addr() when the PC is the start of
return_to_handler, and so we don't even try to recover the return
address.
We can handle that better by checking whether the PC is *within*
return_to_handler(), and aborting when the original return address
cannot be recoverted. I'm happy to go put that together, nad longer term
I would like to do the better reovery I described above such that we can
*always* recover the return address.
Mark.
^ permalink raw reply
* Re: [PATCH v5 8/8] unwind: arm64: Use sframe to unwind interrupt frames
From: Mark Rutland @ 2026-05-12 10:07 UTC (permalink / raw)
To: Dylan Hatch
Cc: Roman Gushchin, Weinan Liu, Will Deacon, Josh Poimboeuf,
Indu Bhagat, Peter Zijlstra, Steven Rostedt, Catalin Marinas,
Jiri Kosina, Jens Remus, Prasanna Kumar T S M, Puranjay Mohan,
Song Liu, joe.lawrence, linux-toolchains, linux-kernel,
live-patching, linux-arm-kernel, Randy Dunlap
In-Reply-To: <CADBMgpx9YxNUO6wLP7mYKxWW8L78Hk9gPwHrMjXUwPyUmGEu9w@mail.gmail.com>
On Mon, May 11, 2026 at 08:00:21PM -0700, Dylan Hatch wrote:
> Hi Mark,
>
> Thanks for all the feedback and help on this. I'm planning on getting
> your comments addressed in the coming days, but I have some initial
> clarifying questions.
>
> On Fri, May 1, 2026 at 9:46 AM Mark Rutland <mark.rutland@arm.com> wrote:
> >
> > Hi Dylan,
> >
> > Thanks for putting this together. I think this is looking pretty good.
> > However, there are some things that aren't quite right and need some
> > work, which I've commented on below.
> >
> > More generally, there are a few things that aren't addressed by this
> > series that we will also need to address. Importantly:
> >
> > (1) For correctness, we'll need to address a latent issue with unwinding
> > across an fgraph return trampoline, where the return address is
> > transiently unrecoverable.
> >
> > Before this series, that doesn't matter for livepatching because the
> > livepatching code isn't called synchronously within the fgraph
> > handler, and unwinds which cross an exception boundary are marked as
> > unreliable.
> >
> > After this series, that does matter as we can unwind across an
> > exception boundary, and might happen to interrupt that transient
> > window.
> >
> > I think we can solve that with some restructuring of that code,
> > restoring the original address *before* removing that from the
> > fgraph return stack, and ensuring that the unwinder can find it.
>
> If my understanding is correct, the issue arrises in return_to_handler
> as the return address is recovered:
>
> mov x0, sp
> bl ftrace_return_to_handler // addr = ftrace_return_to_hander(fregs);
> mov x30, x0 // restore the original return address
>
> Because ftrace_return_to_handler pops the return address from the
> return stack before it can be restored into the LR, it cannot be
> recovered.
Yes.
To be clear, please don't worry about solving that for the next version
of this series; let's get the SFrame bits into shape first. I just
wanted to highlight that there's some more general work that we'll need
to do.
I think we can *detect* this case (and mark the unwind as unreliable)
with some tiny changes to the arm64 code. I'm happy to put that
together.
> Based on this, I believe you are suggesting to restructure this code
> path such that the return address is removed from the return stack
> only after it has been restored to LR. But since kernel/trace/fgraph.c
> is core kernel code, will this end up requiring either (1) a similar
> restructuring of other arches supporting ftrace, or (2) an
> arm64-specific implementation of this recovery logic?
Yes, I am say that to *recover* the address we'd need to make changes to
core code.
In the mean time we can *detect* this case with some minimal changes to
arm64 code, and abort. As above, I'm happy to go put that together.
> It looks to me like there is essentially the same recovery pattern on
> other arches; is there a reason this transient unrecoverability isn't
> an issue for reliable unwind on other platforms?
Yep; on all architectures there's a transient period where the address
cannot be recovered. It's not a correctness issue so long as the
architecture detects this case and marks the unwind as unreliable.
IIUC x86 will mark the unwind as unreliable in this case.
I don't know whether other architectures detect this reliably. That's a
question for loonarch, parisc, powerpc, and s390 folk.
> > I'm not immediately sure whether kretprobes has a similar issue.
> >
> > (2) To make unwinding generally possible, we'll need to annotate some
> > assembly functions as unwindable. We'll need to do that for string
> > routines under lib/, and probably some crypto code, but we don't
> > need to do that for most code in head.S, entry.S, etc.
> >
> > The vast majority of relevant assembly functions are leaf functions
> > (where the return address is never moved out of the LR), so we can
> > probably get away with a simple annotation for those that avoids the
> > need for open-coded CFI directives everywhere.
>
> Are you suggesting something like a SYM_LEAF_FUNC_(START|END), that
> wraps CFI directives for leaf functions?
Yep; that's exactly the sort of thing I was thinking of.
That or have a seaprate annotation we can add, e.g.
SYM_FUNC_START(foo)
SYM_FUN_END(foo)
SYM_FUNC_IS_LEAF_AND_DOES_NOT_TOUCH_LR(foo)
> > I've pushed some reliable stacktrace tests to:
> >
> > git://git.kernel.org/pub/scm/linux/kernel/git/mark/linux.git stacktrace/tests
> >
> > That finds the fgraph issue (regardless of this series). When merged
> > with this series triggers a warning in kunwind_next_frame_record_meta(),
> > where unwind_next_frame_sframe() calls that erroneously as a fallback.
>
> Thanks for the pointer on these tests, they're super useful! I've been
> able to reproduce the fgraph failure you mentioned.
Great!
Mark.
^ permalink raw reply
* Re: [PATCH v5 8/8] unwind: arm64: Use sframe to unwind interrupt frames
From: Jens Remus @ 2026-05-12 8:55 UTC (permalink / raw)
To: Dylan Hatch, Mark Rutland
Cc: Roman Gushchin, Weinan Liu, Will Deacon, Josh Poimboeuf,
Indu Bhagat, Peter Zijlstra, Steven Rostedt, Catalin Marinas,
Jiri Kosina, Prasanna Kumar T S M, Puranjay Mohan, Song Liu,
joe.lawrence, linux-toolchains, linux-kernel, live-patching,
linux-arm-kernel, Randy Dunlap, Heiko Carstens
In-Reply-To: <CADBMgpx9YxNUO6wLP7mYKxWW8L78Hk9gPwHrMjXUwPyUmGEu9w@mail.gmail.com>
Hello Dylan and Mark!
On 5/12/2026 5:00 AM, Dylan Hatch wrote:
> On Fri, May 1, 2026 at 9:46 AM Mark Rutland <mark.rutland@arm.com>
> wrote:
>> More generally, there are a few things that aren't addressed by
>> this series that we will also need to address. Importantly:
>>
>> (1) For correctness, we'll need to address a latent issue with
>> unwinding across an fgraph return trampoline, where the return
>> address is transiently unrecoverable.
>>
>> Before this series, that doesn't matter for livepatching because
>> the livepatching code isn't called synchronously within the fgraph
>> handler, and unwinds which cross an exception boundary are marked
>> as unreliable.
>>
>> After this series, that does matter as we can unwind across an
>> exception boundary, and might happen to interrupt that transient
>> window.
>>
>> I think we can solve that with some restructuring of that code,
>> restoring the original address *before* removing that from the
>> fgraph return stack, and ensuring that the unwinder can find it.
>
> If my understanding is correct, the issue arrises in
> return_to_handler as the return address is recovered:
>
> mov x0, sp bl ftrace_return_to_handler // addr =
> ftrace_return_to_hander(fregs); mov x30, x0 // restore the original
> return address
>
> Because ftrace_return_to_handler pops the return address from the
> return stack before it can be restored into the LR, it cannot be
> recovered.
Based on reliable-stacktrace.rst section "4.4 Rewriting of return
addresses" I wonder whether the following might work:
- If an unwound RA points at return_to_handler the actual RA needs to
be obtained using ftrace_graph_ret_addr(). This might already be
taken into account if ftrace_graph_ret_addr() is used unconditionally.
- If an unwound RA points into return_to_handler() mark the stack trace
as unreliable. This could be accomplished by marking LR in
return_to_handler() as undefined (i.e. .cfi_undefined 30) to use
SFrame's outermost frame indication to stop and mark the stack trace
as unreliable:
diff --git a/arch/arm64/kernel/entry-ftrace.S b/arch/arm64/kernel/entry-ftrace.S
@@ -329,8 +329,12 @@ SYM_FUNC_END(ftrace_stub_graph)
* @fp is checked against the value passed by ftrace_graph_caller().
*/
SYM_CODE_START(return_to_handler)
+ .cfi_startproc
+ /* Mark unwinding of LR as unreliable */
+ .cfi_undefined 30
/* Make room for ftrace_regs */
sub sp, sp, #FREGS_SIZE
+ .cfi_adjust_cfa_offset -FREGS_SIZE
/* Save return value regs */
stp x0, x1, [sp, #FREGS_X0]
@@ -344,6 +348,8 @@ SYM_CODE_START(return_to_handler)
mov x0, sp
bl ftrace_return_to_handler // addr = ftrace_return_to_hander(fregs);
mov x30, x0 // restore the original return address
+ /* Mark unwinding of LR as reliable */
+ .cfi_restore 30
/* Restore return value regs */
ldp x0, x1, [sp, #FREGS_X0]
@@ -351,7 +357,9 @@ SYM_CODE_START(return_to_handler)
ldp x4, x5, [sp, #FREGS_X4]
ldp x6, x7, [sp, #FREGS_X6]
add sp, sp, #FREGS_SIZE
+ .cfi_adjust_cfa_offset FREGS_SIZE
ret
+ .cfi_endproc
SYM_CODE_END(return_to_handler)
#endif /* CONFIG_FUNCTION_GRAPH_TRACER */
Notes regarding above:
- return_to_handler() saves the caller's FP in ftrace_regs but never
restores it. I suppose this is because ftrace_regs is an input to
ftrace_return_to_handler(). The DWARF CFI above assumes SP and FP
can be restored at all times as SP=CFA and FP=FP.
- One might be tempted to add a .cfi_register 30, 0 after the call to
ftrace_return_to_handler. This would be wrong, because if unwinding
comes from ftrace_return_to_handler() the unwound RA will point there
and the unwinding logic would erroneously assume x0 to contain the RA.
- The DWARF CFI could be simplified as follows to just convey that
unwinding through return_to_handler is impossible at all times:
diff --git a/arch/arm64/kernel/entry-ftrace.S b/arch/arm64/kernel/entry-ftrace.S
@@ -329,6 +329,9 @@ SYM_FUNC_END(ftrace_stub_graph)
* @fp is checked against the value passed by ftrace_graph_caller().
*/
SYM_CODE_START(return_to_handler)
+ .cfi_startproc simple
+ /* Mark unwinding of LR as unreliable */
+ .cfi_undefined 30
/* Make room for ftrace_regs */
sub sp, sp, #FREGS_SIZE
@@ -353,5 +356,6 @@ SYM_CODE_START(return_to_handler)
add sp, sp, #FREGS_SIZE
ret
+ .cfi_endproc
SYM_CODE_END(return_to_handler)
#endif /* CONFIG_FUNCTION_GRAPH_TRACER */
>
> Based on this, I believe you are suggesting to restructure this code
> path such that the return address is removed from the return stack
> only after it has been restored to LR. But since kernel/trace/
> fgraph.c is core kernel code, will this end up requiring either (1)
> a similar restructuring of other arches supporting ftrace, or (2) an
> arm64-specific implementation of this recovery logic?
>
> It looks to me like there is essentially the same recovery pattern
> on other arches; is there a reason this transient unrecoverability
> isn't an issue for reliable unwind on other platforms?
>
>>
>> I'm not immediately sure whether kretprobes has a similar issue.
Regards,
Jens
--
Jens Remus
Linux on Z Development (D3303)
jremus@de.ibm.com / jremus@linux.ibm.com
IBM Deutschland Research & Development GmbH; Vorsitzender des Aufsichtsrats: Wolfgang Wendt; Geschäftsführung: David Faller; Sitz der Gesellschaft: Ehningen; Registergericht: Amtsgericht Stuttgart, HRB 243294
IBM Data Privacy Statement: https://www.ibm.com/privacy/
^ permalink raw reply
* Re: [PATCH v5 8/8] unwind: arm64: Use sframe to unwind interrupt frames
From: Dylan Hatch @ 2026-05-12 3:00 UTC (permalink / raw)
To: Mark Rutland
Cc: Roman Gushchin, Weinan Liu, Will Deacon, Josh Poimboeuf,
Indu Bhagat, Peter Zijlstra, Steven Rostedt, Catalin Marinas,
Jiri Kosina, Jens Remus, Prasanna Kumar T S M, Puranjay Mohan,
Song Liu, joe.lawrence, linux-toolchains, linux-kernel,
live-patching, linux-arm-kernel, Randy Dunlap
In-Reply-To: <afTYzAF_x41pyilu@J2N7QTR9R3>
Hi Mark,
Thanks for all the feedback and help on this. I'm planning on getting
your comments addressed in the coming days, but I have some initial
clarifying questions.
On Fri, May 1, 2026 at 9:46 AM Mark Rutland <mark.rutland@arm.com> wrote:
>
> Hi Dylan,
>
> Thanks for putting this together. I think this is looking pretty good.
> However, there are some things that aren't quite right and need some
> work, which I've commented on below.
>
> More generally, there are a few things that aren't addressed by this
> series that we will also need to address. Importantly:
>
> (1) For correctness, we'll need to address a latent issue with unwinding
> across an fgraph return trampoline, where the return address is
> transiently unrecoverable.
>
> Before this series, that doesn't matter for livepatching because the
> livepatching code isn't called synchronously within the fgraph
> handler, and unwinds which cross an exception boundary are marked as
> unreliable.
>
> After this series, that does matter as we can unwind across an
> exception boundary, and might happen to interrupt that transient
> window.
>
> I think we can solve that with some restructuring of that code,
> restoring the original address *before* removing that from the
> fgraph return stack, and ensuring that the unwinder can find it.
If my understanding is correct, the issue arrises in return_to_handler
as the return address is recovered:
mov x0, sp
bl ftrace_return_to_handler // addr = ftrace_return_to_hander(fregs);
mov x30, x0 // restore the original return address
Because ftrace_return_to_handler pops the return address from the
return stack before it can be restored into the LR, it cannot be
recovered.
Based on this, I believe you are suggesting to restructure this code
path such that the return address is removed from the return stack
only after it has been restored to LR. But since kernel/trace/fgraph.c
is core kernel code, will this end up requiring either (1) a similar
restructuring of other arches supporting ftrace, or (2) an
arm64-specific implementation of this recovery logic?
It looks to me like there is essentially the same recovery pattern on
other arches; is there a reason this transient unrecoverability isn't
an issue for reliable unwind on other platforms?
>
> I'm not immediately sure whether kretprobes has a similar issue.
>
> (2) To make unwinding generally possible, we'll need to annotate some
> assembly functions as unwindable. We'll need to do that for string
> routines under lib/, and probably some crypto code, but we don't
> need to do that for most code in head.S, entry.S, etc.
>
> The vast majority of relevant assembly functions are leaf functions
> (where the return address is never moved out of the LR), so we can
> probably get away with a simple annotation for those that avoids the
> need for open-coded CFI directives everywhere.
Are you suggesting something like a SYM_LEAF_FUNC_(START|END), that
wraps CFI directives for leaf functions?
>
> I've pushed some reliable stacktrace tests to:
>
> git://git.kernel.org/pub/scm/linux/kernel/git/mark/linux.git stacktrace/tests
>
> That finds the fgraph issue (regardless of this series). When merged
> with this series triggers a warning in kunwind_next_frame_record_meta(),
> where unwind_next_frame_sframe() calls that erroneously as a fallback.
Thanks for the pointer on these tests, they're super useful! I've been
able to reproduce the fgraph failure you mentioned.
Thanks,
Dylan
^ permalink raw reply
* Re: [PATCH v5 0/8] unwind, arm64: add sframe unwinder for kernel
From: Dylan Hatch @ 2026-05-12 1:10 UTC (permalink / raw)
To: Jens Remus
Cc: Roman Gushchin, Weinan Liu, Will Deacon, Josh Poimboeuf,
Indu Bhagat, Peter Zijlstra, Steven Rostedt, Catalin Marinas,
Jiri Kosina, Mark Rutland, Prasanna Kumar T S M, Puranjay Mohan,
Song Liu, joe.lawrence, linux-toolchains, linux-kernel,
live-patching, linux-arm-kernel, Randy Dunlap, Heiko Carstens
In-Reply-To: <549d10b6-ba2b-4ae9-86ef-6157e13b6ee3@linux.ibm.com>
On Thu, Apr 30, 2026 at 3:11 AM Jens Remus <jremus@linux.ibm.com> wrote:
>
> On 4/28/2026 8:36 PM, Dylan Hatch wrote:
> > Implement a generic kernel sframe-based [1] unwinder. The main goal is
> > to improve reliable stacktrace on arm64 by unwinding across exception
> > boundaries.
>
> Please add support to initialize the optional sframe unwinder debug
> information. Either in the appropriate patches in this series or as a
> separate patch.
Sounds good, I'll add this in as a separate patch in the next version.
>
> Note that for the module case I wonder whether it would be preferable
> to somehow indicate that it is a module name in the string, e.g.
> "(<module-name>)" or "<module-name> (module)"?
I don't have a strong preference, though I agree it makes sense to
indicate that the section is from a module. For now I'll add the
parentheses "(<module-name>)".
>
> diff --git a/kernel/unwind/sframe.c b/kernel/unwind/sframe.c
> --- a/kernel/unwind/sframe.c
> +++ b/kernel/unwind/sframe.c
> @@ -1028,6 +1028,8 @@ void __init init_sframe_table(void)
> kernel_sfsec.text_start = (unsigned long)_stext;
> kernel_sfsec.text_end = (unsigned long)_etext;
>
> + dbg_init(&kernel_sfsec);
> +
> if (WARN_ON(sframe_read_header(&kernel_sfsec)))
> return;
> if (WARN_ON(sframe_validate_section(&kernel_sfsec)))
> @@ -1047,6 +1049,8 @@ void sframe_module_init(struct module *mod, void *sframe, size_t sframe_size,
> sec->text_start = (unsigned long)text;
> sec->text_end = (unsigned long)text + text_size;
>
> + dbg_init(sec);
> +
> if (WARN_ON(sframe_read_header(sec)))
> return;
> if (WARN_ON(sframe_validate_section(sec)))
> diff --git a/kernel/unwind/sframe_debug.h b/kernel/unwind/sframe_debug.h
> --- a/kernel/unwind/sframe_debug.h
> +++ b/kernel/unwind/sframe_debug.h
> @@ -32,6 +32,18 @@ static inline void dbg_init(struct sframe_section *sec)
> struct mm_struct *mm = current->mm;
> struct vm_area_struct *vma;
>
> + if (sec->sec_type == SFRAME_KERNEL) {
> + if (sec == &kernel_sfsec) {
> + sec->filename = kstrdup("(vmlinux)", GFP_KERNEL);
> + } else {
> + struct module *mod = container_of(sec, struct module,
> + arch.sframe_sec);
> + sec->filename = kstrdup(mod->name, GFP_KERNEL);
> + }
> +
> + return;
> + }
> +
> guard(mmap_read_lock)(mm);
> vma = vma_lookup(mm, sec->sframe_start);
> if (!vma)
>
> Regards,
> Jens
> --
> Jens Remus
> Linux on Z Development (D3303)
> jremus@de.ibm.com / jremus@linux.ibm.com
>
> IBM Deutschland Research & Development GmbH; Vorsitzender des Aufsichtsrats: Wolfgang Wendt; Geschäftsführung: David Faller; Sitz der Gesellschaft: Ehningen; Registergericht: Amtsgericht Stuttgart, HRB 243294
> IBM Data Privacy Statement: https://www.ibm.com/privacy/
>
Thanks for the suggestion,
Dylan
^ permalink raw reply
* Re: [PATCH] killswitch: add per-function short-circuit mitigation primitive
From: Sasha Levin @ 2026-05-11 18:09 UTC (permalink / raw)
To: Michal Hocko
Cc: Breno Leitao, Andrew Morton, corbet, skhan, linux-doc,
linux-kernel, linux-kselftest, gregkh, akinobu.mita,
live-patching
In-Reply-To: <agINlnNN4ubZgyiN@tiehlicka>
On Mon, May 11, 2026 at 07:10:46PM +0200, Michal Hocko wrote:
>On Mon 11-05-26 12:45:36, Sasha Levin wrote:
>> Could you describe an existing infrastructure I can use here?
>
>I think it would help to CC maintainers of subsystems that provide
>kernel modification functionality. They will surely have a better
>insight than me.
>
>> Let's look at
>> this recent "Copy Fail" thing as an example.
>>
>> I can obviously build my own kernel and enroll my own key, but 99.9% of our
>> users won't be doing that.
>> Livepatching, or manually building a module that just injects a kprobe is out
>> of the question as we previously agreed.
>
>Onless I am mistaken you can enroll your own key through MOK. But you
>are right that this is an additional step. But the real question is
>whether this is a major road block for users of this specific feature.
The roadblock here is that then I need to start owning the kernel package: I
need to pull updates, rebuild, reinstall, etc. I lose the support I might be
getting from the distro vendor.
I see "users of this particular feature" as the other 99.9% of folks who don't
build their own kernel, who follow security updates from their distro vendor
and could apply the simple workaround that those vendors could now provide.
>> systemtap falls into the same bucket as building my own module.
>>
>> BPF doesn't help because bpf_override_return() requires the target to be on the
>> same within_error_injection_list() whitelist as fault injection, and the CVE
>> targets never are. Some of our fleet doesn't even have BPF enabled either, but
>> that's the smaller objection.
>>
>> I can't use fault injection because:
>>
>> a. It's almost never built in production/distro kernels, and I suspect this
>> won't change.
>> b. The functions I need are not whitelisted.
>> c. Even if (a) and (b) were addressed, fault injection would still need a
>> securityfs front-end, a cmdline parser, a module-unload notifier, a taint flag,
>> and audit on engage and disengage. By the time those land in fail_function and
>> tie into/refactor the fault injection code, the net diff is bigger than this
>> proposal.
>
>I cannot comment on fault injection imeplementation details of course
>but I have to say that the whitelist nature is something that makes its
>use very limited. Maybe this is a good opportunity to change the
>approach.
Possibly, but IMO the bigger hurdle is the refactoring we'll need to do so
seperate fail_function out of the fault injection umbrella.
One approach would be to abstract the kprobe logic out of fail_function into a
common lib that killswitch could also use, but from a brief look the benefit
will be minimal.
>> In my case I can remove the module, but not if I run a distro that shipped with
>> CONFIG_CRYPTO_USER_API_AEAD=y (like RHEL/SUSE).
>
>If you look at copy fail[2], IIRC algif_aead, esp[46] and rxrcp are all
>modules that could be blacklisted.
On some distros sure, on some others, not:
https://gitlab.com/redhat/centos-stream/src/kernel/centos-stream-10/-/raw/main/redhat/configs/common/generic/CONFIG_CRYPTO_USER_API_AEAD
Even if it is a module, what if I can't just unload it because I have something
that actually uses it?
Look at the ESP issue for example. I can mitigate it by simply doing:
echo "engage xfrm4_udp_encap_rcv 0" > /sys/kernel/security/killswitch/control
echo "engage xfrm6_udp_encap_rcv 0" > /sys/kernel/security/killswitch/control
which only kills ESP encapsulated in UDP. The remaining functionality will keep
working just fine.
>> I can use "initcall_blacklist=" hack and reboot, but as things stand today,
>> I'll need to be rebooting few times a day.
>
>with your just disable some functions in the kernel you might need to
>reboot even more. But more seriously...
>
>> Even if I'm okay with rebooting that often (and I really really would prefer
>> not to), this doesn't solve the issues of a larger fleet of servers that can't
>> just reboot that often.
>>
>> What am I missing?
>
>For one, you are missing more maintainers of code modification infrastructures.
Happy to add more, but I don't want to be too spammy. I'll add in the
livepatching ML and the fault injection maintainer (I couldn't find a list).
Please add any other folks/lists who you think might want to contribute to this
discussion.
--
Thanks,
Sasha
^ permalink raw reply
* Re: [PATCH v5 0/6] kselftests: livepatch: Adapt tests to be executed on 4.12 kernels
From: Petr Mladek @ 2026-05-06 12:26 UTC (permalink / raw)
To: Marcos Paulo de Souza
Cc: Josh Poimboeuf, Jiri Kosina, Miroslav Benes, Joe Lawrence,
Shuah Khan, live-patching, linux-kselftest, linux-kernel, marcos
In-Reply-To: <20260504-lp-tests-old-fixes-v5-0-0be26d94ab9a@suse.com>
On Mon 2026-05-04 15:34:41, Marcos Paulo de Souza wrote:
> This is the fifth version of the patchset, which fixes the last Sashiko
> comment, about overwriting MOD_LIVEPATCH global variable and using a
> local variable to avoid module load clashing when an older kernel
> don't support some sysfs attributes.
>
> Original cover-letter:
> These patches don't really change how the patches are run, just skip
> some tests on kernels that don't support a feature (like kprobe and
> livepatched living together) or when a livepatch sysfs attribute is
> missing.
>
> These patches are based on printk/for-next branch.
>
> Please review! Thanks!
>
> Signed-off-by: Marcos Paulo de Souza <mpdesouza@suse.com>
> ---
> Changes in v5:
> - Edit the last three patches to avoid overwriting MOD_LIVEPATCH
> variable, using a local variable. This fixed the last Sashiko report.
> - Link to v4: https://patch.msgid.link/20260429-lp-tests-old-fixes-v4-0-59b9741989d0@suse.com
JFYI, the patchset has been pushed into livepatch.git,
branch for-7.2-selftests.
Best Regards,
Petr
^ permalink raw reply
* Re: [PATCH v5 0/6] kselftests: livepatch: Adapt tests to be executed on 4.12 kernels
From: Petr Mladek @ 2026-05-06 10:23 UTC (permalink / raw)
To: Marcos Paulo de Souza
Cc: Josh Poimboeuf, Jiri Kosina, Miroslav Benes, Joe Lawrence,
Shuah Khan, live-patching, linux-kselftest, linux-kernel, marcos
In-Reply-To: <20260504-lp-tests-old-fixes-v5-0-0be26d94ab9a@suse.com>
On Mon 2026-05-04 15:34:41, Marcos Paulo de Souza wrote:
> This is the fifth version of the patchset, which fixes the last Sashiko
> comment, about overwriting MOD_LIVEPATCH global variable and using a
> local variable to avoid module load clashing when an older kernel
> don't support some sysfs attributes.
>
> Original cover-letter:
> These patches don't really change how the patches are run, just skip
> some tests on kernels that don't support a feature (like kprobe and
> livepatched living together) or when a livepatch sysfs attribute is
> missing.
>
> ---
> Marcos Paulo de Souza (6):
> selftests: livepatch: Check for ARCH_HAS_SYSCALL_WRAPPER config
> selftests: livepatch: Replace true/false module parameter by y/n
> selftests: livepatch: Introduce does_sysfs_exist function
> selftests: livepatch: Check if patched sysfs attribute exists
> selftests: livepatch: Check if replace sysfs attribute exists
> selftests: livepatch: Check if stack_order sysfs attribute exists
>
> tools/testing/selftests/livepatch/functions.sh | 10 +
> tools/testing/selftests/livepatch/test-kprobe.sh | 8 +-
> tools/testing/selftests/livepatch/test-sysfs.sh | 219 +++++++++++----------
> .../livepatch/test_modules/test_klp_syscall.c | 27 ++-
> 4 files changed, 153 insertions(+), 111 deletions(-)
For the entire patchset:
Reviewed-by: Petr Mladek <pmladek@suse.com>
It seems to be ready for linux-next.
Best Regards,
Petr
^ permalink raw reply
* Re: [PATCH v2 2/2] module/kallsyms: sort function symbols and use binary search
From: Petr Pavlu @ 2026-05-06 9:42 UTC (permalink / raw)
To: Petr Mladek
Cc: Stanislaw Gruszka, linux-modules, Sami Tolvanen, Luis Chamberlain,
linux-kernel, linux-trace-kernel, live-patching, Daniel Gomez,
Aaron Tomlin, Steven Rostedt, Masami Hiramatsu, Jordan Rome,
Viktor Malik, Miroslav Benes, Josh Poimboeuf, Joe Lawrence
In-Reply-To: <afsCpoGPasi-kBLb@pathway.suse.cz>
On 5/6/26 10:58 AM, Petr Mladek wrote:
> On Tue 2026-05-05 16:37:56, Petr Pavlu wrote:
>> On 5/5/26 2:24 PM, Petr Mladek wrote:
>>> On Fri 2026-03-27 12:00:05, Stanislaw Gruszka wrote:
>>>> Module symbol lookup via find_kallsyms_symbol() performs a linear scan
>>>> over the entire symtab when resolving an address. The number of symbols
>>>> in module symtabs has grown over the years, largely due to additional
>>>> metadata in non-standard sections, making this lookup very slow.
>>>>
>>>> Improve this by separating function symbols during module load, placing
>>>> them at the beginning of the symtab, sorting them by address, and using
>>>> binary search when resolving addresses in module text.
>>>>
>>>> This also should improve times for linear symbol name lookups, as valid
>>>> function symbols are now located at the beginning of the symtab.
>>>>
>>>> The cost of sorting is small relative to module load time. In repeated
>>>> module load tests [1], depending on .config options, this change
>>>> increases load time between 2% and 4%. With cold caches, the difference
>>>> is not measurable, as memory access latency dominates.
>>>>
>>>> The sorting theoretically could be done in compile time, but much more
>>>> complicated as we would have to simulate kernel addresses resolution
>>>> for symbols, and then correct relocation entries. That would be risky
>>>> if get out of sync.
>>>>
>>>> The improvement can be observed when listing ftrace filter functions.
>>>>
>>>> Before:
>>>>
>>>> root@nano:~# time cat /sys/kernel/tracing/available_filter_functions | wc -l
>>>> 74908
>>>>
>>>> real 0m1.315s
>>>> user 0m0.000s
>>>> sys 0m1.312s
>>>>
>>>> After:
>>>>
>>>> root@nano:~# time cat /sys/kernel/tracing/available_filter_functions | wc -l
>>>> 74911
>>>>
>>>> real 0m0.167s
>>>> user 0m0.004s
>>>> sys 0m0.175s
>>>>
>>>> (there are three more symbols introduced by the patch)
>>>>
>>>> For livepatch modules, the symtab layout is preserved and the existing
>>>> linear search is used. For this case, it should be possible to keep
>>>> the original ELF symtab instead of copying it 1:1, but that is outside
>>>> the scope of this patch.
>>>
>>> What is the exact motivation for the special handling of livepatch modules,
>>> please?
>>>
>>> Honestly, I am always a bit lost in the various symbol tables. It is
>>> possile that I have got something wrong.
>>>
>>> Anyway, my understanding is that there are two aspects which are important
>>> for livepatches:
>>>
>>> 1. Livepatches need to preserve special symbols which are used to
>>> relocate symbols which were local in the original code, see
>>> Documentation/livepatch/module-elf-format.rst
>>>
>>> IMHO, this is why layout_symtab() computes space for all core
>>> symbols in livepatch modules and copies them in add_kallsyms().
>>>
>>> The symtab is normally released when the module is loaded.
>>> But livepatch modules make its own copy of the important
>>> parts, see copy_module_elf().
>>>
>>> IMHO, the sorting of function symbols vs other symbols does
>>> not matter here. I believe that the special relocation
>>> symbols are not affected by this.
>>
>> I'm not sure if I fully follow the conclusion in this point. My
>> understanding is that .klp.rela sections still refer to their special
>> symbols in the symbol table via Elf_Rela::r_info. If the symbol table is
>> filtered or reordered, these references will end up pointing to
>> incorrect symbols.
>
> My understanding is that the relocations point to symbols which
> are found via the name of the entry. Let's take an example
> from Documentation/livepatch/module-elf-format.rst:
>
> 73: 0000000000000000 0 NOTYPE GLOBAL DEFAULT OS [0xff20] .klp.sym.vmlinux.snprintf,0
>
> This symbol points to snprintf() function in vmlinux object.
> The address of this function is found via kallsyms, see
> klp_find_object_symbol().
>
> IMHO, it does not matter if we shuffle this entry in the livepatch
> module because the real address is found via kallsyms().
>
> Even the ordering of the entries in vmlinux is not important in
> _this particular case_ because the "sympos" is zero "0" in this case.
> It means that "snprintf" symbol name is unique in vmlinux.
>
> The ordering of the symbols in "vmlinux" becomes important
> if the livepatch needs to access a symbol and there are more
> symbols of the same name. This is what I tried to describe
> below.
>
> I hope that it makes some sense. As I said, I am not familiar
> with the elf format...
Ah, I misunderstood your original point. I agree that reshuffling the
symbol table in a livepatch module will not cause any issues with
binding .klp.sym.<objname>.<symname>,<sympos> symbol references to their
actual definitions in <objname>.
The problem still exists with the .klp.rela sections. They are regular
RELA sections in the sense that each ELF_R_SYM(Elf_Rela::r_info) value
is an index identifying a symbol in the symbol table. If the module
loader reshuffles or filters the original symbol table in any way, the
indexes in Elf_Rela::r_info would need to be adjusted accordingly.
--
Thanks,
Petr
^ permalink raw reply
* Re: [PATCH v2 2/2] module/kallsyms: sort function symbols and use binary search
From: Petr Mladek @ 2026-05-06 8:58 UTC (permalink / raw)
To: Petr Pavlu
Cc: Stanislaw Gruszka, linux-modules, Sami Tolvanen, Luis Chamberlain,
linux-kernel, linux-trace-kernel, live-patching, Daniel Gomez,
Aaron Tomlin, Steven Rostedt, Masami Hiramatsu, Jordan Rome,
Viktor Malik, Miroslav Benes, Josh Poimboeuf, Joe Lawrence
In-Reply-To: <28bb0f74-8721-4e53-ad89-87b2a78623b2@suse.com>
On Tue 2026-05-05 16:37:56, Petr Pavlu wrote:
> On 5/5/26 2:24 PM, Petr Mladek wrote:
> > On Fri 2026-03-27 12:00:05, Stanislaw Gruszka wrote:
> >> Module symbol lookup via find_kallsyms_symbol() performs a linear scan
> >> over the entire symtab when resolving an address. The number of symbols
> >> in module symtabs has grown over the years, largely due to additional
> >> metadata in non-standard sections, making this lookup very slow.
> >>
> >> Improve this by separating function symbols during module load, placing
> >> them at the beginning of the symtab, sorting them by address, and using
> >> binary search when resolving addresses in module text.
> >>
> >> This also should improve times for linear symbol name lookups, as valid
> >> function symbols are now located at the beginning of the symtab.
> >>
> >> The cost of sorting is small relative to module load time. In repeated
> >> module load tests [1], depending on .config options, this change
> >> increases load time between 2% and 4%. With cold caches, the difference
> >> is not measurable, as memory access latency dominates.
> >>
> >> The sorting theoretically could be done in compile time, but much more
> >> complicated as we would have to simulate kernel addresses resolution
> >> for symbols, and then correct relocation entries. That would be risky
> >> if get out of sync.
> >>
> >> The improvement can be observed when listing ftrace filter functions.
> >>
> >> Before:
> >>
> >> root@nano:~# time cat /sys/kernel/tracing/available_filter_functions | wc -l
> >> 74908
> >>
> >> real 0m1.315s
> >> user 0m0.000s
> >> sys 0m1.312s
> >>
> >> After:
> >>
> >> root@nano:~# time cat /sys/kernel/tracing/available_filter_functions | wc -l
> >> 74911
> >>
> >> real 0m0.167s
> >> user 0m0.004s
> >> sys 0m0.175s
> >>
> >> (there are three more symbols introduced by the patch)
> >>
> >> For livepatch modules, the symtab layout is preserved and the existing
> >> linear search is used. For this case, it should be possible to keep
> >> the original ELF symtab instead of copying it 1:1, but that is outside
> >> the scope of this patch.
> >
> > What is the exact motivation for the special handling of livepatch modules,
> > please?
> >
> > Honestly, I am always a bit lost in the various symbol tables. It is
> > possile that I have got something wrong.
> >
> > Anyway, my understanding is that there are two aspects which are important
> > for livepatches:
> >
> > 1. Livepatches need to preserve special symbols which are used to
> > relocate symbols which were local in the original code, see
> > Documentation/livepatch/module-elf-format.rst
> >
> > IMHO, this is why layout_symtab() computes space for all core
> > symbols in livepatch modules and copies them in add_kallsyms().
> >
> > The symtab is normally released when the module is loaded.
> > But livepatch modules make its own copy of the important
> > parts, see copy_module_elf().
> >
> > IMHO, the sorting of function symbols vs other symbols does
> > not matter here. I believe that the special relocation
> > symbols are not affected by this.
>
> I'm not sure if I fully follow the conclusion in this point. My
> understanding is that .klp.rela sections still refer to their special
> symbols in the symbol table via Elf_Rela::r_info. If the symbol table is
> filtered or reordered, these references will end up pointing to
> incorrect symbols.
My understanding is that the relocations point to symbols which
are found via the name of the entry. Let's take an example
from Documentation/livepatch/module-elf-format.rst:
73: 0000000000000000 0 NOTYPE GLOBAL DEFAULT OS [0xff20] .klp.sym.vmlinux.snprintf,0
This symbol points to snprintf() function in vmlinux object.
The address of this function is found via kallsyms, see
klp_find_object_symbol().
IMHO, it does not matter if we shuffle this entry in the livepatch
module because the real address is found via kallsyms().
Even the ordering of the entries in vmlinux is not important in
_this particular case_ because the "sympos" is zero "0" in this case.
It means that "snprintf" symbol name is unique in vmlinux.
The ordering of the symbols in "vmlinux" becomes important
if the livepatch needs to access a symbol and there are more
symbols of the same name. This is what I tried to describe
below.
I hope that it makes some sense. As I said, I am not familiar
with the elf format...
> This is also described in Documentation/livepatch/module-elf-format.rst,
> section "4.1 A livepatch module's symbol table".
>
> >
> >
> > 2. Livepatches _rely on the sorting_ of symbols in the module.
> > The special relocation symbols might define a symbol position,
> > see
> >
> > .klp.sym.objname.symbol_name,sympos
> >
> > in the documentation. It defines the position of the symbol
> > when there are more symbols of the same name, see
> > klp_match_callback() in kernel/livepatch/core.c.
> >
> > I am afraid that _this patch might break_ this when it moves
> > function symbols before non-funciton ones. I guess that
> > the symbols of the same name will not longer be groupped.
>
> I see. So if the module loader sorts the symbol table in a regular
> module and a livepatch module exists for that module, the livepatch may
> no longer function correctly. This means that the loader cannot even
> reorder the symbol table in regular modules.
Yeah, reordering symbols in regular module might break livepatching.
Namely it might break finding the right symbol when there are
more symbols of the same name.
Best Regards,
Petr
^ permalink raw reply
* Re: [PATCH v5 8/8] unwind: arm64: Use sframe to unwind interrupt frames
From: Jens Remus @ 2026-05-05 15:52 UTC (permalink / raw)
To: Mark Rutland, Indu Bhagat
Cc: Dylan Hatch, Roman Gushchin, Weinan Liu, Will Deacon,
Josh Poimboeuf, Peter Zijlstra, Steven Rostedt, Catalin Marinas,
Jiri Kosina, Prasanna Kumar T S M, Puranjay Mohan, Song Liu,
joe.lawrence, linux-toolchains, linux-kernel, live-patching,
linux-arm-kernel, Randy Dunlap, Heiko Carstens
In-Reply-To: <afnGhsCYEUG0LXR5@J2N7QTR9R3>
On 5/5/2026 12:29 PM, Mark Rutland wrote:
> On Mon, May 04, 2026 at 10:47:26AM +0200, Jens Remus wrote:
>> On 5/1/2026 6:46 PM, Mark Rutland wrote:
>>> Thanks for putting this together. I think this is looking pretty good.
>>> However, there are some things that aren't quite right and need some
>>> work, which I've commented on below.
>>
>>> (2) To make unwinding generally possible, we'll need to annotate some
>>> assembly functions as unwindable. We'll need to do that for string
>>> routines under lib/, and probably some crypto code, but we don't
>>> need to do that for most code in head.S, entry.S, etc.
>>>
>>> The vast majority of relevant assembly functions are leaf functions
>>> (where the return address is never moved out of the LR), so we can
>>> probably get away with a simple annotation for those that avoids the
>>> need for open-coded CFI directives everywhere.
>>
>> Wrapping them in .cfi_startproc ... .cfi_endproc should do. For instance
>> by extending SYM_FUNC_START() and SYM_FUNC_END() or introducing flavors
>> that do. Or where you thinking of something else?
>
> I was expecting we'd do something like that, either with distinct
> versions, or some entirely separate annotation.
>
> We can't override SYM_FUNC_START() or SYM_FUNC_END() since those are
> also used for non-leaf functions. The bulk of the work is going to be
> making sure we only annotate leaf functions specifically, which will
> require some human analysis.
Makes sense.
>>> On Tue, Apr 28, 2026 at 06:36:43PM +0000, Dylan Hatch wrote:
>>>> diff --git a/arch/arm64/include/asm/stacktrace/common.h b/arch/arm64/include/asm/stacktrace/common.h
>>
>>>> @@ -21,6 +21,8 @@ struct stack_info {
>>>> *
>>>> * @fp: The fp value in the frame record (or the real fp)
>>>> * @pc: The lr value in the frame record (or the real lr)
>>>> + * @sp: The sp value at the call site of the current function.
>>>> + * @unreliable: Stacktrace is unreliable.
>>>> *
>>>> * @stack: The stack currently being unwound.
>>>> * @stacks: An array of stacks which can be unwound.
>>>> @@ -29,7 +31,11 @@ struct stack_info {
>>>> struct unwind_state {
>>>> unsigned long fp;
>>>> unsigned long pc;
>>>> +#ifdef CONFIG_HAVE_UNWIND_KERNEL_SFRAME
>>>> + unsigned long sp;
>>>> +#endif
>>>
>>> As this is only used by the kernel unwinder (and not the hyp unwinder),
>>> this should live in struct kunwind_state in stacktrace.c.
>>>
>>> That said, for unwinding across exception boundaries we should not need
>>> this, as the SP value will be in the pt_regs. If we only use SFrame for
>>> the exception boundary case, we can remove this entirely. I would
>>> strongly prefer that we do that.
>
>>>> + /* Get the Canonical Frame Address (CFA) */
>>>> + switch (frame.cfa.rule) {
>>>> + case UNWIND_CFA_RULE_SP_OFFSET:
>>>> + cfa = state->common.sp;
>>
>> IIUC you suggest this to be changed as follows?
>>
>> cfa = regs->regs[31];
>
> I was suggesting:
>
> cfa = regs->sp;
>
> Note: arm64's struct pt_regs has:
>
> union {
> struct user_pt_regs user_regs;
> struct {
> u64 regs[31];
> u64 sp;
> u64 pc;
> u64 pstate;
> };
> };
>
> ... so regs->regs[31] would be an out-of-bounds array access.
Aww, my bad! Of course!
>
> [...]
>
>>>> + case UNWIND_CFA_RULE_REG_OFFSET:
>>>> + case UNWIND_CFA_RULE_REG_OFFSET_DEREF:
>>>> + /* regs only available in topmost/interrupt frame */
>>>> + if (!regs || frame.cfa.regnum > 30)
>>>> + return -EINVAL;
>>>> + cfa = regs->regs[frame.cfa.regnum];
>>>> + break;
>>>
>>> Do we ever expect to see UNWIND_CFA_RULE_REG_OFFSET or
>>> UNWIND_CFA_RULE_REG_OFFSET_DEREF in practice for kernel code?
>>
>> No. Those can only occur with SFrame V3 flexible FDE, which are
>> currently not generated by GNU assembler for arm64/aarch64, and thus
>> could be omitted in the arm64-specific kernel sframe unwinder:
>>
>> https://sourceware.org/git/?p=binutils-gdb.git;a=blob;f=gas/config/tc-aarch64.h;hb=binutils-2_46#l342
>
> Ok.
>
> Do we know whether there are currently cases on aarch64 that cannot be
> encoded in SFrame (without flexible FDE), or whether SFrame without
> flexible FDE is sufficient for arm64 as-is? ... or do we have
> counter-examples today?
Not that I am aware of. IIUC this is why Indu, the SFrame maintainer,
did not enable SFrame V3 flexible FDE for arm64/aarch64 in the GNU
assembler.
> Looking at:
>
> https://sourceware.org/binutils/docs/sframe-spec.html#Flexible-FDE-Type-Interpretation-1
>
> For arm64 I'm not sure whether we'd encounter the DRAP or stack
> realignment cases within the kernel (perhaps with SVE?), nor whether the
> Register-based RA/FP Locations cases would apply if we assume that we
> continue to use frame records.
@Indu: Can you provide more insight?
> [... ]
>
>> I must admit that while reviewing I thought it would be future-proof to
>> have support for rules that can only be represented with SFrame V3
>> flexible FDE, even if they are currently not used on arm64. Ideally
>> kunwind_next_sframe() could be made common, if another architecture
>> would implement kernel unwinding using sframe.
>
> While I understand that principle, I think that for now it would be
> better to keep this arch-specific and minimal:
>
> * We have arch-specific concerns (e.g. the FRAME_META_TYPE_FINAL
> frames), and factoring that into generic code is going to be painful
> to adapt (which we're likely to need to do in the near future), and to
> maintain going forwards. Keeping that arch-specific for now will make
> it easier/quicker to get to a stable state.
>
> * Code which isn't used is liable to be wrong or made wrong by accident.
> For example, with all the SP cases I mentioned in my initial reply.
>
> We can certainly look at making that more generic in future, but for now
> I'd prefer to omit the code that cannot be used (and have some sort of
> build or boot/module-load time check that SFrame only has elements that
> we expect), and make sure that we thoroughly test the cases that do
> exist in practice.
Makes sense.
> Do we expect SFrame V3 flexible FDE to be generated by toolchains in the
> near future?
There is work in progress to implement SFrame V3 generation in LLVM.
Their implementation could be less restrictive regarding to which
SFrame V3 feature to enable on arm64/aarch64. But even then for
SFrame V3 flexible FDE to be generated there must be DWARF CFI patterns
that can only be represented using those. So if those do not exist
(see your previous question above), then they should not be generated.
@Indu: What are your thoughts on this?
> [...]
>
>>>> + /* CFA alignment 8 bytes */
>>>> + if (cfa & 0x7)
>>>> + return -EINVAL;
>>>
>>> If the CFA is the SP upon entry to the function, then per AAPCS64 rules
>>> it should be aligned to 16 bytes. Otherwise, where has this 8 byte
>>> alignment requirement come from? Does SFrame mandate that?
>>
>> That originates from the common unwind user logic (see
>> kernel/unwind/user.c, unwind_user_next_common()), which currently
>> assumes 8-byte/4-byte SP alignment for all 64-bit/32-bit architectures.
>>
>> So checking for 16-byte alignment here would make sense.
>
> Just to confirm, am I correct to understand that the SFrame definition
> of CFA is intended to be the same as the DWARF definition of CFA, and so
Correct.
> for arm64 the CFA is the SP when the function is called?
Correct.
>
> That's the case for DWARF on arm64:
>
> https://github.com/ARM-software/abi-aa/releases/download/2025Q4/aadwarf64.pdf
> https://github.com/ARM-software/abi-aa/blob/daa7a94ca55973736c0e434a67a6e4bbcd35d7fa/aadwarf64/aadwarf64.rst
>
> | The CFA is the value of the stack pointer (sp) at the call site in the
> | previous frame.
>
> I couldn't find an explciit statement to that effect in:
>
> https://sourceware.org/binutils/docs/sframe-spec.html
>
> ... but I guess that is implied, given the other bits inherited from
> DWARF.
I assume so.
>
> I see that the documented behaviour for CFA on AMD64 and s390x are
> consistent with their DWARF behaviour.
Yes.
>
>>>> +
>>>> + /* Get the Return Address (RA) */
>>>> + switch (frame.ra.rule) {
>>>> + case UNWIND_RULE_RETAIN:
>>>> + /* regs only available in topmost/interrupt frame */
>>>> + if (!regs)
>>>> + return -EINVAL;
>>>> + ra = regs->regs[30];
>>>> + source = KUNWIND_SOURCE_REGS_LR;
>>>> + break;
>>>> + /* UNWIND_USER_RULE_CFA_OFFSET not implemented on purpose */
>>
>> Nit: s/UNWIND_USER_RULE_CFA_OFFSET/UNWIND_RULE_CFA_OFFSET/
>>
>>>
>>> It would be better for the comment to say *why* that's not implemented.
>>>
>>> I assume that's because UNWIND_USER_RULE_CFA_OFFSET would mean that the return
>>> address is a stack address, and that's obviously not legitimate.
>>
>> That and SFrame V3 currently cannot represent FP/RA as CFA + offset
>> (i.e. UNWIND_RULE_CFA_OFFSET; .cfi_val_offset FP/RA).
>>
>> The comment originates from the common unwind user logic (see
>> kernel/unwind/user.c). I am open to improve that. What about:
>>
>> /*
>> * UNWIND_RULE_CFA_OFFSET not implemented on purpose, as a stack
>> * address cannot be a legitimate return address value. It is
>> * also not used (e.g. not represented in sframe).
>> */
>
> I'd go with something simpler, e.g.
>
> /*
> * UNWIND_RULE_CFA_OFFSET doesn't make sense for RA.
> * The return address cannot legitimately be a stack addres.
> */
Thanks! I have updated the comments accordingly in my latest unwind
user sframe patch series sent today:
[PATCH v14 14/19] unwind_user: Flexible FP/RA recovery rules
https://lore.kernel.org/all/20260505121718.3572346-15-jremus@linux.ibm.com/
[...]
>>>> + default:
>>>> + WARN_ON_ONCE(1);
>>>> + return -EINVAL;
>>>> + }
>>>> +
>>>> + /* Get the Frame Pointer (FP) */
>>>> + switch (frame.fp.rule) {
>>>> + case UNWIND_RULE_RETAIN:
>>>> + fp = state->common.fp;
>>>> + break;
>>>> + /* UNWIND_USER_RULE_CFA_OFFSET not implemented on purpose */
>>>
>>> As for RA, the comment should explain why that's not implemented.
>>
>> I am open to improve the comment in the the common unwind user logic.
>> What about:
>>
>> /*
>> * UNWIND_RULE_CFA_OFFSET not implemented on purpose, as it is
>> * not used (e.g. not represented in sframe).
>> */
>
> For me, this wording raises more questions, e.g.
>
> * Does 'not used' mean that toolchains don't use that, or that the spec
> doesn't permit that?
unwind user currently only supports frame pointer, with SFrame to be
hopefully added soon. Out of these only SFrame requires ("uses") these
elaborated rules.
>
> * Does 'not represented' mean that this is not represntable, or that
> toolchains currently don't generate SFrame with the appropriate
> elements.
In DWARF CFI it is representable using .cfi_val_offset <FP>, <offset>.
But SFrame V3 currently cannot represent this:
"Note that, using a value of 0 as padding data word, does mean that
currently, e.g., for RA [JR: FP likewise], the rule RA = CFA + 0 cannot
be encoded. NB: RA = CFA + 0 is distinct from RA = *(CFA + 0)."
https://sourceware.org/binutils/docs/sframe-spec.html#Flexible-FDE-Type-Interpretation-1
>
> IIUC you're saying that this *is* representable with flexible FDE, but
> current toolchains don't generate that.
Thanks for the feedback! I changed it as follows to clarify:
/*
* UNWIND_USER_RULE_CFA_OFFSET is currently not used for FP
* (e.g. SFrame cannot represent this rule).
*/
Regards,
Jens
--
Jens Remus
Linux on Z Development (D3303)
jremus@de.ibm.com / jremus@linux.ibm.com
IBM Deutschland Research & Development GmbH; Vorsitzender des Aufsichtsrats: Wolfgang Wendt; Geschäftsführung: David Faller; Sitz der Gesellschaft: Ehningen; Registergericht: Amtsgericht Stuttgart, HRB 243294
IBM Data Privacy Statement: https://www.ibm.com/privacy/
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox