| From 5c51f4ae84df0f9df33ac08aa5be50061a8b4242 Mon Sep 17 00:00:00 2001 |
| From: Josh Poimboeuf <jpoimboe@redhat.com> |
| Date: Thu, 2 Mar 2017 16:57:23 -0600 |
| Subject: objtool: Fix another GCC jump table detection issue |
| |
| From: Josh Poimboeuf <jpoimboe@redhat.com> |
| |
| commit 5c51f4ae84df0f9df33ac08aa5be50061a8b4242 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: Arnd Bergmann <arnd@arndb.de> |
| Tested-by: Arnd Bergmann <arnd@arndb.de> |
| Signed-off-by: Josh 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: 3732710ff6f2 ("objtool: Improve rare switch jump table pattern detection") |
| Link: http://lkml.kernel.org/r/20170302225723.3ndbsnl4hkqbne7a@treble |
| Signed-off-by: Ingo Molnar <mingo@kernel.org> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| tools/objtool/builtin-check.c | 15 ++++++++++++--- |
| tools/objtool/elf.c | 12 ++++++++++++ |
| tools/objtool/elf.h | 1 + |
| 3 files changed, 25 insertions(+), 3 deletions(-) |
| |
| --- a/tools/objtool/builtin-check.c |
| +++ b/tools/objtool/builtin-check.c |
| @@ -757,11 +757,20 @@ static struct rela *find_switch_table(st |
| insn->jump_dest->offset > orig_insn->offset)) |
| break; |
| |
| + /* look for a relocation which references .rodata */ |
| text_rela = find_rela_by_dest_range(insn->sec, insn->offset, |
| insn->len); |
| - if (text_rela && text_rela->sym == file->rodata->sym) |
| - return find_rela_by_dest(file->rodata, |
| - text_rela->addend); |
| + if (!text_rela || text_rela->sym != file->rodata->sym) |
| + continue; |
| + |
| + /* |
| + * 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; |
| --- a/tools/objtool/elf.c |
| +++ b/tools/objtool/elf.c |
| @@ -85,6 +85,18 @@ struct symbol *find_symbol_by_offset(str |
| 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, |
| unsigned int len) |
| { |
| --- a/tools/objtool/elf.h |
| +++ b/tools/objtool/elf.h |
| @@ -79,6 +79,7 @@ struct elf { |
| struct elf *elf_open(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_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_range(struct section *sec, unsigned long offset, |
| unsigned int len); |