]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
objtool/klp: Match symbols based on demangled_name for global variables
authorSong Liu <song@kernel.org>
Thu, 5 Mar 2026 23:15:30 +0000 (15:15 -0800)
committerJosh Poimboeuf <jpoimboe@kernel.org>
Fri, 6 Mar 2026 16:08:33 +0000 (08:08 -0800)
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>
tools/objtool/elf.c
tools/objtool/include/objtool/elf.h
tools/objtool/klp-diff.c

index feaec45a14a01db392a1c9f7c9c6215a003d54e8..8122c5f5141c546fa673c17d95ecd5a670396837 100644 (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)
 {
index e12c516bd32007948e1445b86faa71110bee7c2d..25573e5af76efe125a04dca8b25e6fc18f1d0859 100644 (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);
index 639bdd3ad5eb942ca58871b1aedd9476f3288d58..46afbf4a03b9543fb834ac85593fee6956936419 100644 (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;