summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJosh Poimboeuf <jpoimboe@redhat.com>2017-02-21 16:35:32 -0500
committerIngo Molnar <mingo@kernel.org>2017-02-24 03:10:52 -0500
commitd1091c7fa3d52ebce4dd3f15d04155b3469b2f90 (patch)
treea56b8cc4e12f90c21c26bde0d49996c00c2341ef
parent9f0c18aec620bc9d82268b3cb937568dd07b43ff (diff)
objtool: Improve detection of BUG() and other dead ends
The BUG() macro's use of __builtin_unreachable() via the unreachable() macro tells gcc that the instruction is a dead end, and that it's safe to assume the current code path will not execute past the previous instruction. On x86, the BUG() macro is implemented with the 'ud2' instruction. When objtool's branch analysis sees that instruction, it knows the current code path has come to a dead end. Peter Zijlstra has been working on a patch to change the WARN macros to use 'ud2'. That patch will break objtool's assumption that 'ud2' is always a dead end. Generally it's best for objtool to avoid making those kinds of assumptions anyway. The more ignorant it is of kernel code internals, the better. So create a more generic way for objtool to detect dead ends by adding an annotation to the unreachable() macro. The annotation stores a pointer to the end of the unreachable code path in an '__unreachable' section. Objtool can read that section to find the dead ends. Tested-by: Peter Zijlstra (Intel) <peterz@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/41a6d33971462ebd944a1c60ad4bf5be86c17b77.1487712920.git.jpoimboe@redhat.com Signed-off-by: Ingo Molnar <mingo@kernel.org>
-rw-r--r--arch/x86/kernel/vmlinux.lds.S1
-rw-r--r--include/linux/compiler-gcc.h13
-rw-r--r--tools/objtool/arch.h5
-rw-r--r--tools/objtool/arch/x86/decode.c3
-rw-r--r--tools/objtool/builtin-check.c60
5 files changed, 71 insertions, 11 deletions
diff --git a/arch/x86/kernel/vmlinux.lds.S b/arch/x86/kernel/vmlinux.lds.S
index e79f15f108a8..ad0118fbce90 100644
--- a/arch/x86/kernel/vmlinux.lds.S
+++ b/arch/x86/kernel/vmlinux.lds.S
@@ -346,6 +346,7 @@ SECTIONS
346 /DISCARD/ : { 346 /DISCARD/ : {
347 *(.eh_frame) 347 *(.eh_frame)
348 *(__func_stack_frame_non_standard) 348 *(__func_stack_frame_non_standard)
349 *(__unreachable)
349 } 350 }
350} 351}
351 352
diff --git a/include/linux/compiler-gcc.h b/include/linux/compiler-gcc.h
index 0444b1336268..8ea159fc489d 100644
--- a/include/linux/compiler-gcc.h
+++ b/include/linux/compiler-gcc.h
@@ -195,6 +195,17 @@
195#endif 195#endif
196#endif 196#endif
197 197
198#ifdef CONFIG_STACK_VALIDATION
199#define annotate_unreachable() ({ \
200 asm("1:\t\n" \
201 ".pushsection __unreachable, \"a\"\t\n" \
202 ".long 1b\t\n" \
203 ".popsection\t\n"); \
204})
205#else
206#define annotate_unreachable()
207#endif
208
198/* 209/*
199 * Mark a position in code as unreachable. This can be used to 210 * Mark a position in code as unreachable. This can be used to
200 * suppress control flow warnings after asm blocks that transfer 211 * suppress control flow warnings after asm blocks that transfer
@@ -204,7 +215,7 @@
204 * this in the preprocessor, but we can live with this because they're 215 * this in the preprocessor, but we can live with this because they're
205 * unreleased. Really, we need to have autoconf for the kernel. 216 * unreleased. Really, we need to have autoconf for the kernel.
206 */ 217 */
207#define unreachable() __builtin_unreachable() 218#define unreachable() annotate_unreachable(); __builtin_unreachable()
208 219
209/* Mark a function definition as prohibited from being cloned. */ 220/* Mark a function definition as prohibited from being cloned. */
210#define __noclone __attribute__((__noclone__, __optimize__("no-tracer"))) 221#define __noclone __attribute__((__noclone__, __optimize__("no-tracer")))
diff --git a/tools/objtool/arch.h b/tools/objtool/arch.h
index f7350fcedc70..a59e061c0b4a 100644
--- a/tools/objtool/arch.h
+++ b/tools/objtool/arch.h
@@ -31,9 +31,8 @@
31#define INSN_CALL_DYNAMIC 8 31#define INSN_CALL_DYNAMIC 8
32#define INSN_RETURN 9 32#define INSN_RETURN 9
33#define INSN_CONTEXT_SWITCH 10 33#define INSN_CONTEXT_SWITCH 10
34#define INSN_BUG 11 34#define INSN_NOP 11
35#define INSN_NOP 12 35#define INSN_OTHER 12
36#define INSN_OTHER 13
37#define INSN_LAST INSN_OTHER 36#define INSN_LAST INSN_OTHER
38 37
39int arch_decode_instruction(struct elf *elf, struct section *sec, 38int arch_decode_instruction(struct elf *elf, struct section *sec,
diff --git a/tools/objtool/arch/x86/decode.c b/tools/objtool/arch/x86/decode.c
index 039636ffb6c8..6ac99e3266eb 100644
--- a/tools/objtool/arch/x86/decode.c
+++ b/tools/objtool/arch/x86/decode.c
@@ -118,9 +118,6 @@ int arch_decode_instruction(struct elf *elf, struct section *sec,
118 op2 == 0x35) 118 op2 == 0x35)
119 /* sysenter, sysret */ 119 /* sysenter, sysret */
120 *type = INSN_CONTEXT_SWITCH; 120 *type = INSN_CONTEXT_SWITCH;
121 else if (op2 == 0x0b || op2 == 0xb9)
122 /* ud2 */
123 *type = INSN_BUG;
124 else if (op2 == 0x0d || op2 == 0x1f) 121 else if (op2 == 0x0d || op2 == 0x1f)
125 /* nopl/nopw */ 122 /* nopl/nopw */
126 *type = INSN_NOP; 123 *type = INSN_NOP;
diff --git a/tools/objtool/builtin-check.c b/tools/objtool/builtin-check.c
index e8a1f699058a..5fc52ee3264c 100644
--- a/tools/objtool/builtin-check.c
+++ b/tools/objtool/builtin-check.c
@@ -51,7 +51,7 @@ struct instruction {
51 unsigned int len, state; 51 unsigned int len, state;
52 unsigned char type; 52 unsigned char type;
53 unsigned long immediate; 53 unsigned long immediate;
54 bool alt_group, visited; 54 bool alt_group, visited, dead_end;
55 struct symbol *call_dest; 55 struct symbol *call_dest;
56 struct instruction *jump_dest; 56 struct instruction *jump_dest;
57 struct list_head alts; 57 struct list_head alts;
@@ -330,6 +330,54 @@ static int decode_instructions(struct objtool_file *file)
330} 330}
331 331
332/* 332/*
333 * Find all uses of the unreachable() macro, which are code path dead ends.
334 */
335static int add_dead_ends(struct objtool_file *file)
336{
337 struct section *sec;
338 struct rela *rela;
339 struct instruction *insn;
340 bool found;
341
342 sec = find_section_by_name(file->elf, ".rela__unreachable");
343 if (!sec)
344 return 0;
345
346 list_for_each_entry(rela, &sec->rela_list, list) {
347 if (rela->sym->type != STT_SECTION) {
348 WARN("unexpected relocation symbol type in .rela__unreachable");
349 return -1;
350 }
351 insn = find_insn(file, rela->sym->sec, rela->addend);
352 if (insn)
353 insn = list_prev_entry(insn, list);
354 else if (rela->addend == rela->sym->sec->len) {
355 found = false;
356 list_for_each_entry_reverse(insn, &file->insn_list, list) {
357 if (insn->sec == rela->sym->sec) {
358 found = true;
359 break;
360 }
361 }
362
363 if (!found) {
364 WARN("can't find unreachable insn at %s+0x%x",
365 rela->sym->sec->name, rela->addend);
366 return -1;
367 }
368 } else {
369 WARN("can't find unreachable insn at %s+0x%x",
370 rela->sym->sec->name, rela->addend);
371 return -1;
372 }
373
374 insn->dead_end = true;
375 }
376
377 return 0;
378}
379
380/*
333 * Warnings shouldn't be reported for ignored functions. 381 * Warnings shouldn't be reported for ignored functions.
334 */ 382 */
335static void add_ignores(struct objtool_file *file) 383static void add_ignores(struct objtool_file *file)
@@ -843,6 +891,10 @@ static int decode_sections(struct objtool_file *file)
843 if (ret) 891 if (ret)
844 return ret; 892 return ret;
845 893
894 ret = add_dead_ends(file);
895 if (ret)
896 return ret;
897
846 add_ignores(file); 898 add_ignores(file);
847 899
848 ret = add_jump_destinations(file); 900 ret = add_jump_destinations(file);
@@ -1037,13 +1089,13 @@ static int validate_branch(struct objtool_file *file,
1037 1089
1038 return 0; 1090 return 0;
1039 1091
1040 case INSN_BUG:
1041 return 0;
1042
1043 default: 1092 default:
1044 break; 1093 break;
1045 } 1094 }
1046 1095
1096 if (insn->dead_end)
1097 return 0;
1098
1047 insn = next_insn_same_sec(file, insn); 1099 insn = next_insn_same_sec(file, insn);
1048 if (!insn) { 1100 if (!insn) {
1049 WARN("%s: unexpected end of section", sec->name); 1101 WARN("%s: unexpected end of section", sec->name);