diff options
author | Josh Poimboeuf <jpoimboe@redhat.com> | 2018-05-14 09:53:24 -0400 |
---|---|---|
committer | Ingo Molnar <mingo@kernel.org> | 2018-05-15 01:30:59 -0400 |
commit | 6f5ec2993b1f39aed12fa6fd56e8dc2272ee8a33 (patch) | |
tree | 9069cef5fd4a462096ba7a7c7b82b2a494dab094 | |
parent | fd35c88b74170d9335530d9abf271d5d73eb5401 (diff) |
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: Randy Dunlap <rdunlap@infradead.org>
Signed-off-by: Josh 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.com
Signed-off-by: Ingo Molnar <mingo@kernel.org>
-rw-r--r-- | tools/objtool/check.c | 33 |
1 files changed, 18 insertions, 15 deletions
diff --git a/tools/objtool/check.c b/tools/objtool/check.c index 9bb04fddd3c8..f4bbce838433 100644 --- a/tools/objtool/check.c +++ b/tools/objtool/check.c | |||
@@ -903,24 +903,24 @@ static struct rela *find_switch_table(struct objtool_file *file, | |||
903 | { | 903 | { |
904 | struct rela *text_rela, *rodata_rela; | 904 | struct rela *text_rela, *rodata_rela; |
905 | struct instruction *orig_insn = insn; | 905 | struct instruction *orig_insn = insn; |
906 | unsigned long table_offset; | ||
906 | 907 | ||
908 | /* case 1 & 2 */ | ||
907 | text_rela = find_rela_by_dest_range(insn->sec, insn->offset, insn->len); | 909 | text_rela = find_rela_by_dest_range(insn->sec, insn->offset, insn->len); |
908 | if (text_rela && text_rela->sym == file->rodata->sym && | 910 | if (text_rela && text_rela->sym == file->rodata->sym && |
909 | !find_symbol_containing(file->rodata, text_rela->addend)) { | 911 | !find_symbol_containing(file->rodata, text_rela->addend)) { |
910 | 912 | ||
911 | /* case 1 */ | 913 | table_offset = text_rela->addend; |
912 | rodata_rela = find_rela_by_dest(file->rodata, | 914 | if (text_rela->type == R_X86_64_PC32) { |
913 | text_rela->addend); | 915 | /* case 2 */ |
914 | if (rodata_rela) | 916 | table_offset += 4; |
915 | return rodata_rela; | 917 | file->ignore_unreachables = true; |
918 | } | ||
916 | 919 | ||
917 | /* case 2 */ | 920 | rodata_rela = find_rela_by_dest(file->rodata, table_offset); |
918 | rodata_rela = find_rela_by_dest(file->rodata, | ||
919 | text_rela->addend + 4); | ||
920 | if (!rodata_rela) | 921 | if (!rodata_rela) |
921 | return NULL; | 922 | return NULL; |
922 | 923 | ||
923 | file->ignore_unreachables = true; | ||
924 | return rodata_rela; | 924 | return rodata_rela; |
925 | } | 925 | } |
926 | 926 | ||
@@ -954,18 +954,21 @@ static struct rela *find_switch_table(struct objtool_file *file, | |||
954 | if (!text_rela || text_rela->sym != file->rodata->sym) | 954 | if (!text_rela || text_rela->sym != file->rodata->sym) |
955 | continue; | 955 | continue; |
956 | 956 | ||
957 | table_offset = text_rela->addend; | ||
958 | if (text_rela->type == R_X86_64_PC32) | ||
959 | table_offset += 4; | ||
960 | |||
957 | /* | 961 | /* |
958 | * Make sure the .rodata address isn't associated with a | 962 | * Make sure the .rodata address isn't associated with a |
959 | * symbol. gcc jump tables are anonymous data. | 963 | * symbol. gcc jump tables are anonymous data. |
960 | */ | 964 | */ |
961 | if (find_symbol_containing(file->rodata, text_rela->addend)) | 965 | if (find_symbol_containing(file->rodata, table_offset)) |
962 | continue; | ||
963 | |||
964 | rodata_rela = find_rela_by_dest(file->rodata, text_rela->addend); | ||
965 | if (!rodata_rela) | ||
966 | continue; | 966 | continue; |
967 | 967 | ||
968 | return rodata_rela; | 968 | /* mov [rodata addr], %reg */ |
969 | rodata_rela = find_rela_by_dest(file->rodata, table_offset); | ||
970 | if (rodata_rela) | ||
971 | return rodata_rela; | ||
969 | } | 972 | } |
970 | 973 | ||
971 | return NULL; | 974 | return NULL; |