diff options
author | Josh Poimboeuf <jpoimboe@redhat.com> | 2018-01-11 16:46:24 -0500 |
---|---|---|
committer | Thomas Gleixner <tglx@linutronix.de> | 2018-01-11 18:14:28 -0500 |
commit | 258c76059cece01bebae098e81bacb1af2edad17 (patch) | |
tree | 1d3bdfe76ad0064b40cdacda35edc7d194f288c8 | |
parent | 39b735332cb8b33a27c28592d969e4016c86c3ea (diff) |
objtool: Allow alternatives to be ignored
Getting objtool to understand retpolines is going to be a bit of a
challenge. For now, take advantage of the fact that retpolines are
patched in with alternatives. Just read the original (sane)
non-alternative instruction, and ignore the patched-in retpoline.
This allows objtool to understand the control flow *around* the
retpoline, even if it can't yet follow what's inside. This means the
ORC unwinder will fail to unwind from inside a retpoline, but will work
fine otherwise.
Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
Signed-off-by: David Woodhouse <dwmw@amazon.co.uk>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Cc: gnomes@lxorguk.ukuu.org.uk
Cc: Rik van Riel <riel@redhat.com>
Cc: Andi Kleen <ak@linux.intel.com>
Cc: thomas.lendacky@amd.com
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Jiri Kosina <jikos@kernel.org>
Cc: Andy Lutomirski <luto@amacapital.net>
Cc: Dave Hansen <dave.hansen@intel.com>
Cc: Kees Cook <keescook@google.com>
Cc: Tim Chen <tim.c.chen@linux.intel.com>
Cc: Greg Kroah-Hartman <gregkh@linux-foundation.org>
Cc: Paul Turner <pjt@google.com>
Link: https://lkml.kernel.org/r/1515707194-20531-3-git-send-email-dwmw@amazon.co.uk
-rw-r--r-- | tools/objtool/check.c | 62 | ||||
-rw-r--r-- | tools/objtool/check.h | 2 |
2 files changed, 57 insertions, 7 deletions
diff --git a/tools/objtool/check.c b/tools/objtool/check.c index de053fb7049b..f40d46e24bcc 100644 --- a/tools/objtool/check.c +++ b/tools/objtool/check.c | |||
@@ -428,6 +428,40 @@ static void add_ignores(struct objtool_file *file) | |||
428 | } | 428 | } |
429 | 429 | ||
430 | /* | 430 | /* |
431 | * FIXME: For now, just ignore any alternatives which add retpolines. This is | ||
432 | * a temporary hack, as it doesn't allow ORC to unwind from inside a retpoline. | ||
433 | * But it at least allows objtool to understand the control flow *around* the | ||
434 | * retpoline. | ||
435 | */ | ||
436 | static int add_nospec_ignores(struct objtool_file *file) | ||
437 | { | ||
438 | struct section *sec; | ||
439 | struct rela *rela; | ||
440 | struct instruction *insn; | ||
441 | |||
442 | sec = find_section_by_name(file->elf, ".rela.discard.nospec"); | ||
443 | if (!sec) | ||
444 | return 0; | ||
445 | |||
446 | list_for_each_entry(rela, &sec->rela_list, list) { | ||
447 | if (rela->sym->type != STT_SECTION) { | ||
448 | WARN("unexpected relocation symbol type in %s", sec->name); | ||
449 | return -1; | ||
450 | } | ||
451 | |||
452 | insn = find_insn(file, rela->sym->sec, rela->addend); | ||
453 | if (!insn) { | ||
454 | WARN("bad .discard.nospec entry"); | ||
455 | return -1; | ||
456 | } | ||
457 | |||
458 | insn->ignore_alts = true; | ||
459 | } | ||
460 | |||
461 | return 0; | ||
462 | } | ||
463 | |||
464 | /* | ||
431 | * Find the destination instructions for all jumps. | 465 | * Find the destination instructions for all jumps. |
432 | */ | 466 | */ |
433 | static int add_jump_destinations(struct objtool_file *file) | 467 | static int add_jump_destinations(struct objtool_file *file) |
@@ -509,11 +543,18 @@ static int add_call_destinations(struct objtool_file *file) | |||
509 | dest_off = insn->offset + insn->len + insn->immediate; | 543 | dest_off = insn->offset + insn->len + insn->immediate; |
510 | insn->call_dest = find_symbol_by_offset(insn->sec, | 544 | insn->call_dest = find_symbol_by_offset(insn->sec, |
511 | dest_off); | 545 | dest_off); |
546 | /* | ||
547 | * FIXME: Thanks to retpolines, it's now considered | ||
548 | * normal for a function to call within itself. So | ||
549 | * disable this warning for now. | ||
550 | */ | ||
551 | #if 0 | ||
512 | if (!insn->call_dest) { | 552 | if (!insn->call_dest) { |
513 | WARN_FUNC("can't find call dest symbol at offset 0x%lx", | 553 | WARN_FUNC("can't find call dest symbol at offset 0x%lx", |
514 | insn->sec, insn->offset, dest_off); | 554 | insn->sec, insn->offset, dest_off); |
515 | return -1; | 555 | return -1; |
516 | } | 556 | } |
557 | #endif | ||
517 | } else if (rela->sym->type == STT_SECTION) { | 558 | } else if (rela->sym->type == STT_SECTION) { |
518 | insn->call_dest = find_symbol_by_offset(rela->sym->sec, | 559 | insn->call_dest = find_symbol_by_offset(rela->sym->sec, |
519 | rela->addend+4); | 560 | rela->addend+4); |
@@ -678,12 +719,6 @@ static int add_special_section_alts(struct objtool_file *file) | |||
678 | return ret; | 719 | return ret; |
679 | 720 | ||
680 | list_for_each_entry_safe(special_alt, tmp, &special_alts, list) { | 721 | list_for_each_entry_safe(special_alt, tmp, &special_alts, list) { |
681 | alt = malloc(sizeof(*alt)); | ||
682 | if (!alt) { | ||
683 | WARN("malloc failed"); | ||
684 | ret = -1; | ||
685 | goto out; | ||
686 | } | ||
687 | 722 | ||
688 | orig_insn = find_insn(file, special_alt->orig_sec, | 723 | orig_insn = find_insn(file, special_alt->orig_sec, |
689 | special_alt->orig_off); | 724 | special_alt->orig_off); |
@@ -694,6 +729,10 @@ static int add_special_section_alts(struct objtool_file *file) | |||
694 | goto out; | 729 | goto out; |
695 | } | 730 | } |
696 | 731 | ||
732 | /* Ignore retpoline alternatives. */ | ||
733 | if (orig_insn->ignore_alts) | ||
734 | continue; | ||
735 | |||
697 | new_insn = NULL; | 736 | new_insn = NULL; |
698 | if (!special_alt->group || special_alt->new_len) { | 737 | if (!special_alt->group || special_alt->new_len) { |
699 | new_insn = find_insn(file, special_alt->new_sec, | 738 | new_insn = find_insn(file, special_alt->new_sec, |
@@ -719,6 +758,13 @@ static int add_special_section_alts(struct objtool_file *file) | |||
719 | goto out; | 758 | goto out; |
720 | } | 759 | } |
721 | 760 | ||
761 | alt = malloc(sizeof(*alt)); | ||
762 | if (!alt) { | ||
763 | WARN("malloc failed"); | ||
764 | ret = -1; | ||
765 | goto out; | ||
766 | } | ||
767 | |||
722 | alt->insn = new_insn; | 768 | alt->insn = new_insn; |
723 | list_add_tail(&alt->list, &orig_insn->alts); | 769 | list_add_tail(&alt->list, &orig_insn->alts); |
724 | 770 | ||
@@ -1035,6 +1081,10 @@ static int decode_sections(struct objtool_file *file) | |||
1035 | 1081 | ||
1036 | add_ignores(file); | 1082 | add_ignores(file); |
1037 | 1083 | ||
1084 | ret = add_nospec_ignores(file); | ||
1085 | if (ret) | ||
1086 | return ret; | ||
1087 | |||
1038 | ret = add_jump_destinations(file); | 1088 | ret = add_jump_destinations(file); |
1039 | if (ret) | 1089 | if (ret) |
1040 | return ret; | 1090 | return ret; |
diff --git a/tools/objtool/check.h b/tools/objtool/check.h index 47d9ea70a83d..dbadb304a410 100644 --- a/tools/objtool/check.h +++ b/tools/objtool/check.h | |||
@@ -44,7 +44,7 @@ struct instruction { | |||
44 | unsigned int len; | 44 | unsigned int len; |
45 | unsigned char type; | 45 | unsigned char type; |
46 | unsigned long immediate; | 46 | unsigned long immediate; |
47 | bool alt_group, visited, dead_end, ignore, hint, save, restore; | 47 | bool alt_group, visited, dead_end, ignore, hint, save, restore, ignore_alts; |
48 | struct symbol *call_dest; | 48 | struct symbol *call_dest; |
49 | struct instruction *jump_dest; | 49 | struct instruction *jump_dest; |
50 | struct list_head alts; | 50 | struct list_head alts; |