aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJosh Poimboeuf <jpoimboe@redhat.com>2018-05-18 16:10:34 -0400
committerIngo Molnar <mingo@kernel.org>2018-05-19 02:10:04 -0400
commit7dec80ccbe310fb7e225bf21c48c672bb780ce7b (patch)
treef3347e1337b03e89b27c6db8e1c5fe0b84e8171b
parent6f5ec2993b1f39aed12fa6fd56e8dc2272ee8a33 (diff)
objtool: Detect RIP-relative switch table references, part 2
With the following commit: fd35c88b7417 ("objtool: Support GCC 8 switch tables") I added a "can't find switch jump table" warning, to stop covering up silent failures if add_switch_table() can't find anything. That warning found yet another bug in the objtool switch table detection logic. For cases 1 and 2 (as described in the comments of find_switch_table()), the find_symbol_containing() check doesn't adjust the offset for RIP-relative switch jumps. Incidentally, this bug was already fixed for case 3 with: 6f5ec2993b1f ("objtool: Detect RIP-relative switch table references") However, that commit missed the fix for cases 1 and 2. The different cases are now starting to look more and more alike. So fix the bug by consolidating them into a single case, by checking the original dynamic jump instruction in the case 3 loop. This also simplifies the code and makes it more robust against future switch table detection issues -- of which I'm sure there will be many... Switch table detection has been the most fragile area of objtool, by far. I long for the day when we'll have a GCC plugin for annotating switch tables. Linus asked me to delay such a plugin due to the flakiness of the plugin infrastructure in older versions of GCC, so this rickety code is what we're stuck with for now. At least the code is now a little simpler than it was. Reported-by: kbuild test robot <lkp@intel.com> 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/f400541613d45689086329432f3095119ffbc328.1526674218.git.jpoimboe@redhat.com Signed-off-by: Ingo Molnar <mingo@kernel.org>
-rw-r--r--tools/objtool/check.c37
1 files changed, 12 insertions, 25 deletions
diff --git a/tools/objtool/check.c b/tools/objtool/check.c
index f4bbce838433..3a31b238f885 100644
--- a/tools/objtool/check.c
+++ b/tools/objtool/check.c
@@ -905,40 +905,19 @@ static struct rela *find_switch_table(struct objtool_file *file,
905 struct instruction *orig_insn = insn; 905 struct instruction *orig_insn = insn;
906 unsigned long table_offset; 906 unsigned long table_offset;
907 907
908 /* case 1 & 2 */
909 text_rela = find_rela_by_dest_range(insn->sec, insn->offset, insn->len);
910 if (text_rela && text_rela->sym == file->rodata->sym &&
911 !find_symbol_containing(file->rodata, text_rela->addend)) {
912
913 table_offset = text_rela->addend;
914 if (text_rela->type == R_X86_64_PC32) {
915 /* case 2 */
916 table_offset += 4;
917 file->ignore_unreachables = true;
918 }
919
920 rodata_rela = find_rela_by_dest(file->rodata, table_offset);
921 if (!rodata_rela)
922 return NULL;
923
924 return rodata_rela;
925 }
926
927 /* case 3 */
928 /* 908 /*
929 * Backward search using the @first_jump_src links, these help avoid 909 * Backward search using the @first_jump_src links, these help avoid
930 * much of the 'in between' code. Which avoids us getting confused by 910 * much of the 'in between' code. Which avoids us getting confused by
931 * it. 911 * it.
932 */ 912 */
933 for (insn = list_prev_entry(insn, list); 913 for (;
934
935 &insn->list != &file->insn_list && 914 &insn->list != &file->insn_list &&
936 insn->sec == func->sec && 915 insn->sec == func->sec &&
937 insn->offset >= func->offset; 916 insn->offset >= func->offset;
938 917
939 insn = insn->first_jump_src ?: list_prev_entry(insn, list)) { 918 insn = insn->first_jump_src ?: list_prev_entry(insn, list)) {
940 919
941 if (insn->type == INSN_JUMP_DYNAMIC) 920 if (insn != orig_insn && insn->type == INSN_JUMP_DYNAMIC)
942 break; 921 break;
943 922
944 /* allow small jumps within the range */ 923 /* allow small jumps within the range */
@@ -965,10 +944,18 @@ static struct rela *find_switch_table(struct objtool_file *file,
965 if (find_symbol_containing(file->rodata, table_offset)) 944 if (find_symbol_containing(file->rodata, table_offset))
966 continue; 945 continue;
967 946
968 /* mov [rodata addr], %reg */
969 rodata_rela = find_rela_by_dest(file->rodata, table_offset); 947 rodata_rela = find_rela_by_dest(file->rodata, table_offset);
970 if (rodata_rela) 948 if (rodata_rela) {
949 /*
950 * Use of RIP-relative switch jumps is quite rare, and
951 * indicates a rare GCC quirk/bug which can leave dead
952 * code behind.
953 */
954 if (text_rela->type == R_X86_64_PC32)
955 file->ignore_unreachables = true;
956
971 return rodata_rela; 957 return rodata_rela;
958 }
972 } 959 }
973 960
974 return NULL; 961 return NULL;