objtool/klp: Fix pointer comparisons for rodata objects

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.

Acked-by: Song Liu <song@kernel.org>
Signed-off-by: Josh Poimboeuf <jpoimboe@kernel.org>
This commit is contained in:
Josh Poimboeuf 2026-04-28 14:34:23 -07:00
parent 8fdc3585b3
commit d5b0f02528
3 changed files with 28 additions and 2 deletions

View File

@ -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.
*/

View File

@ -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,

View File

@ -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 */
}