objtool/klp: Match symbols based on demangled_name for global variables

correlate_symbols() will always try to match full name first. If there is
no match, try match only demangled_name.

In very rare cases, it is possible to have multiple foo.llvm.<hash> in
the same kernel. Whenever there is ambiguity like this, fail the klp diff.

Signed-off-by: Song Liu <song@kernel.org>
Link: https://patch.msgid.link/20260305231531.3847295-7-song@kernel.org
Signed-off-by: Josh Poimboeuf <jpoimboe@kernel.org>
This commit is contained in:
Song Liu
2026-03-05 15:15:30 -08:00
committed by Josh Poimboeuf
parent 020b71dcaf
commit cdea5cadb0
3 changed files with 73 additions and 0 deletions

View File

@@ -323,6 +323,19 @@ 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)
{

View File

@@ -186,6 +186,9 @@ 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);

View File

@@ -355,6 +355,46 @@ static bool dont_correlate(struct symbol *sym)
strstarts(sym->name, "__initcall__");
}
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.
*
* Note that, in very rare cases, it is possible to have multiple
* foo.llvm.<hash> in the same kernel. When this happens, report error and
* fail the diff.
*/
static int find_global_symbol_by_demangled_name(struct elf *elf, struct symbol *sym,
struct symbol **out_sym)
{
struct process_demangled_name_data data = {};
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);
return -1;
}
*out_sym = data.ret;
return 0;
}
/*
* For each symbol in the original kernel, find its corresponding "twin" in the
* patched kernel.
@@ -453,6 +493,23 @@ static int correlate_symbols(struct elfs *e)
continue;
sym2 = find_global_symbol_by_name(e->patched, sym1->name);
if (sym2 && !sym2->twin) {
sym1->twin = sym2;
sym2->twin = sym1;
}
}
/*
* Correlate globals with demangled_name.
* A separate loop is needed because we want to finish all the
* full name correlations first.
*/
for_each_sym(e->orig, sym1) {
if (sym1->bind == STB_LOCAL || sym1->twin)
continue;
if (find_global_symbol_by_demangled_name(e->patched, sym1, &sym2))
return -1;
if (sym2 && !sym2->twin) {
sym1->twin = sym2;