Commit 3e51ccba authored by Josh Poimboeuf's avatar Josh Poimboeuf Committed by Greg Kroah-Hartman

objtool: Fix another GCC jump table detection issue

commit 5c51f4ae upstream.

Arnd Bergmann reported a (false positive) objtool warning:

  drivers/infiniband/sw/rxe/rxe_resp.o: warning: objtool: rxe_responder()+0xfe: sibling call from callable instruction with changed frame pointer

The issue is in find_switch_table().  It tries to find a switch
statement's jump table by walking backwards from an indirect jump
instruction, looking for a relocation to the .rodata section.  In this
case it stopped walking prematurely: the first .rodata relocation it
encountered was for a variable (resp_state_name) instead of a jump
table, so it just assumed there wasn't a jump table.

The fix is to ignore any .rodata relocation which refers to an ELF
object symbol.  This works because the jump tables are anonymous and
have no symbols associated with them.
Reported-by: default avatarArnd Bergmann <arnd@arndb.de>
Tested-by: default avatarArnd Bergmann <arnd@arndb.de>
Signed-off-by: default avatarJosh Poimboeuf <jpoimboe@redhat.com>
Cc: Denys Vlasenko <dvlasenk@redhat.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Fixes: 3732710f ("objtool: Improve rare switch jump table pattern detection")
Link: http://lkml.kernel.org/r/20170302225723.3ndbsnl4hkqbne7a@trebleSigned-off-by: default avatarIngo Molnar <mingo@kernel.org>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 92e66676
...@@ -757,11 +757,20 @@ static struct rela *find_switch_table(struct objtool_file *file, ...@@ -757,11 +757,20 @@ static struct rela *find_switch_table(struct objtool_file *file,
insn->jump_dest->offset > orig_insn->offset)) insn->jump_dest->offset > orig_insn->offset))
break; break;
/* look for a relocation which references .rodata */
text_rela = find_rela_by_dest_range(insn->sec, insn->offset, text_rela = find_rela_by_dest_range(insn->sec, insn->offset,
insn->len); insn->len);
if (text_rela && text_rela->sym == file->rodata->sym) if (!text_rela || text_rela->sym != file->rodata->sym)
return find_rela_by_dest(file->rodata, continue;
text_rela->addend);
/*
* Make sure the .rodata address isn't associated with a
* symbol. gcc jump tables are anonymous data.
*/
if (find_symbol_containing(file->rodata, text_rela->addend))
continue;
return find_rela_by_dest(file->rodata, text_rela->addend);
} }
return NULL; return NULL;
......
...@@ -85,6 +85,18 @@ struct symbol *find_symbol_by_offset(struct section *sec, unsigned long offset) ...@@ -85,6 +85,18 @@ struct symbol *find_symbol_by_offset(struct section *sec, unsigned long offset)
return NULL; return NULL;
} }
struct symbol *find_symbol_containing(struct section *sec, unsigned long offset)
{
struct symbol *sym;
list_for_each_entry(sym, &sec->symbol_list, list)
if (sym->type != STT_SECTION &&
offset >= sym->offset && offset < sym->offset + sym->len)
return sym;
return NULL;
}
struct rela *find_rela_by_dest_range(struct section *sec, unsigned long offset, struct rela *find_rela_by_dest_range(struct section *sec, unsigned long offset,
unsigned int len) unsigned int len)
{ {
......
...@@ -79,6 +79,7 @@ struct elf { ...@@ -79,6 +79,7 @@ struct elf {
struct elf *elf_open(const char *name); struct elf *elf_open(const char *name);
struct section *find_section_by_name(struct elf *elf, const char *name); struct section *find_section_by_name(struct elf *elf, const char *name);
struct symbol *find_symbol_by_offset(struct section *sec, unsigned long offset); struct symbol *find_symbol_by_offset(struct section *sec, unsigned long offset);
struct symbol *find_symbol_containing(struct section *sec, unsigned long offset);
struct rela *find_rela_by_dest(struct section *sec, unsigned long offset); struct rela *find_rela_by_dest(struct section *sec, unsigned long offset);
struct rela *find_rela_by_dest_range(struct section *sec, unsigned long offset, struct rela *find_rela_by_dest_range(struct section *sec, unsigned long offset,
unsigned int len); unsigned int len);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment