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 <[email protected]>
---
 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


Reply via email to