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 /tools/objtool | |
| 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>
Diffstat (limited to 'tools/objtool')
| -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; |
