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 <[email protected]>
---
 tools/objtool/elf.c                 | 13 +++++++
 tools/objtool/include/objtool/elf.h |  3 ++
 tools/objtool/klp-diff.c            | 57 +++++++++++++++++++++++++++++
 3 files changed, 73 insertions(+)

diff --git a/tools/objtool/elf.c b/tools/objtool/elf.c
index efb13ec0a89d..5ddbfa8f8701 100644
--- a/tools/objtool/elf.c
+++ b/tools/objtool/elf.c
@@ -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)
 {
diff --git a/tools/objtool/include/objtool/elf.h 
b/tools/objtool/include/objtool/elf.h
index e12c516bd320..25573e5af76e 100644
--- a/tools/objtool/include/objtool/elf.h
+++ b/tools/objtool/include/objtool/elf.h
@@ -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);
diff --git a/tools/objtool/klp-diff.c b/tools/objtool/klp-diff.c
index 57606bc3390a..92043da0ed0b 100644
--- a/tools/objtool/klp-diff.c
+++ b/tools/objtool/klp-diff.c
@@ -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;
-- 
2.47.3


Reply via email to