diff options
Diffstat (limited to 'tools/objtool/check.c')
-rw-r--r-- | tools/objtool/check.c | 69 |
1 files changed, 63 insertions, 6 deletions
diff --git a/tools/objtool/check.c b/tools/objtool/check.c index 9b341584eb1b..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) |
@@ -456,6 +490,13 @@ static int add_jump_destinations(struct objtool_file *file) | |||
456 | } else if (rela->sym->sec->idx) { | 490 | } else if (rela->sym->sec->idx) { |
457 | dest_sec = rela->sym->sec; | 491 | dest_sec = rela->sym->sec; |
458 | dest_off = rela->sym->sym.st_value + rela->addend + 4; | 492 | dest_off = rela->sym->sym.st_value + rela->addend + 4; |
493 | } else if (strstr(rela->sym->name, "_indirect_thunk_")) { | ||
494 | /* | ||
495 | * Retpoline jumps are really dynamic jumps in | ||
496 | * disguise, so convert them accordingly. | ||
497 | */ | ||
498 | insn->type = INSN_JUMP_DYNAMIC; | ||
499 | continue; | ||
459 | } else { | 500 | } else { |
460 | /* sibling call */ | 501 | /* sibling call */ |
461 | insn->jump_dest = 0; | 502 | insn->jump_dest = 0; |
@@ -502,11 +543,18 @@ static int add_call_destinations(struct objtool_file *file) | |||
502 | dest_off = insn->offset + insn->len + insn->immediate; | 543 | dest_off = insn->offset + insn->len + insn->immediate; |
503 | insn->call_dest = find_symbol_by_offset(insn->sec, | 544 | insn->call_dest = find_symbol_by_offset(insn->sec, |
504 | 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 | ||
505 | if (!insn->call_dest) { | 552 | if (!insn->call_dest) { |
506 | 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", |
507 | insn->sec, insn->offset, dest_off); | 554 | insn->sec, insn->offset, dest_off); |
508 | return -1; | 555 | return -1; |
509 | } | 556 | } |
557 | #endif | ||
510 | } else if (rela->sym->type == STT_SECTION) { | 558 | } else if (rela->sym->type == STT_SECTION) { |
511 | insn->call_dest = find_symbol_by_offset(rela->sym->sec, | 559 | insn->call_dest = find_symbol_by_offset(rela->sym->sec, |
512 | rela->addend+4); | 560 | rela->addend+4); |
@@ -671,12 +719,6 @@ static int add_special_section_alts(struct objtool_file *file) | |||
671 | return ret; | 719 | return ret; |
672 | 720 | ||
673 | list_for_each_entry_safe(special_alt, tmp, &special_alts, list) { | 721 | list_for_each_entry_safe(special_alt, tmp, &special_alts, list) { |
674 | alt = malloc(sizeof(*alt)); | ||
675 | if (!alt) { | ||
676 | WARN("malloc failed"); | ||
677 | ret = -1; | ||
678 | goto out; | ||
679 | } | ||
680 | 722 | ||
681 | orig_insn = find_insn(file, special_alt->orig_sec, | 723 | orig_insn = find_insn(file, special_alt->orig_sec, |
682 | special_alt->orig_off); | 724 | special_alt->orig_off); |
@@ -687,6 +729,10 @@ static int add_special_section_alts(struct objtool_file *file) | |||
687 | goto out; | 729 | goto out; |
688 | } | 730 | } |
689 | 731 | ||
732 | /* Ignore retpoline alternatives. */ | ||
733 | if (orig_insn->ignore_alts) | ||
734 | continue; | ||
735 | |||
690 | new_insn = NULL; | 736 | new_insn = NULL; |
691 | if (!special_alt->group || special_alt->new_len) { | 737 | if (!special_alt->group || special_alt->new_len) { |
692 | new_insn = find_insn(file, special_alt->new_sec, | 738 | new_insn = find_insn(file, special_alt->new_sec, |
@@ -712,6 +758,13 @@ static int add_special_section_alts(struct objtool_file *file) | |||
712 | goto out; | 758 | goto out; |
713 | } | 759 | } |
714 | 760 | ||
761 | alt = malloc(sizeof(*alt)); | ||
762 | if (!alt) { | ||
763 | WARN("malloc failed"); | ||
764 | ret = -1; | ||
765 | goto out; | ||
766 | } | ||
767 | |||
715 | alt->insn = new_insn; | 768 | alt->insn = new_insn; |
716 | list_add_tail(&alt->list, &orig_insn->alts); | 769 | list_add_tail(&alt->list, &orig_insn->alts); |
717 | 770 | ||
@@ -1028,6 +1081,10 @@ static int decode_sections(struct objtool_file *file) | |||
1028 | 1081 | ||
1029 | add_ignores(file); | 1082 | add_ignores(file); |
1030 | 1083 | ||
1084 | ret = add_nospec_ignores(file); | ||
1085 | if (ret) | ||
1086 | return ret; | ||
1087 | |||
1031 | ret = add_jump_destinations(file); | 1088 | ret = add_jump_destinations(file); |
1032 | if (ret) | 1089 | if (ret) |
1033 | return ret; | 1090 | return ret; |