Commit 6f5ec299 authored by Josh Poimboeuf's avatar Josh Poimboeuf Committed by Ingo Molnar

objtool: Detect RIP-relative switch table references

Typically a switch table can be found by detecting a .rodata access
followed an indirect jump:

    1969:	4a 8b 0c e5 00 00 00 	mov    0x0(,%r12,8),%rcx
    1970:	00
			196d: R_X86_64_32S	.rodata+0x438
    1971:	e9 00 00 00 00       	jmpq   1976 <dispc_runtime_suspend+0xb6a>
			1972: R_X86_64_PC32	__x86_indirect_thunk_rcx-0x4

Randy Dunlap reported a case (seen with GCC 4.8) where the .rodata
access uses RIP-relative addressing:

    19bd:	48 8b 3d 00 00 00 00 	mov    0x0(%rip),%rdi        # 19c4 <dispc_runtime_suspend+0xbb8>
			19c0: R_X86_64_PC32	.rodata+0x45c
    19c4:	e9 00 00 00 00       	jmpq   19c9 <dispc_runtime_suspend+0xbbd>
			19c5: R_X86_64_PC32	__x86_indirect_thunk_rdi-0x4

In this case the relocation addend needs to be adjusted accordingly in
order to find the location of the switch table.

The fix is for case 3 (as described in the comments), but also make the
existing case 1 & 2 checks more precise by only adjusting the addend for
R_X86_64_PC32 relocations.

This fixes the following warnings:

  drivers/video/fbdev/omap2/omapfb/dss/dispc.o: warning: objtool: dispc_runtime_suspend()+0xbb8: sibling call from callable instruction with modified stack frame
  drivers/video/fbdev/omap2/omapfb/dss/dispc.o: warning: objtool: dispc_runtime_resume()+0xcc5: sibling call from callable instruction with modified stack frame
Reported-by: default avatarRandy Dunlap <rdunlap@infradead.org>
Signed-off-by: default avatarJosh Poimboeuf <jpoimboe@redhat.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Link: http://lkml.kernel.org/r/b6098294fd67afb69af8c47c9883d7a68bf0f8ea.1526305958.git.jpoimboe@redhat.comSigned-off-by: default avatarIngo Molnar <mingo@kernel.org>
parent fd35c88b
...@@ -903,24 +903,24 @@ static struct rela *find_switch_table(struct objtool_file *file, ...@@ -903,24 +903,24 @@ static struct rela *find_switch_table(struct objtool_file *file,
{ {
struct rela *text_rela, *rodata_rela; struct rela *text_rela, *rodata_rela;
struct instruction *orig_insn = insn; struct instruction *orig_insn = insn;
unsigned long table_offset;
/* case 1 & 2 */
text_rela = find_rela_by_dest_range(insn->sec, insn->offset, insn->len); text_rela = find_rela_by_dest_range(insn->sec, insn->offset, insn->len);
if (text_rela && text_rela->sym == file->rodata->sym && if (text_rela && text_rela->sym == file->rodata->sym &&
!find_symbol_containing(file->rodata, text_rela->addend)) { !find_symbol_containing(file->rodata, text_rela->addend)) {
/* case 1 */ table_offset = text_rela->addend;
rodata_rela = find_rela_by_dest(file->rodata, if (text_rela->type == R_X86_64_PC32) {
text_rela->addend);
if (rodata_rela)
return rodata_rela;
/* case 2 */ /* case 2 */
rodata_rela = find_rela_by_dest(file->rodata, table_offset += 4;
text_rela->addend + 4); file->ignore_unreachables = true;
}
rodata_rela = find_rela_by_dest(file->rodata, table_offset);
if (!rodata_rela) if (!rodata_rela)
return NULL; return NULL;
file->ignore_unreachables = true;
return rodata_rela; return rodata_rela;
} }
...@@ -954,17 +954,20 @@ static struct rela *find_switch_table(struct objtool_file *file, ...@@ -954,17 +954,20 @@ static struct rela *find_switch_table(struct objtool_file *file,
if (!text_rela || text_rela->sym != file->rodata->sym) if (!text_rela || text_rela->sym != file->rodata->sym)
continue; continue;
table_offset = text_rela->addend;
if (text_rela->type == R_X86_64_PC32)
table_offset += 4;
/* /*
* Make sure the .rodata address isn't associated with a * Make sure the .rodata address isn't associated with a
* symbol. gcc jump tables are anonymous data. * symbol. gcc jump tables are anonymous data.
*/ */
if (find_symbol_containing(file->rodata, text_rela->addend)) if (find_symbol_containing(file->rodata, table_offset))
continue;
rodata_rela = find_rela_by_dest(file->rodata, text_rela->addend);
if (!rodata_rela)
continue; continue;
/* mov [rodata addr], %reg */
rodata_rela = find_rela_by_dest(file->rodata, table_offset);
if (rodata_rela)
return rodata_rela; return rodata_rela;
} }
......
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