* [PATCH v2 20/53] objtool/klp: Don't correlate .rodata.cst* constant pool objects
From: Josh Poimboeuf @ 2026-05-01 4:08 UTC (permalink / raw)
To: x86
Cc: linux-kernel, live-patching, Peter Zijlstra, Joe Lawrence,
Song Liu, Miroslav Benes, Petr Mladek
In-Reply-To: <cover.1777575752.git.jpoimboe@kernel.org>
Clang aggregates UBSAN type descriptors into shared anonymous
.data..L__unnamed_* sections. This data is used by UBSAN trap handlers.
When a changed function has an UBSAN bounds check, klp-diff clones the
entire UBSAN data section associated with the TU. Relocations within
the cloned section that reference named rodata objects in .rodata.cst*
(like 'exponent', 'pirq_ali_set.irqmap') become KLP relocations because
those objects now get correlated.
That results in a .klp.rela.vmlinux..data section which can easily have
thousands of KLP relocs, most of which are completely superfluous, used
by functions which aren't cloned to the patch module.
The .rodata.cst* sections are SHF_MERGE constant pool sections
containing small fixed-size data (lookup tables, bitmasks) that is only
read by value. Pointer identity is never relevant for these objects, so
correlating them is unnecessary.
Exclude .rodata.cst* objects from correlation so they get cloned as
local data instead of generating KLP relocations.
It might be possible to someday treat UBSAN data sections as special
sections, and only extract the few needed entries. But this works for
now.
Signed-off-by: Josh Poimboeuf <jpoimboe@kernel.org>
---
tools/objtool/klp-diff.c | 17 ++++++++++++++++-
1 file changed, 16 insertions(+), 1 deletion(-)
diff --git a/tools/objtool/klp-diff.c b/tools/objtool/klp-diff.c
index bf37c652188b..ca87bcb9afa3 100644
--- a/tools/objtool/klp-diff.c
+++ b/tools/objtool/klp-diff.c
@@ -372,6 +372,21 @@ static bool is_initcall_sym(struct symbol *sym)
strstarts(sym->name, "__initstub__");
}
+/*
+ * Some .rodata is anonymous and can't be correlated due to there being no
+ * symbol names.
+ *
+ * The .rodata.cst* sections aren't technically anonymous, they're SHF_MERGE
+ * constant pool sections containing small fixed-size data (lookup tables,
+ * bitmasks) which are only read by value, so pointer equivalence isn't needed.
+ * They are typically referenced by UBSAN data sections.
+ */
+static bool is_anonymous_rodata(struct symbol *sym)
+{
+ return is_rodata_sec(sym->sec) &&
+ (!is_object_sym(sym) || strstarts(sym->sec->name, ".rodata.cst"));
+}
+
/*
* These symbols should never be correlated, so their local patched versions
* are used instead of linking to the originals.
@@ -386,7 +401,7 @@ static bool dont_correlate(struct symbol *sym)
is_uncorrelated_static_local(sym) ||
is_local_label(sym) ||
is_string_sec(sym->sec) ||
- (is_rodata_sec(sym->sec) && !is_object_sym(sym)) ||
+ is_anonymous_rodata(sym) ||
is_initcall_sym(sym) ||
is_addressable_sym(sym) ||
is_special_section(sym->sec) ||
--
2.53.0
^ permalink raw reply related
* [PATCH v2 19/53] objtool/klp: Fix pointer comparisons for rodata objects
From: Josh Poimboeuf @ 2026-05-01 4:08 UTC (permalink / raw)
To: x86
Cc: linux-kernel, live-patching, Peter Zijlstra, Joe Lawrence,
Song Liu, Miroslav Benes, Petr Mladek
In-Reply-To: <cover.1777575752.git.jpoimboe@kernel.org>
klp-diff treats all rodata as uncorrelated, so any reference to it uses
a duplicated copy rather than using a KLP reloc.
For the contents of the data itself, a duplicated copy is fine.
However, pointer comparisons (e.g., f->f_op == &foo_ops) are broken.
Fix it by correlating non-anonymous rodata objects.
Also, use a new find_symbol_containing_inclusive() helper for matching
the end of a symbol so bounds calculations don't get broken, for the
case where an array or other symbol's ending address is used as part of
a bounds calculation.
While these are really two distinct changes, they need to be done in the
same patch so as to avoid introducing bisection regressions.
Signed-off-by: Josh Poimboeuf <jpoimboe@kernel.org>
---
tools/objtool/elf.c | 14 ++++++++++++++
tools/objtool/include/objtool/elf.h | 1 +
tools/objtool/klp-diff.c | 15 +++++++++++++--
3 files changed, 28 insertions(+), 2 deletions(-)
diff --git a/tools/objtool/elf.c b/tools/objtool/elf.c
index 87c6e00749c6..5a20dab683dd 100644
--- a/tools/objtool/elf.c
+++ b/tools/objtool/elf.c
@@ -207,6 +207,20 @@ struct symbol *find_symbol_containing(const struct section *sec, unsigned long o
return sym ? sym->alias : NULL;
}
+/*
+ * Also match the symbol end address which can be used for a bounds comparison.
+ */
+struct symbol *find_symbol_containing_inclusive(const struct section *sec,
+ unsigned long offset)
+{
+ struct symbol *sym = find_symbol_containing(sec, offset);
+
+ if (!sym && offset)
+ sym = find_symbol_containing(sec, offset - 1);
+
+ return sym;
+}
+
/*
* Returns size of hole starting at @offset.
*/
diff --git a/tools/objtool/include/objtool/elf.h b/tools/objtool/include/objtool/elf.h
index ab5f7017ec34..8a543cea43b9 100644
--- a/tools/objtool/include/objtool/elf.h
+++ b/tools/objtool/include/objtool/elf.h
@@ -211,6 +211,7 @@ struct symbol *find_symbol_by_offset(struct section *sec, unsigned long offset);
struct symbol *find_symbol_by_name(const struct elf *elf, const char *name);
struct symbol *find_global_symbol_by_name(const struct elf *elf, const char *name);
struct symbol *find_symbol_containing(const struct section *sec, unsigned long offset);
+struct symbol *find_symbol_containing_inclusive(const struct section *sec, unsigned long offset);
int find_symbol_hole_containing(const struct section *sec, unsigned long offset);
struct reloc *find_reloc_by_dest(const struct elf *elf, struct section *sec, unsigned long offset);
struct reloc *find_reloc_by_dest_range(const struct elf *elf, struct section *sec,
diff --git a/tools/objtool/klp-diff.c b/tools/objtool/klp-diff.c
index 78633c9b68eb..bf37c652188b 100644
--- a/tools/objtool/klp-diff.c
+++ b/tools/objtool/klp-diff.c
@@ -386,6 +386,7 @@ static bool dont_correlate(struct symbol *sym)
is_uncorrelated_static_local(sym) ||
is_local_label(sym) ||
is_string_sec(sym->sec) ||
+ (is_rodata_sec(sym->sec) && !is_object_sym(sym)) ||
is_initcall_sym(sym) ||
is_addressable_sym(sym) ||
is_special_section(sym->sec) ||
@@ -979,7 +980,7 @@ static int convert_reloc_secsym_to_sym(struct elf *elf, struct reloc *reloc)
goto found_sym;
/* No dedicated section; find the symbol manually */
- sym = find_symbol_containing(sec, arch_adjusted_addend(reloc));
+ sym = find_symbol_containing_inclusive(sec, arch_adjusted_addend(reloc));
if (!sym) {
/*
* This is presumably an .altinstr_replacement section which is
@@ -988,6 +989,17 @@ static int convert_reloc_secsym_to_sym(struct elf *elf, struct reloc *reloc)
if (!sec_size(sec))
return 1;
+ /*
+ * .rodata is a mixed bag of named objects and anonymous data.
+ *
+ * Convert section symbol references to named object symbols
+ * when possible, to preserve pointer identity for const
+ * structs like file_operations. Otherwise a section symbol is
+ * fine.
+ */
+ if (is_rodata_sec(sec))
+ return 0;
+
/*
* This can happen for special section references to weak code
* whose symbol has been stripped by the linker.
@@ -1009,7 +1021,6 @@ static int convert_reloc_secsym_to_sym(struct elf *elf, struct reloc *reloc)
static bool is_uncorrelated_section(struct section *sec)
{
return is_string_sec(sec) ||
- strstarts(sec->name, ".rodata") ||
strstarts(sec->name, ".data..Lubsan") || /* GCC */
strstarts(sec->name, ".data..L__unnamed_"); /* Clang */
}
--
2.53.0
^ permalink raw reply related
* [PATCH v2 18/53] objtool/klp: Simplify reloc symbol conversion
From: Josh Poimboeuf @ 2026-05-01 4:08 UTC (permalink / raw)
To: x86
Cc: linux-kernel, live-patching, Peter Zijlstra, Joe Lawrence,
Song Liu, Miroslav Benes, Petr Mladek
In-Reply-To: <cover.1777575752.git.jpoimboe@kernel.org>
Inline section_reference_needed() and is_reloc_allowed() into
convert_reloc_sym() and remove the redundant is_reloc_allowed() check in
clone_reloc().
Move the is_sec_sym() checks into the convert callees so they become
no-ops when the reloc is already in the right format. This allows
convert_reloc_sym() to unconditionally dispatch to the right converter
based on section type.
Signed-off-by: Josh Poimboeuf <jpoimboe@kernel.org>
---
tools/objtool/klp-diff.c | 76 +++++++++++++++-------------------------
1 file changed, 28 insertions(+), 48 deletions(-)
diff --git a/tools/objtool/klp-diff.c b/tools/objtool/klp-diff.c
index 19bc811db396..78633c9b68eb 100644
--- a/tools/objtool/klp-diff.c
+++ b/tools/objtool/klp-diff.c
@@ -838,39 +838,6 @@ static int clone_included_functions(struct elfs *e)
return 0;
}
-/*
- * Determine whether a relocation should reference the section rather than the
- * underlying symbol.
- */
-static bool section_reference_needed(struct section *sec)
-{
- /*
- * String symbols are zero-length and uncorrelated. It's easier to
- * deal with them as section symbols.
- */
- if (is_string_sec(sec))
- return true;
-
- /*
- * .rodata has mostly anonymous data so there's no way to determine the
- * length of a needed reference. just copy the whole section if needed.
- */
- if (strstarts(sec->name, ".rodata"))
- return true;
-
- /* UBSAN anonymous data */
- if (strstarts(sec->name, ".data..Lubsan") || /* GCC */
- strstarts(sec->name, ".data..L__unnamed_")) /* Clang */
- return true;
-
- return false;
-}
-
-static bool is_reloc_allowed(struct reloc *reloc)
-{
- return section_reference_needed(reloc->sym->sec) == is_sec_sym(reloc->sym);
-}
-
static struct export *find_export(struct symbol *sym)
{
struct export *export;
@@ -979,11 +946,15 @@ static bool klp_reloc_needed(struct reloc *patched_reloc)
return true;
}
+/* Return -1 error, 0 success, 1 skip */
static int convert_reloc_sym_to_secsym(struct elf *elf, struct reloc *reloc)
{
struct symbol *sym = reloc->sym;
struct section *sec = sym->sec;
+ if (is_sec_sym(sym))
+ return 0;
+
if (!sec->sym && !elf_create_section_symbol(elf, sec))
return -1;
@@ -993,11 +964,15 @@ static int convert_reloc_sym_to_secsym(struct elf *elf, struct reloc *reloc)
return 0;
}
+/* Return -1 error, 0 success, 1 skip */
static int convert_reloc_secsym_to_sym(struct elf *elf, struct reloc *reloc)
{
struct symbol *sym = reloc->sym;
struct section *sec = sym->sec;
+ if (!is_sec_sym(sym))
+ return 0;
+
/* If the symbol has a dedicated section, it's easy to find */
sym = find_symbol_by_offset(sec, 0);
if (sym && sym->len == sec_size(sec))
@@ -1027,22 +1002,34 @@ static int convert_reloc_secsym_to_sym(struct elf *elf, struct reloc *reloc)
return 0;
}
+/*
+ * Sections with anonymous or uncorrelated data (strings, UBSAN data)
+ * need section symbol references.
+ */
+static bool is_uncorrelated_section(struct section *sec)
+{
+ return is_string_sec(sec) ||
+ strstarts(sec->name, ".rodata") ||
+ strstarts(sec->name, ".data..Lubsan") || /* GCC */
+ strstarts(sec->name, ".data..L__unnamed_"); /* Clang */
+}
+
/*
* Convert a relocation symbol reference to the needed format: either a section
- * symbol or the underlying symbol itself.
+ * symbol or the underlying symbol itself. Return -1 error, 0 success, 1 skip.
*/
static int convert_reloc_sym(struct elf *elf, struct reloc *reloc)
{
+ struct section *sec = reloc->sym->sec;
+
if (reloc_type(reloc) == R_NONE)
return 1;
- if (is_reloc_allowed(reloc))
- return 0;
-
- if (section_reference_needed(reloc->sym->sec))
+ if (is_uncorrelated_section(sec))
return convert_reloc_sym_to_secsym(elf, reloc);
- else
- return convert_reloc_secsym_to_sym(elf, reloc);
+
+ /* Everything else: references should use named symbols. */
+ return convert_reloc_secsym_to_sym(elf, reloc);
}
/*
@@ -1187,13 +1174,6 @@ static int clone_reloc(struct elfs *e, struct reloc *patched_reloc,
struct symbol *out_sym;
bool klp;
- if (!is_reloc_allowed(patched_reloc)) {
- ERROR_FUNC(patched_reloc->sec->base, reloc_offset(patched_reloc),
- "missing symbol for reference to %s+%ld",
- patched_sym->name, addend);
- return -1;
- }
-
klp = klp_reloc_needed(patched_reloc);
dbg_clone_reloc(sec, offset, patched_sym, addend, export, klp);
@@ -1223,7 +1203,7 @@ static int clone_reloc(struct elfs *e, struct reloc *patched_reloc,
/*
* For strings, all references use section symbols, thanks to
- * section_reference_needed(). clone_symbol() has cloned an empty
+ * convert_reloc_sym(). clone_symbol() has cloned an empty
* version of the string section. Now copy the string itself.
*/
if (is_string_sec(patched_sym->sec)) {
--
2.53.0
^ permalink raw reply related
* [PATCH v2 17/53] objtool: Move mark_rodata() to elf.c
From: Josh Poimboeuf @ 2026-05-01 4:08 UTC (permalink / raw)
To: x86
Cc: linux-kernel, live-patching, Peter Zijlstra, Joe Lawrence,
Song Liu, Miroslav Benes, Petr Mladek
In-Reply-To: <cover.1777575752.git.jpoimboe@kernel.org>
Move the sec->rodata marking from check.c to elf.c so it's set during
ELF reading rather than during the check pipeline. This makes the
rodata flag available to all objtool users, including klp-diff which
reads ELF files directly without running check().
Add an is_rodata_sec() helper to elf.h for consistency with
is_text_sec() and is_string_sec().
Acked-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Acked-by: Song Liu <song@kernel.org>
Reviewed-by: Miroslav Benes <mbenes@suse.cz>
Signed-off-by: Josh Poimboeuf <jpoimboe@kernel.org>
---
tools/objtool/check.c | 11 +++--------
tools/objtool/elf.c | 13 +++++++++++++
tools/objtool/include/objtool/elf.h | 5 +++++
3 files changed, 21 insertions(+), 8 deletions(-)
diff --git a/tools/objtool/check.c b/tools/objtool/check.c
index e3604b1201f9..e7579c4e46dc 100644
--- a/tools/objtool/check.c
+++ b/tools/objtool/check.c
@@ -2566,7 +2566,6 @@ static int classify_symbols(struct objtool_file *file)
static void mark_rodata(struct objtool_file *file)
{
struct section *sec;
- bool found = false;
/*
* Search for the following rodata sections, each of which can
@@ -2579,15 +2578,11 @@ static void mark_rodata(struct objtool_file *file)
* .rodata.str1.* sections are ignored; they don't contain jump tables.
*/
for_each_sec(file->elf, sec) {
- if ((!strncmp(sec->name, ".rodata", 7) &&
- !strstr(sec->name, ".str1.")) ||
- !strncmp(sec->name, ".data.rel.ro", 12)) {
- sec->rodata = true;
- found = true;
+ if (is_rodata_sec(sec)) {
+ file->rodata = true;
+ return;
}
}
-
- file->rodata = found;
}
static void mark_holes(struct objtool_file *file)
diff --git a/tools/objtool/elf.c b/tools/objtool/elf.c
index dc39132f71c1..87c6e00749c6 100644
--- a/tools/objtool/elf.c
+++ b/tools/objtool/elf.c
@@ -1138,6 +1138,17 @@ static int read_relocs(struct elf *elf)
return 0;
}
+static void mark_rodata(struct elf *elf)
+{
+ struct section *sec;
+
+ for_each_sec(elf, sec) {
+ if ((strstarts(sec->name, ".rodata") && !strstr(sec->name, ".str1.")) ||
+ strstarts(sec->name, ".data.rel.ro"))
+ sec->rodata = true;
+ }
+}
+
struct elf *elf_open_read(const char *name, int flags)
{
struct elf *elf;
@@ -1188,6 +1199,8 @@ struct elf *elf_open_read(const char *name, int flags)
if (read_sections(elf))
goto err;
+ mark_rodata(elf);
+
if (read_symbols(elf))
goto err;
diff --git a/tools/objtool/include/objtool/elf.h b/tools/objtool/include/objtool/elf.h
index 00b04029023e..ab5f7017ec34 100644
--- a/tools/objtool/include/objtool/elf.h
+++ b/tools/objtool/include/objtool/elf.h
@@ -317,6 +317,11 @@ static inline bool is_text_sec(struct section *sec)
return sec->sh.sh_flags & SHF_EXECINSTR;
}
+static inline bool is_rodata_sec(struct section *sec)
+{
+ return sec->rodata;
+}
+
static inline bool sec_changed(struct section *sec)
{
return sec->_changed;
--
2.53.0
^ permalink raw reply related
* [PATCH v2 16/53] objtool/klp: Fix relocation conversion failures for R_X86_64_NONE
From: Josh Poimboeuf @ 2026-05-01 4:08 UTC (permalink / raw)
To: x86
Cc: linux-kernel, live-patching, Peter Zijlstra, Joe Lawrence,
Song Liu, Miroslav Benes, Petr Mladek
In-Reply-To: <cover.1777575752.git.jpoimboe@kernel.org>
Objtool has some hacks which NOP out certain calls/jumps and replace
their relocations with R_X86_64_NONE. The klp-diff relocation
extraction code will error out when trying to copy these relocations due
to their negative addend, which would only makes sense for a PC-relative
branch instruction. Just ignore them.
Fixes: dd590d4d57eb ("objtool/klp: Introduce klp diff subcommand for diffing object files")
Acked-by: Song Liu <song@kernel.org>
Reviewed-by: Miroslav Benes <mbenes@suse.cz>
Signed-off-by: Josh Poimboeuf <jpoimboe@kernel.org>
---
tools/objtool/klp-diff.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/tools/objtool/klp-diff.c b/tools/objtool/klp-diff.c
index dd0e51dfc621..19bc811db396 100644
--- a/tools/objtool/klp-diff.c
+++ b/tools/objtool/klp-diff.c
@@ -1033,6 +1033,9 @@ static int convert_reloc_secsym_to_sym(struct elf *elf, struct reloc *reloc)
*/
static int convert_reloc_sym(struct elf *elf, struct reloc *reloc)
{
+ if (reloc_type(reloc) == R_NONE)
+ return 1;
+
if (is_reloc_allowed(reloc))
return 0;
--
2.53.0
^ permalink raw reply related
* [PATCH v2 15/53] objtool/klp: Fix kCFI trap handling
From: Josh Poimboeuf @ 2026-05-01 4:08 UTC (permalink / raw)
To: x86
Cc: linux-kernel, live-patching, Peter Zijlstra, Joe Lawrence,
Song Liu, Miroslav Benes, Petr Mladek
In-Reply-To: <cover.1777575752.git.jpoimboe@kernel.org>
.kcfi_traps contains references to kCFI trap instruction locations.
When a KCFI type check fails at an indirect call, the trap handler looks
up the faulting address in this section.
Add it to the special sections list so the entries get extracted for the
changed functions they reference.
Acked-by: Song Liu <song@kernel.org>
Reviewed-by: Miroslav Benes <mbenes@suse.cz>
Signed-off-by: Josh Poimboeuf <jpoimboe@kernel.org>
---
tools/objtool/klp-diff.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/tools/objtool/klp-diff.c b/tools/objtool/klp-diff.c
index 42970b38728f..dd0e51dfc621 100644
--- a/tools/objtool/klp-diff.c
+++ b/tools/objtool/klp-diff.c
@@ -291,6 +291,7 @@ static bool is_special_section(struct section *sec)
{
static const char * const specials[] = {
".altinstructions",
+ ".kcfi_traps",
".smp_locks",
"__bug_table",
"__ex_table",
--
2.53.0
^ permalink raw reply related
* [PATCH v2 14/53] objtool/klp: Fix extraction of text annotations for alternatives
From: Josh Poimboeuf @ 2026-05-01 4:08 UTC (permalink / raw)
To: x86
Cc: linux-kernel, live-patching, Peter Zijlstra, Joe Lawrence,
Song Liu, Miroslav Benes, Petr Mladek
In-Reply-To: <cover.1777575752.git.jpoimboe@kernel.org>
Objtool is failing to extract text annotations which reference
.altinstr_replacement instructions:
1) Alternative replacement fake symbols are NOTYPE rather than FUNC,
and they don't have sym->included set, thus they aren't recognized
by should_keep_special_sym().
2) .discard.annotate_insn gets processed before .altinstr_replacement,
so the referenced (fake) symbols don't have clones yet.
Fix the first issue by checking for a valid clone instead of
sym->included and by accepting NOTYPE symbols when processing
.discard.annotate_insn.
Fix the second issue by deferring text annotation processing until after
the other special sections have been cloned.
Fixes: dd590d4d57eb ("objtool/klp: Introduce klp diff subcommand for diffing object files")
Acked-by: Song Liu <song@kernel.org>
Reviewed-by: Miroslav Benes <mbenes@suse.cz>
Signed-off-by: Josh Poimboeuf <jpoimboe@kernel.org>
---
tools/objtool/klp-diff.c | 33 ++++++++++++++++++++++++++++-----
1 file changed, 28 insertions(+), 5 deletions(-)
diff --git a/tools/objtool/klp-diff.c b/tools/objtool/klp-diff.c
index 17a6146b9406..42970b38728f 100644
--- a/tools/objtool/klp-diff.c
+++ b/tools/objtool/klp-diff.c
@@ -1437,6 +1437,7 @@ static int create_fake_symbols(struct elf *elf)
/* Keep a special section entry if it references an included function */
static bool should_keep_special_sym(struct elf *elf, struct symbol *sym)
{
+ bool annotate_insn = !strcmp(sym->sec->name, ".discard.annotate_insn");
struct reloc *reloc;
if (is_sec_sym(sym) || !sym->sec->rsec)
@@ -1446,7 +1447,16 @@ static bool should_keep_special_sym(struct elf *elf, struct symbol *sym)
if (convert_reloc_sym(elf, reloc))
continue;
- if (is_func_sym(reloc->sym) && reloc->sym->included)
+ if (!reloc->sym->clone || is_undef_sym(reloc->sym->clone))
+ continue;
+
+ /*
+ * Keep special section references to cloned functions.
+ * In some cases annotate_insn can also reference cloned alt
+ * replacement fake symbols; keep those references as well.
+ */
+ if (is_func_sym(reloc->sym) ||
+ (annotate_insn && is_notype_sym(reloc->sym)))
return true;
}
@@ -1590,15 +1600,28 @@ static int clone_special_section(struct elfs *e, struct section *patched_sec)
/* Extract only the needed bits from special sections */
static int clone_special_sections(struct elfs *e)
{
- struct section *patched_sec;
+ struct section *sec, *annotate_insn = NULL;
- for_each_sec(e->patched, patched_sec) {
- if (is_special_section(patched_sec)) {
- if (clone_special_section(e, patched_sec))
+ for_each_sec(e->patched, sec) {
+ if (is_special_section(sec)) {
+ if (!strcmp(sec->name, ".discard.annotate_insn")) {
+ annotate_insn = sec;
+ continue;
+ }
+ if (clone_special_section(e, sec))
return -1;
}
}
+ /*
+ * Do .discard.annotate_insn last, it can reference other special
+ * sections (alt replacements) so they need to be cloned first.
+ */
+ if (annotate_insn) {
+ if (clone_special_section(e, annotate_insn))
+ return -1;
+ }
+
return 0;
}
--
2.53.0
^ permalink raw reply related
* [PATCH v2 13/53] objtool/klp: Fix XXH3 state memory leak
From: Josh Poimboeuf @ 2026-05-01 4:08 UTC (permalink / raw)
To: x86
Cc: linux-kernel, live-patching, Peter Zijlstra, Joe Lawrence,
Song Liu, Miroslav Benes, Petr Mladek
In-Reply-To: <cover.1777575752.git.jpoimboe@kernel.org>
The XXH3 state allocated in checksum_init() is never freed. Free it in
checksum_finish().
Acked-by: Song Liu <song@kernel.org>
Reviewed-by: Miroslav Benes <mbenes@suse.cz>
Signed-off-by: Josh Poimboeuf <jpoimboe@kernel.org>
---
tools/objtool/include/objtool/checksum.h | 1 +
1 file changed, 1 insertion(+)
diff --git a/tools/objtool/include/objtool/checksum.h b/tools/objtool/include/objtool/checksum.h
index 7fe21608722a..0bd16fe9168b 100644
--- a/tools/objtool/include/objtool/checksum.h
+++ b/tools/objtool/include/objtool/checksum.h
@@ -26,6 +26,7 @@ static inline void checksum_finish(struct symbol *func)
{
if (func && func->csum.state) {
func->csum.checksum = XXH3_64bits_digest(func->csum.state);
+ XXH3_freeState(func->csum.state);
func->csum.state = NULL;
}
}
--
2.53.0
^ permalink raw reply related
* [PATCH v2 12/53] objtool/klp: Fix cloning of zero-length section symbols
From: Josh Poimboeuf @ 2026-05-01 4:08 UTC (permalink / raw)
To: x86
Cc: linux-kernel, live-patching, Peter Zijlstra, Joe Lawrence,
Song Liu, Miroslav Benes, Petr Mladek
In-Reply-To: <cover.1777575752.git.jpoimboe@kernel.org>
Fix NULL dereference when cloning a symbol from an empty section.
sec->data is only populated for sections with non-zero size.
Fixes: dd590d4d57eb ("objtool/klp: Introduce klp diff subcommand for diffing object files")
Acked-by: Song Liu <song@kernel.org>
Reviewed-by: Miroslav Benes <mbenes@suse.cz>
Signed-off-by: Josh Poimboeuf <jpoimboe@kernel.org>
---
tools/objtool/klp-diff.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tools/objtool/klp-diff.c b/tools/objtool/klp-diff.c
index a226e99948b3..17a6146b9406 100644
--- a/tools/objtool/klp-diff.c
+++ b/tools/objtool/klp-diff.c
@@ -681,7 +681,7 @@ static struct symbol *__clone_symbol(struct elf *elf, struct symbol *patched_sym
size_t size;
/* bss doesn't have data */
- if (patched_sym->sec->data->d_buf)
+ if (patched_sym->sec->data && patched_sym->sec->data->d_buf)
data = patched_sym->sec->data->d_buf + patched_sym->offset;
if (is_sec_sym(patched_sym))
--
2.53.0
^ permalink raw reply related
* [PATCH v2 11/53] objtool/klp: Fix handling of zero-length .altinstr_replacement sections
From: Josh Poimboeuf @ 2026-05-01 4:07 UTC (permalink / raw)
To: x86
Cc: linux-kernel, live-patching, Peter Zijlstra, Joe Lawrence,
Song Liu, Miroslav Benes, Petr Mladek
In-Reply-To: <cover.1777575752.git.jpoimboe@kernel.org>
When a section is empty (e.g. only zero-length alternative
replacements), there are no symbols to convert a section symbol
reference to. Skip the reloc instead of erroring out.
Fixes: dd590d4d57eb ("objtool/klp: Introduce klp diff subcommand for diffing object files")
Acked-by: Song Liu <song@kernel.org>
Reviewed-by: Miroslav Benes <mbenes@suse.cz>
Signed-off-by: Josh Poimboeuf <jpoimboe@kernel.org>
---
tools/objtool/klp-diff.c | 13 ++++++++++++-
1 file changed, 12 insertions(+), 1 deletion(-)
diff --git a/tools/objtool/klp-diff.c b/tools/objtool/klp-diff.c
index 30ce234e01a1..a226e99948b3 100644
--- a/tools/objtool/klp-diff.c
+++ b/tools/objtool/klp-diff.c
@@ -1005,6 +1005,13 @@ static int convert_reloc_secsym_to_sym(struct elf *elf, struct reloc *reloc)
/* No dedicated section; find the symbol manually */
sym = find_symbol_containing(sec, arch_adjusted_addend(reloc));
if (!sym) {
+ /*
+ * This is presumably an .altinstr_replacement section which is
+ * empty due to it only having zero-length replacement(s).
+ */
+ if (!sec_size(sec))
+ return 1;
+
/*
* This can happen for special section references to weak code
* whose symbol has been stripped by the linker.
@@ -1265,6 +1272,7 @@ static int clone_sym_relocs(struct elfs *e, struct symbol *patched_sym)
for_each_reloc(patched_rsec, patched_reloc) {
unsigned long offset;
+ int ret;
if (reloc_offset(patched_reloc) < start ||
reloc_offset(patched_reloc) >= end)
@@ -1278,12 +1286,15 @@ static int clone_sym_relocs(struct elfs *e, struct symbol *patched_sym)
!strcmp(patched_reloc->sym->sec->name, ".altinstr_aux"))
continue;
- if (convert_reloc_sym(e->patched, patched_reloc)) {
+ ret = convert_reloc_sym(e->patched, patched_reloc);
+ if (ret < 0) {
ERROR_FUNC(patched_rsec->base, reloc_offset(patched_reloc),
"failed to convert reloc sym '%s' to its proper format",
patched_reloc->sym->name);
return -1;
}
+ if (ret > 0)
+ continue;
offset = out_sym->offset + (reloc_offset(patched_reloc) - patched_sym->offset);
--
2.53.0
^ permalink raw reply related
* [PATCH v2 10/53] objtool/klp: Fix --debug-checksum for duplicate symbol names
From: Josh Poimboeuf @ 2026-05-01 4:07 UTC (permalink / raw)
To: x86
Cc: linux-kernel, live-patching, Peter Zijlstra, Joe Lawrence,
Song Liu, Miroslav Benes, Petr Mladek
In-Reply-To: <cover.1777575752.git.jpoimboe@kernel.org>
find_symbol_by_name() only returns the first match, so
--debug-checksum=<func> silently ignores any subsequent duplicately
named functions after the first.
Fix that, along with a new for_each_sym_by_name() helper.
Acked-by: Song Liu <song@kernel.org>
Signed-off-by: Josh Poimboeuf <jpoimboe@kernel.org>
---
tools/objtool/check.c | 15 ++++++++++-----
tools/objtool/include/objtool/elf.h | 5 +++++
2 files changed, 15 insertions(+), 5 deletions(-)
diff --git a/tools/objtool/check.c b/tools/objtool/check.c
index 9b11cf3193b9..e3604b1201f9 100644
--- a/tools/objtool/check.c
+++ b/tools/objtool/check.c
@@ -3677,18 +3677,23 @@ static int checksum_debug_init(struct objtool_file *file)
s = dup;
while (*s) {
- struct symbol *func;
+ bool found = false;
+ struct symbol *sym;
char *comma;
comma = strchr(s, ',');
if (comma)
*comma = '\0';
- func = find_symbol_by_name(file->elf, s);
- if (!func || !is_func_sym(func))
+ for_each_sym_by_name(file->elf, s, sym) {
+ if (!is_func_sym(sym))
+ continue;
+ sym->debug_checksum = 1;
+ found = true;
+ }
+
+ if (!found)
WARN("--debug-checksum: can't find '%s'", s);
- else
- func->debug_checksum = 1;
if (!comma)
break;
diff --git a/tools/objtool/include/objtool/elf.h b/tools/objtool/include/objtool/elf.h
index b142984eb9b5..00b04029023e 100644
--- a/tools/objtool/include/objtool/elf.h
+++ b/tools/objtool/include/objtool/elf.h
@@ -489,6 +489,11 @@ static inline void set_sym_next_reloc(struct reloc *reloc, struct reloc *next)
#define for_each_sym_continue(elf, sym) \
list_for_each_entry_continue(sym, &elf->symbols, global_list)
+#define for_each_sym_by_name(elf, _name, sym) \
+ elf_hash_for_each_possible(elf, symbol_name, sym, name_hash, \
+ str_hash_demangled(_name)) \
+ if (strcmp(sym->name, _name)) {} else
+
#define for_each_sym_by_demangled_name(elf, name, sym) \
elf_hash_for_each_possible(elf, symbol_name, sym, name_hash, \
str_hash(name)) \
--
2.53.0
^ permalink raw reply related
* [PATCH v2 09/53] objtool: Replace iterator callback with for_each_sym_by_mangled_name()
From: Josh Poimboeuf @ 2026-05-01 4:07 UTC (permalink / raw)
To: x86
Cc: linux-kernel, live-patching, Peter Zijlstra, Joe Lawrence,
Song Liu, Miroslav Benes, Petr Mladek
In-Reply-To: <cover.1777575752.git.jpoimboe@kernel.org>
Convert the callback-based iterate_sym_by_demangled_name() with a new
for_each_sym_by_demangled_name() macro. This eliminates the callback
struct/function and makes the code more compact and readable.
Signed-off-by: Josh Poimboeuf <jpoimboe@kernel.org>
---
tools/objtool/elf.c | 68 ++++++++---------------------
tools/objtool/include/objtool/elf.h | 32 ++++++++++++--
tools/objtool/klp-diff.c | 42 ++++++------------
3 files changed, 60 insertions(+), 82 deletions(-)
diff --git a/tools/objtool/elf.c b/tools/objtool/elf.c
index f3df2bde119f..dc39132f71c1 100644
--- a/tools/objtool/elf.c
+++ b/tools/objtool/elf.c
@@ -27,27 +27,16 @@
static ssize_t demangled_name_len(const char *name);
-static inline u32 str_hash(const char *str)
-{
- return jhash(str, strlen(str), 0);
-}
-
-static inline u32 str_hash_demangled(const char *str)
+u32 str_hash_demangled(const char *str)
{
return jhash(str, demangled_name_len(str), 0);
}
-#define __elf_table(name) (elf->name##_hash)
-#define __elf_bits(name) (elf->name##_bits)
-
-#define __elf_table_entry(name, key) \
- __elf_table(name)[hash_min(key, __elf_bits(name))]
-
#define elf_hash_add(name, node, key) \
({ \
struct elf_hash_node *__node = node; \
- __node->next = __elf_table_entry(name, key); \
- __elf_table_entry(name, key) = __node; \
+ __node->next = __elf_table_entry(elf, name, key); \
+ __elf_table_entry(elf, name, key) = __node; \
})
static inline void __elf_hash_del(struct elf_hash_node *node,
@@ -69,30 +58,20 @@ static inline void __elf_hash_del(struct elf_hash_node *node,
}
#define elf_hash_del(name, node, key) \
- __elf_hash_del(node, &__elf_table_entry(name, key))
-
-#define elf_list_entry(ptr, type, member) \
-({ \
- typeof(ptr) __ptr = (ptr); \
- __ptr ? container_of(__ptr, type, member) : NULL; \
-})
-
-#define elf_hash_for_each_possible(name, obj, member, key) \
- for (obj = elf_list_entry(__elf_table_entry(name, key), typeof(*obj), member); \
- obj; \
- obj = elf_list_entry(obj->member.next, typeof(*(obj)), member))
+ __elf_hash_del(node, &__elf_table_entry(elf, name, key))
#define elf_alloc_hash(name, size) \
({ \
- __elf_bits(name) = max(10, ilog2(size)); \
- __elf_table(name) = mmap(NULL, sizeof(struct elf_hash_node *) << __elf_bits(name), \
+ __elf_bits(elf, name) = max(10, ilog2(size)); \
+ __elf_table(elf, name) = mmap(NULL, \
+ sizeof(struct elf_hash_node *) << __elf_bits(elf, name), \
PROT_READ|PROT_WRITE, \
MAP_PRIVATE|MAP_ANON, -1, 0); \
- if (__elf_table(name) == (void *)-1L) { \
+ if (__elf_table(elf, name) == (void *)-1L) { \
ERROR_GLIBC("mmap fail " #name); \
- __elf_table(name) = NULL; \
+ __elf_table(elf, name) = NULL; \
} \
- __elf_table(name); \
+ __elf_table(elf, name); \
})
static inline unsigned long __sym_start(struct symbol *s)
@@ -141,7 +120,7 @@ struct section *find_section_by_name(const struct elf *elf, const char *name)
{
struct section *sec;
- elf_hash_for_each_possible(section_name, sec, name_hash, str_hash(name)) {
+ elf_hash_for_each_possible(elf, section_name, sec, name_hash, str_hash(name)) {
if (!strcmp(sec->name, name))
return sec;
}
@@ -154,7 +133,7 @@ static struct section *find_section_by_index(struct elf *elf,
{
struct section *sec;
- elf_hash_for_each_possible(section, sec, hash, idx) {
+ elf_hash_for_each_possible(elf, section, sec, hash, idx) {
if (sec->idx == idx)
return sec;
}
@@ -166,7 +145,7 @@ static struct symbol *find_symbol_by_index(struct elf *elf, unsigned int idx)
{
struct symbol *sym;
- elf_hash_for_each_possible(symbol, sym, hash, idx) {
+ elf_hash_for_each_possible(elf, symbol, sym, hash, idx) {
if (sym->idx == idx)
return sym;
}
@@ -285,7 +264,7 @@ struct symbol *find_symbol_by_name(const struct elf *elf, const char *name)
{
struct symbol *sym;
- elf_hash_for_each_possible(symbol_name, sym, name_hash, str_hash(name)) {
+ elf_hash_for_each_possible(elf, symbol_name, sym, name_hash, str_hash(name)) {
if (!strcmp(sym->name, name))
return sym;
}
@@ -300,7 +279,7 @@ static struct symbol *find_local_symbol_by_file_and_name(const struct elf *elf,
{
struct symbol *sym;
- elf_hash_for_each_possible(symbol_name, sym, name_hash, str_hash_demangled(name)) {
+ elf_hash_for_each_possible(elf, symbol_name, sym, name_hash, str_hash_demangled(name)) {
if (sym->bind == STB_LOCAL && sym->file == file &&
!strcmp(sym->name, name)) {
return sym;
@@ -314,7 +293,7 @@ struct symbol *find_global_symbol_by_name(const struct elf *elf, const char *nam
{
struct symbol *sym;
- elf_hash_for_each_possible(symbol_name, sym, name_hash, str_hash_demangled(name)) {
+ elf_hash_for_each_possible(elf, symbol_name, sym, name_hash, str_hash_demangled(name)) {
if (!strcmp(sym->name, name) && !is_local_sym(sym))
return sym;
}
@@ -322,19 +301,6 @@ struct symbol *find_global_symbol_by_name(const struct elf *elf, const char *nam
return NULL;
}
-void iterate_global_symbol_by_demangled_name(const struct elf *elf,
- const char *demangled_name,
- void (*process)(struct symbol *sym, void *data),
- void *data)
-{
- struct symbol *sym;
-
- elf_hash_for_each_possible(symbol_name, sym, name_hash, str_hash(demangled_name)) {
- if (!strcmp(sym->demangled_name, demangled_name) && !is_local_sym(sym))
- process(sym, data);
- }
-}
-
struct reloc *find_reloc_by_dest_range(const struct elf *elf, struct section *sec,
unsigned long offset, unsigned int len)
{
@@ -347,7 +313,7 @@ struct reloc *find_reloc_by_dest_range(const struct elf *elf, struct section *se
return NULL;
for_offset_range(o, offset, offset + len) {
- elf_hash_for_each_possible(reloc, reloc, hash,
+ elf_hash_for_each_possible(elf, reloc, reloc, hash,
sec_offset_hash(rsec, o)) {
if (reloc->sec != rsec)
continue;
diff --git a/tools/objtool/include/objtool/elf.h b/tools/objtool/include/objtool/elf.h
index 25573e5af76e..b142984eb9b5 100644
--- a/tools/objtool/include/objtool/elf.h
+++ b/tools/objtool/include/objtool/elf.h
@@ -21,6 +21,13 @@
#define SEC_NAME_LEN 1024
#define SYM_NAME_LEN 512
+static inline u32 str_hash(const char *str)
+{
+ return jhash(str, strlen(str), 0);
+}
+
+u32 str_hash_demangled(const char *str);
+
#define bswap_if_needed(elf, val) __bswap_if_needed(&elf->ehdr, val)
#ifdef LIBELF_USE_DEPRECATED
@@ -130,6 +137,23 @@ struct elf {
struct symbol *symbol_data;
};
+#define __elf_table(elf, name) ((elf)->name##_hash)
+#define __elf_bits(elf, name) ((elf)->name##_bits)
+
+#define __elf_table_entry(elf, name, key) \
+ __elf_table(elf, name)[hash_min(key, __elf_bits(elf, name))]
+
+#define elf_list_entry(ptr, type, member) \
+({ \
+ typeof(ptr) __ptr = (ptr); \
+ __ptr ? container_of(__ptr, type, member) : NULL; \
+})
+
+#define elf_hash_for_each_possible(elf, name, obj, member, key) \
+ for (obj = elf_list_entry(__elf_table_entry(elf, name, key), typeof(*obj), member); \
+ obj; \
+ obj = elf_list_entry(obj->member.next, typeof(*(obj)), member))
+
struct elf *elf_open_read(const char *name, int flags);
struct elf *elf_create_file(GElf_Ehdr *ehdr, const char *name);
@@ -186,9 +210,6 @@ struct symbol *find_func_by_offset(struct section *sec, unsigned long offset);
struct symbol *find_symbol_by_offset(struct section *sec, unsigned long offset);
struct symbol *find_symbol_by_name(const struct elf *elf, const char *name);
struct symbol *find_global_symbol_by_name(const struct elf *elf, const char *name);
-void iterate_global_symbol_by_demangled_name(const struct elf *elf, const char *demangled_name,
- void (*process)(struct symbol *sym, void *data),
- void *data);
struct symbol *find_symbol_containing(const struct section *sec, unsigned long offset);
int find_symbol_hole_containing(const struct section *sec, unsigned long offset);
struct reloc *find_reloc_by_dest(const struct elf *elf, struct section *sec, unsigned long offset);
@@ -468,6 +489,11 @@ static inline void set_sym_next_reloc(struct reloc *reloc, struct reloc *next)
#define for_each_sym_continue(elf, sym) \
list_for_each_entry_continue(sym, &elf->symbols, global_list)
+#define for_each_sym_by_demangled_name(elf, name, sym) \
+ elf_hash_for_each_possible(elf, symbol_name, sym, name_hash, \
+ str_hash(name)) \
+ if (strcmp(sym->demangled_name, name)) {} else
+
#define rsec_next_reloc(rsec, reloc) \
reloc_idx(reloc) < sec_num_entries(rsec) - 1 ? reloc + 1 : NULL
diff --git a/tools/objtool/klp-diff.c b/tools/objtool/klp-diff.c
index 0653bf6a33bd..30ce234e01a1 100644
--- a/tools/objtool/klp-diff.c
+++ b/tools/objtool/klp-diff.c
@@ -46,11 +46,6 @@ static const struct option klp_diff_options[] = {
static DEFINE_HASHTABLE(exports, 15);
-static inline u32 str_hash(const char *str)
-{
- return jhash(str, strlen(str), 0);
-}
-
static char *escape_str(const char *orig)
{
size_t len = 0;
@@ -396,22 +391,6 @@ static bool dont_correlate(struct symbol *sym)
is_special_section_aux(sym->sec);
}
-struct process_demangled_name_data {
- struct symbol *ret;
- int count;
-};
-
-static void process_demangled_name(struct symbol *sym, void *d)
-{
- struct process_demangled_name_data *data = d;
-
- if (sym->twin)
- return;
-
- data->count++;
- data->ret = sym;
-}
-
/*
* When there is no full name match, try match demangled_name. This would
* match original foo.llvm.123 to patched foo.llvm.456.
@@ -423,16 +402,23 @@ static void process_demangled_name(struct symbol *sym, void *d)
static int find_global_symbol_by_demangled_name(struct elf *elf, struct symbol *sym,
struct symbol **out_sym)
{
- struct process_demangled_name_data data = {};
+ struct symbol *sym2, *result = NULL;
+ int count = 0;
- iterate_global_symbol_by_demangled_name(elf, sym->demangled_name,
- process_demangled_name,
- &data);
- if (data.count > 1) {
- ERROR("Multiple (%d) correlation candidates for %s", data.count, sym->name);
+ for_each_sym_by_demangled_name(elf, sym->demangled_name, sym2) {
+ if (is_local_sym(sym2) || sym2->twin)
+ continue;
+
+ count++;
+ result = sym2;
+ }
+
+ if (count > 1) {
+ ERROR("Multiple (%d) correlation candidates for %s", count, sym->name);
return -1;
}
- *out_sym = data.ret;
+
+ *out_sym = result;
return 0;
}
--
2.53.0
^ permalink raw reply related
* [PATCH v2 08/53] objtool/klp: Fix create_fake_symbols() skipping entsize-based sections
From: Josh Poimboeuf @ 2026-05-01 4:07 UTC (permalink / raw)
To: x86
Cc: linux-kernel, live-patching, Peter Zijlstra, Joe Lawrence,
Song Liu, Miroslav Benes, Petr Mladek
In-Reply-To: <cover.1777575752.git.jpoimboe@kernel.org>
From: Joe Lawrence <joe.lawrence@redhat.com>
create_fake_symbols() has two phases: creating symbols from
ANNOTATE_DATA_SPECIAL entries, and a fallback that uses sh_entsize for
special sections like .static_call_sites.
When .discard.annotate_data is absent, the function returns early,
skipping the entsize fallback and silently allowing unsupported
module-local static call keys through.
Fix it by jumping to the entsize phase instead of returning early.
Fixes: dd590d4d57eb ("objtool/klp: Introduce klp diff subcommand for diffing object files")
Assisted-by: Claude:claude-4-opus
Signed-off-by: Joe Lawrence <joe.lawrence@redhat.com>
Acked-by: Song Liu <song@kernel.org>
Reviewed-by: Miroslav Benes <mbenes@suse.cz>
Signed-off-by: Josh Poimboeuf <jpoimboe@kernel.org>
---
tools/objtool/klp-diff.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/tools/objtool/klp-diff.c b/tools/objtool/klp-diff.c
index c5d4c9ed8580..0653bf6a33bd 100644
--- a/tools/objtool/klp-diff.c
+++ b/tools/objtool/klp-diff.c
@@ -1374,7 +1374,7 @@ static int create_fake_symbols(struct elf *elf)
sec = find_section_by_name(elf, ".discard.annotate_data");
if (!sec || !sec->rsec)
- return 0;
+ goto entsize;
for_each_reloc(sec->rsec, reloc) {
unsigned long offset, size;
@@ -1406,7 +1406,7 @@ static int create_fake_symbols(struct elf *elf)
/*
* 2) Make symbols for sh_entsize, and simple arrays of pointers:
*/
-
+entsize:
for_each_sec(elf, sec) {
unsigned int entry_size;
unsigned long offset;
--
2.53.0
^ permalink raw reply related
* [PATCH v2 07/53] objtool/klp: Improve local label check
From: Josh Poimboeuf @ 2026-05-01 4:07 UTC (permalink / raw)
To: x86
Cc: linux-kernel, live-patching, Peter Zijlstra, Joe Lawrence,
Song Liu, Miroslav Benes, Petr Mladek
In-Reply-To: <cover.1777575752.git.jpoimboe@kernel.org>
Clang emits various .L-prefixed local symbols beyond .Ltmp*, such as
.L__const.* for local constant data. These are assembler-local labels
not present in kallsyms, so they can never be resolved at module load
time.
Broaden the check from .Ltmp* to all .L* symbols so they get cloned into
the patch module instead.
Signed-off-by: Josh Poimboeuf <jpoimboe@kernel.org>
---
tools/objtool/klp-diff.c | 14 +++++++-------
1 file changed, 7 insertions(+), 7 deletions(-)
diff --git a/tools/objtool/klp-diff.c b/tools/objtool/klp-diff.c
index ccb16a45107e..c5d4c9ed8580 100644
--- a/tools/objtool/klp-diff.c
+++ b/tools/objtool/klp-diff.c
@@ -282,14 +282,14 @@ static bool is_uncorrelated_static_local(struct symbol *sym)
}
/*
- * Clang emits several useless .Ltmp_* code labels.
+ * .L symbols are assembler-local labels not present in kallsyms. They must
+ * never become KLP relocations; instead their data is cloned into the patch
+ * module. This covers .Ltmp* (Clang temp labels), .L__const.* (Clang local
+ * constants), and any other assembler-local pattern.
*/
-static bool is_clang_tmp_label(struct symbol *sym)
+static bool is_local_label(struct symbol *sym)
{
- return is_notype_sym(sym) &&
- is_text_sec(sym->sec) &&
- strstarts(sym->name, ".Ltmp") &&
- isdigit(sym->name[5]);
+ return strstarts(sym->name, ".L");
}
static bool is_special_section(struct section *sec)
@@ -388,7 +388,7 @@ static bool dont_correlate(struct symbol *sym)
is_abs_sym(sym) ||
is_prefix_func(sym) ||
is_uncorrelated_static_local(sym) ||
- is_clang_tmp_label(sym) ||
+ is_local_label(sym) ||
is_string_sec(sym->sec) ||
is_initcall_sym(sym) ||
is_addressable_sym(sym) ||
--
2.53.0
^ permalink raw reply related
* [PATCH v2 06/53] objtool/klp: Don't report uncorrelated functions as new
From: Josh Poimboeuf @ 2026-05-01 4:07 UTC (permalink / raw)
To: x86
Cc: linux-kernel, live-patching, Peter Zijlstra, Joe Lawrence,
Song Liu, Miroslav Benes, Petr Mladek
In-Reply-To: <cover.1777575752.git.jpoimboe@kernel.org>
Clang LTO uses __UNIQUE_ID() to generate some uniquely named wrapper
functions, like initstubs. If they're uncorrelated, prevent them from
being reported as new functions and included unnecessarily.
Note that dont_correlate() already includes prefix functions, so prefix
functions are still being ignored here.
Acked-by: Song Liu <song@kernel.org>
Reviewed-by: Miroslav Benes <mbenes@suse.cz>
Signed-off-by: Josh Poimboeuf <jpoimboe@kernel.org>
---
tools/objtool/klp-diff.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/tools/objtool/klp-diff.c b/tools/objtool/klp-diff.c
index 4f668117c45e..ccb16a45107e 100644
--- a/tools/objtool/klp-diff.c
+++ b/tools/objtool/klp-diff.c
@@ -802,7 +802,7 @@ static int mark_changed_functions(struct elfs *e)
/* Find changed functions */
for_each_sym(e->orig, sym_orig) {
- if (!is_func_sym(sym_orig) || is_prefix_func(sym_orig))
+ if (!is_func_sym(sym_orig) || dont_correlate(sym_orig))
continue;
patched_sym = sym_orig->twin;
@@ -818,7 +818,7 @@ static int mark_changed_functions(struct elfs *e)
/* Find added functions and print them */
for_each_sym(e->patched, patched_sym) {
- if (!is_func_sym(patched_sym) || is_prefix_func(patched_sym))
+ if (!is_func_sym(patched_sym) || dont_correlate(patched_sym))
continue;
if (!patched_sym->twin) {
--
2.53.0
^ permalink raw reply related
* [PATCH v2 05/53] objtool/klp: Don't correlate __initstub__ symbols
From: Josh Poimboeuf @ 2026-05-01 4:07 UTC (permalink / raw)
To: x86
Cc: linux-kernel, live-patching, Peter Zijlstra, Joe Lawrence,
Song Liu, Miroslav Benes, Petr Mladek
In-Reply-To: <cover.1777575752.git.jpoimboe@kernel.org>
With LTO, the initcall infrastructure generates __initstub__kmod_*
wrapper functions in .init.text. These are the LTO equivalent of
__initcall__kmod_* data pointers, which are already excluded from
correlation.
These are __init functions whose memory is freed after boot, so there's
no reason to include or reference them in a livepatch module.
Acked-by: Song Liu <song@kernel.org>
Reviewed-by: Miroslav Benes <mbenes@suse.cz>
Signed-off-by: Josh Poimboeuf <jpoimboe@kernel.org>
---
tools/objtool/klp-diff.c | 10 ++++++++--
1 file changed, 8 insertions(+), 2 deletions(-)
diff --git a/tools/objtool/klp-diff.c b/tools/objtool/klp-diff.c
index 27ebe1b1f463..4f668117c45e 100644
--- a/tools/objtool/klp-diff.c
+++ b/tools/objtool/klp-diff.c
@@ -370,6 +370,12 @@ static bool is_abs_sym(struct symbol *sym)
return sym->sym.st_shndx == SHN_ABS && !is_file_sym(sym);
}
+static bool is_initcall_sym(struct symbol *sym)
+{
+ return strstarts(sym->name, "__initcall__") ||
+ strstarts(sym->name, "__initstub__");
+}
+
/*
* These symbols should never be correlated, so their local patched versions
* are used instead of linking to the originals.
@@ -384,10 +390,10 @@ static bool dont_correlate(struct symbol *sym)
is_uncorrelated_static_local(sym) ||
is_clang_tmp_label(sym) ||
is_string_sec(sym->sec) ||
+ is_initcall_sym(sym) ||
is_addressable_sym(sym) ||
is_special_section(sym->sec) ||
- is_special_section_aux(sym->sec) ||
- strstarts(sym->name, "__initcall__");
+ is_special_section_aux(sym->sec);
}
struct process_demangled_name_data {
--
2.53.0
^ permalink raw reply related
* [PATCH v2 04/53] objtool/klp: Don't correlate absolute symbols
From: Josh Poimboeuf @ 2026-05-01 4:07 UTC (permalink / raw)
To: x86
Cc: linux-kernel, live-patching, Peter Zijlstra, Joe Lawrence,
Song Liu, Miroslav Benes, Petr Mladek
In-Reply-To: <cover.1777575752.git.jpoimboe@kernel.org>
Some arch/x86/crypto/*.S files define local .set/.equ constants that get
duplicated in vmlinux.o. This causes klp-diff to fail with "Multiple
correlation candidates" errors since it can't uniquely match these
between orig and patched builds.
Skip ABS symbols in dont_correlate(). They're purely compile-time
assembly constants that are never referenced by relocations, so they
don't need correlation.
Acked-by: Song Liu <song@kernel.org>
Reviewed-by: Miroslav Benes <mbenes@suse.cz>
Signed-off-by: Josh Poimboeuf <jpoimboe@kernel.org>
---
tools/objtool/klp-diff.c | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/tools/objtool/klp-diff.c b/tools/objtool/klp-diff.c
index 36753eeba58c..27ebe1b1f463 100644
--- a/tools/objtool/klp-diff.c
+++ b/tools/objtool/klp-diff.c
@@ -361,6 +361,15 @@ static bool is_addressable_sym(struct symbol *sym)
return !strcmp(sym->sec->name, ".discard.addressable");
}
+/*
+ * ABS symbols are typically assembly .set/.equ constants which are never
+ * referenced by relocations. (Exclude FILE symbols which are also SHN_ABS.)
+ */
+static bool is_abs_sym(struct symbol *sym)
+{
+ return sym->sym.st_shndx == SHN_ABS && !is_file_sym(sym);
+}
+
/*
* These symbols should never be correlated, so their local patched versions
* are used instead of linking to the originals.
@@ -370,6 +379,7 @@ static bool dont_correlate(struct symbol *sym)
return is_file_sym(sym) ||
is_null_sym(sym) ||
is_sec_sym(sym) ||
+ is_abs_sym(sym) ||
is_prefix_func(sym) ||
is_uncorrelated_static_local(sym) ||
is_clang_tmp_label(sym) ||
--
2.53.0
^ permalink raw reply related
* [PATCH v2 03/53] objtool/klp: Don't correlate __ADDRESSABLE() symbols
From: Josh Poimboeuf @ 2026-05-01 4:07 UTC (permalink / raw)
To: x86
Cc: linux-kernel, live-patching, Peter Zijlstra, Joe Lawrence,
Song Liu, Miroslav Benes, Petr Mladek
In-Reply-To: <cover.1777575752.git.jpoimboe@kernel.org>
Symbols created by __ADDRESSABLE() are only used to convince the
toolchain not to optimize out the referenced symbol.
Reviewed-by: Miroslav Benes <mbenes@suse.cz>
Signed-off-by: Josh Poimboeuf <jpoimboe@kernel.org>
---
tools/objtool/klp-diff.c | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/tools/objtool/klp-diff.c b/tools/objtool/klp-diff.c
index cb26c1c92a74..36753eeba58c 100644
--- a/tools/objtool/klp-diff.c
+++ b/tools/objtool/klp-diff.c
@@ -352,6 +352,15 @@ static bool is_special_section_aux(struct section *sec)
return false;
}
+/*
+ * Symbols created by ___ADDRESSABLE() are only used to convince the toolchain
+ * not to optimize out the referenced symbol.
+ */
+static bool is_addressable_sym(struct symbol *sym)
+{
+ return !strcmp(sym->sec->name, ".discard.addressable");
+}
+
/*
* These symbols should never be correlated, so their local patched versions
* are used instead of linking to the originals.
@@ -365,6 +374,7 @@ static bool dont_correlate(struct symbol *sym)
is_uncorrelated_static_local(sym) ||
is_clang_tmp_label(sym) ||
is_string_sec(sym->sec) ||
+ is_addressable_sym(sym) ||
is_special_section(sym->sec) ||
is_special_section_aux(sym->sec) ||
strstarts(sym->name, "__initcall__");
--
2.53.0
^ permalink raw reply related
* [PATCH v2 02/53] objtool/klp: Fix .data..once static local non-correlation
From: Josh Poimboeuf @ 2026-05-01 4:07 UTC (permalink / raw)
To: x86
Cc: linux-kernel, live-patching, Peter Zijlstra, Joe Lawrence,
Song Liu, Miroslav Benes, Petr Mladek
In-Reply-To: <cover.1777575752.git.jpoimboe@kernel.org>
While there was once a section named .data.once, it has since been
renamed to .data..once with commit dbefa1f31a91 ("Rename .data.once to
.data..once to fix resetting WARN*_ONCE"). Fix it.
Fixes: dd590d4d57eb ("objtool/klp: Introduce klp diff subcommand for diffing object files")
Acked-by: Song Liu <song@kernel.org>
Reviewed-by: Miroslav Benes <mbenes@suse.cz>
Signed-off-by: Josh Poimboeuf <jpoimboe@kernel.org>
---
tools/objtool/klp-diff.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/tools/objtool/klp-diff.c b/tools/objtool/klp-diff.c
index b1b068e9b4c7..cb26c1c92a74 100644
--- a/tools/objtool/klp-diff.c
+++ b/tools/objtool/klp-diff.c
@@ -257,7 +257,8 @@ static bool is_uncorrelated_static_local(struct symbol *sym)
if (!is_object_sym(sym) || !is_local_sym(sym))
return false;
- if (!strcmp(sym->sec->name, ".data.once"))
+ /* WARN_ONCE, etc */
+ if (!strcmp(sym->sec->name, ".data..once"))
return true;
dot = strchr(sym->name, '.');
--
2.53.0
^ permalink raw reply related
* [PATCH v2 01/53] objtool/klp: Fix is_uncorrelated_static_local() for Clang
From: Josh Poimboeuf @ 2026-05-01 4:07 UTC (permalink / raw)
To: x86
Cc: linux-kernel, live-patching, Peter Zijlstra, Joe Lawrence,
Song Liu, Miroslav Benes, Petr Mladek
In-Reply-To: <cover.1777575752.git.jpoimboe@kernel.org>
From: Joe Lawrence <joe.lawrence@redhat.com>
For naming function-local static locals, GCC uses <var>.<id>, e.g.
__already_done.15, while Clang uses <func>.<var> with optional .<id>,
e.g. create_worker.__already_done.111
The existing is_uncorrelated_static_local() check only matches the GCC
convention where the variable name is a prefix. Handle both cases by
checking for a prefix match (GCC) and by checking after the first dot
separator (Clang).
Fixes: dd590d4d57eb ("objtool/klp: Introduce klp diff subcommand for diffing object files")
Signed-off-by: Joe Lawrence <joe.lawrence@redhat.com>
Acked-by: Song Liu <song@kernel.org>
Reviewed-by: Miroslav Benes <mbenes@suse.cz>
Signed-off-by: Josh Poimboeuf <jpoimboe@kernel.org>
---
tools/objtool/klp-diff.c | 33 +++++++++++++++++++++++----------
1 file changed, 23 insertions(+), 10 deletions(-)
diff --git a/tools/objtool/klp-diff.c b/tools/objtool/klp-diff.c
index 0b0d1503851f..b1b068e9b4c7 100644
--- a/tools/objtool/klp-diff.c
+++ b/tools/objtool/klp-diff.c
@@ -242,16 +242,17 @@ static struct symbol *next_file_symbol(struct elf *elf, struct symbol *sym)
static bool is_uncorrelated_static_local(struct symbol *sym)
{
static const char * const vars[] = {
- "__already_done.",
- "__func__.",
- "__key.",
- "__warned.",
- "_entry.",
- "_entry_ptr.",
- "_rs.",
- "descriptor.",
- "CSWTCH.",
+ "__already_done",
+ "__func__",
+ "__key",
+ "__warned",
+ "_entry",
+ "_entry_ptr",
+ "_rs",
+ "descriptor",
+ "CSWTCH",
};
+ const char *dot;
if (!is_object_sym(sym) || !is_local_sym(sym))
return false;
@@ -259,8 +260,20 @@ static bool is_uncorrelated_static_local(struct symbol *sym)
if (!strcmp(sym->sec->name, ".data.once"))
return true;
+ dot = strchr(sym->name, '.');
+ if (!dot)
+ return false;
+
for (int i = 0; i < ARRAY_SIZE(vars); i++) {
- if (strstarts(sym->name, vars[i]))
+ size_t len = strlen(vars[i]);
+
+ /* GCC: <var>.<id> */
+ if (strstarts(sym->name, vars[i]) && (sym->name[len] == '.'))
+ return true;
+
+ /* Clang: <func>.<var>[.<id>] */
+ if (strstarts(dot + 1, vars[i]) &&
+ (dot[1 + len] == '.' || dot[1 + len] == '\0'))
return true;
}
--
2.53.0
^ permalink raw reply related
* [PATCH v2 00/53] objtool/klp: Some klp-build fixes and improvements
From: Josh Poimboeuf @ 2026-05-01 4:07 UTC (permalink / raw)
To: x86
Cc: linux-kernel, live-patching, Peter Zijlstra, Joe Lawrence,
Song Liu, Miroslav Benes, Petr Mladek
Changes since v1 (https://lore.kernel.org/cover.1776916871.git.jpoimboe@kernel.org):
- Add comment for find_reloc_by_dest_range() first-match behavior
[Peter]
- Simplify is_cold_func() [Peter]
- Grow __cfi_ symbols [Peter]
- Rename "Ignore __UNIQUE_ID_*() PCI stub functions" to more general
"Don't report uncorrelated functions as new" [Song]
- Move rodata non-correlation into pointer-comparison fix [Miroslav]
- Add comments for convert_reloc_sym() return values [Song]
- Remove redundant SRC/OBJ variables [Song]
- Use "if (mismatch) {} else" in for_each_sym_by_*() [Song]
- Flatten nested if-else chain in short-circuit validation [Song]
- Add comments with examples to symbol correlation algorithm [Song]
- Move callback refactor to earlier in the patch set [Miroslav]
- Fix reloc corruption in convert_reloc_sym_to_secsym() [Sashiko]
- Include offset in object checksum hashing [Sashiko]
- Fix klp-build checksum comparison output for added/removed
instructions [Sashiko]
- Fix kCFI prefix finding/cloning
- Add reloc symbol conversion simplification cleanup
- Improve local label check for uncorrelated symbols
- Drop "Make function prefix handling more generic" for now (refactored
version will come with arm64 patches)
- Refactor inline alternative cloning into separate
clone_inline_alternatives()
- Add Acked-by/Reviewed-by tags
---
While working on the (upcoming) arm64 support, I ended up shaking out a
lot of bugs by tested several patches on a variety of configs (distro,
LTO, FineIBT, kCFI, etc).
While arm64 support seems to be working well, I decided to leave those
patches out of this set to try to keep the number of patches
"reasonable".
And these stand alone as nice improvements for x86 anyway.
Full arm64 support (this set + more) can be found here:
git://git.kernel.org/pub/scm/linux/kernel/git/jpoimboe/linux.git klp-build-arm64
Joe Lawrence (2):
objtool/klp: Fix is_uncorrelated_static_local() for Clang
objtool/klp: Fix create_fake_symbols() skipping entsize-based sections
Josh Poimboeuf (51):
objtool/klp: Fix .data..once static local non-correlation
objtool/klp: Don't correlate __ADDRESSABLE() symbols
objtool/klp: Don't correlate absolute symbols
objtool/klp: Don't correlate __initstub__ symbols
objtool/klp: Don't report uncorrelated functions as new
objtool/klp: Improve local label check
objtool: Replace iterator callback with for_each_sym_by_mangled_name()
objtool/klp: Fix --debug-checksum for duplicate symbol names
objtool/klp: Fix handling of zero-length .altinstr_replacement
sections
objtool/klp: Fix cloning of zero-length section symbols
objtool/klp: Fix XXH3 state memory leak
objtool/klp: Fix extraction of text annotations for alternatives
objtool/klp: Fix kCFI trap handling
objtool/klp: Fix relocation conversion failures for R_X86_64_NONE
objtool: Move mark_rodata() to elf.c
objtool/klp: Simplify reloc symbol conversion
objtool/klp: Fix pointer comparisons for rodata objects
objtool/klp: Don't correlate .rodata.cst* constant pool objects
objtool/klp: Fix reloc corruption in convert_reloc_sym_to_secsym()
objtool: Fix reloc hash collision in find_reloc_by_dest_range()
klp-build: Fix hang on out-of-date .config
klp-build: Fix checksum comparison for changed offsets
klp-build: Don't use errexit
klp-build: Validate patch file existence
klp-build: Suppress excessive fuzz output by default
klp-build: Fix patch cleanup on interrupt
klp-build: Reject patches to vDSO
klp-build: Reject patches to realmode
klp-build: Print "objtool klp diff" command in verbose mode
klp-build: Remove redundant SRC and OBJ variables
objtool/klp: Don't set sym->file for section symbols
objtool: Include libsubcmd headers directly from source tree
objtool/klp: Create empty checksum sections for function-less object
files
objtool/klp: Handle Clang .data..Lanon anonymous data sections
objtool: Add is_alias_sym() helper
objtool: Add is_cold_func() helper
objtool/klp: Extricate checksum calculation from validate_branch()
objtool: Consolidate file decoding into decode_file()
objtool/klp: Add "objtool klp checksum" subcommand
klp-build: Use "objtool klp checksum" subcommand
objtool/klp: Remove "objtool --checksum"
klp-build: Validate short-circuit prerequisites
objtool/klp: Calculate object checksums
objtool/klp: Rewrite symbol correlation algorithm
objtool/klp: Add correlation debugging output
objtool: Add insn_sym() helper
objtool/klp: Fix position-dependent checksums for non-relocated
jumps/calls
objtool: Grow __cfi_* prefix symbols for all CFI+CALL_PADDING
objtool/klp: Fix kCFI prefix finding/cloning
objtool: Improve and simplify prefix symbol detection
objtool/klp: Cache dont_correlate() result
arch/x86/Kconfig | 4 -
lib/Kconfig.debug | 2 +-
scripts/Makefile.lib | 7 +-
scripts/livepatch/klp-build | 250 ++++---
tools/objtool/Build | 2 +-
tools/objtool/Makefile | 4 +-
tools/objtool/arch/x86/decode.c | 17 +-
tools/objtool/builtin-check.c | 26 +-
tools/objtool/builtin-klp.c | 1 +
tools/objtool/check.c | 412 ++++-------
tools/objtool/disas.c | 22 +-
tools/objtool/elf.c | 124 ++--
tools/objtool/include/objtool/arch.h | 3 +
tools/objtool/include/objtool/builtin.h | 7 +-
tools/objtool/include/objtool/check.h | 34 +-
tools/objtool/include/objtool/checksum.h | 53 +-
tools/objtool/include/objtool/elf.h | 59 +-
tools/objtool/include/objtool/klp.h | 1 +
tools/objtool/include/objtool/warn.h | 57 +-
tools/objtool/klp-checksum.c | 347 ++++++++++
tools/objtool/klp-diff.c | 826 ++++++++++++++++-------
tools/objtool/objtool.c | 3 -
tools/objtool/trace.c | 8 +-
23 files changed, 1493 insertions(+), 776 deletions(-)
create mode 100644 tools/objtool/klp-checksum.c
--
2.53.0
^ permalink raw reply
* Re: [PATCH 41/48] objtool/klp: Rewrite symbol correlation algorithm
From: Song Liu @ 2026-04-30 15:11 UTC (permalink / raw)
To: Josh Poimboeuf
Cc: x86, linux-kernel, live-patching, Peter Zijlstra, Joe Lawrence,
Miroslav Benes, Petr Mladek
In-Reply-To: <z5njxpewtwl4m3drovmqykouqmwon3klnsvs6ypa63jym7t2ic@nonc2g73yvs3>
On Thu, Apr 30, 2026 at 3:53 PM Josh Poimboeuf <jpoimboe@kernel.org> wrote:
>
> On Tue, Apr 28, 2026 at 09:50:46PM +0100, Song Liu wrote:
> > On Tue, Apr 28, 2026 at 5:23 PM Josh Poimboeuf <jpoimboe@kernel.org> wrote:
> > [...]
> > > > Also a few nitpicks below.
> > > >
> > > > > Signed-off-by: Josh Poimboeuf <jpoimboe@kernel.org>
> > > > > ---
> > > > [...]
> > > > > +static struct symbol *find_twin(struct elfs *e, struct symbol *sym1)
> > > > > +{
> > > > > + struct symbol *name_last = NULL, *scope_last = NULL,
> > > > > + *file_last = NULL, *csum_last = NULL;
> > > > > + unsigned int name_orig = 0, name_patched = 0;
> > > > > + unsigned int scope_orig = 0, scope_patched = 0;
> > > > > + unsigned int file_orig = 0, file_patched = 0;
> > > > > + unsigned int csum_orig = 0, csum_patched = 0;
> > > > > + struct symbol *sym2, *match = NULL;
> > > > > +
> > > > > + /* Count orig candidates */
> > > > > + for_each_sym_by_demangled_name(e->orig, sym1->demangled_name, sym2) {
> > > > > + if (sym2->twin || sym1->type != sym2->type || dont_correlate(sym2) ||
> > > > > + (!maybe_same_file(sym1, sym2)))
> > > > > continue;
> > > > >
> > > > > - count++;
> > > > > - result = sym2;
> > > > > + /* Level 1: name match (widest filter) */
> > > > > + name_orig++;
> > > > > +
> > > > > + /* Level 2: scope (scope changes allowed) */
> > > > > + if (is_tu_local_sym(sym1) != is_tu_local_sym(sym2))
> > > >
> > > > is_tu_local_sym(sym1) is called many times, shall we add a variable
> > > > for it?
> > >
> > > Unless it's actually a performance issue I'd prefer not to add yet
> > > another bit to struct symbol.
> >
> > We don't need to add it to struct symbol, a local variable for sym1
> > will be good. No need for sym2.
> >
> > IOW, we have something like
> >
> > bool sym1_is_local = is_tu_local_sym(sym1);
> > ...
> > if (sym1_is_local != is_tu_local_sym(sym2))
>
> I'd rather keep it the way it is for readability, the compiler should be
> able to recognize the sym1 value doesn't change and calculate its value
> once.
Agreed for readability.
We can always optimize it in case this becomes a real slowdown.
Thanks,
Song
^ permalink raw reply
* Re: [PATCH 41/48] objtool/klp: Rewrite symbol correlation algorithm
From: Josh Poimboeuf @ 2026-04-30 14:53 UTC (permalink / raw)
To: Song Liu
Cc: x86, linux-kernel, live-patching, Peter Zijlstra, Joe Lawrence,
Miroslav Benes, Petr Mladek
In-Reply-To: <CAPhsuW5H47o59-Np_Uj1xP5V5wFj7KeVEaiDUoTGki=uxrbGDQ@mail.gmail.com>
On Tue, Apr 28, 2026 at 09:50:46PM +0100, Song Liu wrote:
> On Tue, Apr 28, 2026 at 5:23 PM Josh Poimboeuf <jpoimboe@kernel.org> wrote:
> [...]
> > > Also a few nitpicks below.
> > >
> > > > Signed-off-by: Josh Poimboeuf <jpoimboe@kernel.org>
> > > > ---
> > > [...]
> > > > +static struct symbol *find_twin(struct elfs *e, struct symbol *sym1)
> > > > +{
> > > > + struct symbol *name_last = NULL, *scope_last = NULL,
> > > > + *file_last = NULL, *csum_last = NULL;
> > > > + unsigned int name_orig = 0, name_patched = 0;
> > > > + unsigned int scope_orig = 0, scope_patched = 0;
> > > > + unsigned int file_orig = 0, file_patched = 0;
> > > > + unsigned int csum_orig = 0, csum_patched = 0;
> > > > + struct symbol *sym2, *match = NULL;
> > > > +
> > > > + /* Count orig candidates */
> > > > + for_each_sym_by_demangled_name(e->orig, sym1->demangled_name, sym2) {
> > > > + if (sym2->twin || sym1->type != sym2->type || dont_correlate(sym2) ||
> > > > + (!maybe_same_file(sym1, sym2)))
> > > > continue;
> > > >
> > > > - count++;
> > > > - result = sym2;
> > > > + /* Level 1: name match (widest filter) */
> > > > + name_orig++;
> > > > +
> > > > + /* Level 2: scope (scope changes allowed) */
> > > > + if (is_tu_local_sym(sym1) != is_tu_local_sym(sym2))
> > >
> > > is_tu_local_sym(sym1) is called many times, shall we add a variable
> > > for it?
> >
> > Unless it's actually a performance issue I'd prefer not to add yet
> > another bit to struct symbol.
>
> We don't need to add it to struct symbol, a local variable for sym1
> will be good. No need for sym2.
>
> IOW, we have something like
>
> bool sym1_is_local = is_tu_local_sym(sym1);
> ...
> if (sym1_is_local != is_tu_local_sym(sym2))
I'd rather keep it the way it is for readability, the compiler should be
able to recognize the sym1 value doesn't change and calculate its value
once.
--
Josh
^ permalink raw reply
* Re: [PATCH v5 0/8] unwind, arm64: add sframe unwinder for kernel
From: Jens Remus @ 2026-04-30 10:11 UTC (permalink / raw)
To: Dylan Hatch, Roman Gushchin, Weinan Liu, Will Deacon,
Josh Poimboeuf, Indu Bhagat, Peter Zijlstra, Steven Rostedt,
Catalin Marinas, Jiri Kosina
Cc: 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: <20260428183643.3796063-1-dylanbhatch@google.com>
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.
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)"?
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/
^ permalink raw reply
* Re: [PATCH v5 6/8] arm64/module, sframe: Add sframe support for modules
From: Jens Remus @ 2026-04-30 10:04 UTC (permalink / raw)
To: Dylan Hatch, Roman Gushchin, Weinan Liu, Will Deacon,
Josh Poimboeuf, Indu Bhagat, Peter Zijlstra, Steven Rostedt,
Catalin Marinas, Jiri Kosina
Cc: 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: <20260428183643.3796063-7-dylanbhatch@google.com>
On 4/28/2026 8:36 PM, Dylan Hatch wrote:
> Add sframe table to mod_arch_specific and support sframe PC lookups when
> an .sframe section can be found on incoming modules.
One small fix and a proposal to sort the module's SFrame FDE index.
> diff --git a/kernel/unwind/sframe.c b/kernel/unwind/sframe.c
A subsequent patch adds a call to sframe_validate_section(), which would
operate on the temporary struct sframe_section instance and thus fail
to use container_of() to access the struct module instance. To resolve
change as follows:
> +void sframe_module_init(struct module *mod, void *sframe, size_t sframe_size,
> + void *text, size_t text_size)
> +{
> + struct sframe_section sec;
struct sframe_section *sec = &mod->arch.sframe_sec;
It is fine to initialize the module's struct sframe_section instance as
use of the information is guarded by mod->arch.sframe_init, which is
only set if the instance has been full initialized.
> +
> + memset(&sec, 0, sizeof(sec));
Can be dropped if struct module instance got zero-initialized.
> + sec.sec_type = SFRAME_KERNEL;
> + sec.sframe_start = (unsigned long)sframe;
> + sec.sframe_end = (unsigned long)sframe + sframe_size;
> + sec.text_start = (unsigned long)text;
> + sec.text_end = (unsigned long)text + text_size;
Adjust all lines above to pointer access.
> +
> + if (WARN_ON(sframe_read_header(&sec)))
Ditto.
> + return;
> +
> + mod->arch.sframe_sec = sec;
Drop.
> + mod->arch.sframe_init = true;
> +}
Indu suggested that it would be preferable if a module's .sframe FDE
index table could be sorted during loading of the module to enable
binary search instead of having to resort to linear search. I propose
to change this patch as follows to sort the module .sframe FDE index
table in sframe_module_init(). Note that the patch assumes above
changes have been implemented. The sorting is very similar to sorting
of ORC tables in arch/x86/kernel/unwind_orc.c in unwind_module_init().
diff --git a/kernel/unwind/sframe.c b/kernel/unwind/sframe.c
--- a/kernel/unwind/sframe.c
+++ b/kernel/unwind/sframe.c
@@ -12,6 +12,7 @@
#include <linux/mm.h>
#include <linux/string_helpers.h>
#include <linux/sframe.h>
+#include <linux/sort.h>
#include <linux/unwind_types.h>
#include <asm/unwind_sframe.h>
#ifdef CONFIG_HAVE_UNWIND_KERNEL_SFRAME
@@ -1038,6 +1039,50 @@ void __init init_sframe_table(void)
sframe_init = true;
}
+static int sframe_sort_cmp_fde(const void *a, const void *b)
+{
+ const struct sframe_fde_v3 *fde_a = a, *fde_b = b;
+ unsigned long func_start_a, func_start_b;
+
+ func_start_a = (unsigned long)fde_a + fde_a->func_start_off;
+ func_start_b = (unsigned long)fde_b + fde_b->func_start_off;
+
+ return cmp_int(func_start_a, func_start_b);
+}
+
+static void sframe_sort_swap_fde(void *a, void *b, int size)
+{
+ struct sframe_fde_v3 *fde_a = a, *fde_b = b;
+ struct sframe_fde_v3 temp;
+ long delta;
+
+ /* Swap potentially unaligned FDE */
+ memcpy(&temp, fde_a, sizeof(struct sframe_fde_v3));
+ memcpy(fde_a, fde_b, sizeof(struct sframe_fde_v3));
+ memcpy(fde_b, &temp, sizeof(struct sframe_fde_v3));
+
+ /* Adjust FDE function start offset from FDE */
+ delta = (long)((unsigned long)fde_b - (unsigned long)fde_a);
+ fde_a->func_start_off += delta;
+ fde_b->func_start_off -= delta;
+}
+
+static int sframe_sort_fdes(struct sframe_section *sec)
+{
+ void *fdes = (void *)sec->fdes_start;
+ size_t num_fdes = sec->num_fdes;
+
+ if (sec->sec_type != SFRAME_KERNEL)
+ return -EINVAL;
+ if (sec->fdes_sorted)
+ return 0;
+
+ sort(fdes, num_fdes, sizeof(struct sframe_fde_v3),
+ sframe_sort_cmp_fde, sframe_sort_swap_fde);
+ sec->fdes_sorted = true;
+ return 0;
+}
+
void sframe_module_init(struct module *mod, void *sframe, size_t sframe_size,
void *text, size_t text_size)
{
@@ -1053,6 +1098,8 @@ void sframe_module_init(struct module *mod, void *sframe, size_t sframe_size,
if (WARN_ON(sframe_read_header(sec)))
return;
+ if (WARN_ON(sframe_sort_fdes(sec)))
+ return;
if (WARN_ON(sframe_validate_section(sec)))
return;
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