Skip to content

Commit d1091c7

Browse files
jpoimboeIngo Molnar
authored andcommitted
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>
1 parent 9f0c18a commit d1091c7

File tree

5 files changed

+71
-11
lines changed

5 files changed

+71
-11
lines changed

arch/x86/kernel/vmlinux.lds.S

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -346,6 +346,7 @@ SECTIONS
346346
/DISCARD/ : {
347347
*(.eh_frame)
348348
*(__func_stack_frame_non_standard)
349+
*(__unreachable)
349350
}
350351
}
351352

include/linux/compiler-gcc.h

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,17 @@
195195
#endif
196196
#endif
197197

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+
198209
/*
199210
* Mark a position in code as unreachable. This can be used to
200211
* suppress control flow warnings after asm blocks that transfer
@@ -204,7 +215,7 @@
204215
* this in the preprocessor, but we can live with this because they're
205216
* unreleased. Really, we need to have autoconf for the kernel.
206217
*/
207-
#define unreachable() __builtin_unreachable()
218+
#define unreachable() annotate_unreachable(); __builtin_unreachable()
208219

209220
/* Mark a function definition as prohibited from being cloned. */
210221
#define __noclone __attribute__((__noclone__, __optimize__("no-tracer")))

tools/objtool/arch.h

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,8 @@
3131
#define INSN_CALL_DYNAMIC 8
3232
#define INSN_RETURN 9
3333
#define INSN_CONTEXT_SWITCH 10
34-
#define INSN_BUG 11
35-
#define INSN_NOP 12
36-
#define INSN_OTHER 13
34+
#define INSN_NOP 11
35+
#define INSN_OTHER 12
3736
#define INSN_LAST INSN_OTHER
3837

3938
int arch_decode_instruction(struct elf *elf, struct section *sec,

tools/objtool/arch/x86/decode.c

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -118,9 +118,6 @@ int arch_decode_instruction(struct elf *elf, struct section *sec,
118118
op2 == 0x35)
119119
/* sysenter, sysret */
120120
*type = INSN_CONTEXT_SWITCH;
121-
else if (op2 == 0x0b || op2 == 0xb9)
122-
/* ud2 */
123-
*type = INSN_BUG;
124121
else if (op2 == 0x0d || op2 == 0x1f)
125122
/* nopl/nopw */
126123
*type = INSN_NOP;

tools/objtool/builtin-check.c

Lines changed: 56 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ struct instruction {
5151
unsigned int len, state;
5252
unsigned char type;
5353
unsigned long immediate;
54-
bool alt_group, visited;
54+
bool alt_group, visited, dead_end;
5555
struct symbol *call_dest;
5656
struct instruction *jump_dest;
5757
struct list_head alts;
@@ -329,6 +329,54 @@ static int decode_instructions(struct objtool_file *file)
329329
return 0;
330330
}
331331

332+
/*
333+
* Find all uses of the unreachable() macro, which are code path dead ends.
334+
*/
335+
static 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+
332380
/*
333381
* Warnings shouldn't be reported for ignored functions.
334382
*/
@@ -843,6 +891,10 @@ static int decode_sections(struct objtool_file *file)
843891
if (ret)
844892
return ret;
845893

894+
ret = add_dead_ends(file);
895+
if (ret)
896+
return ret;
897+
846898
add_ignores(file);
847899

848900
ret = add_jump_destinations(file);
@@ -1037,13 +1089,13 @@ static int validate_branch(struct objtool_file *file,
10371089

10381090
return 0;
10391091

1040-
case INSN_BUG:
1041-
return 0;
1042-
10431092
default:
10441093
break;
10451094
}
10461095

1096+
if (insn->dead_end)
1097+
return 0;
1098+
10471099
insn = next_insn_same_sec(file, insn);
10481100
if (!insn) {
10491101
WARN("%s: unexpected end of section", sec->name);

0 commit comments

Comments
 (0)