diff options
Diffstat (limited to 'tools/objtool/check.c')
-rw-r--r-- | tools/objtool/check.c | 34 |
1 files changed, 33 insertions, 1 deletions
diff --git a/tools/objtool/check.c b/tools/objtool/check.c index 4da6bfb8a98d..fa9bf364545c 100644 --- a/tools/objtool/check.c +++ b/tools/objtool/check.c @@ -1983,6 +1983,11 @@ static int handle_insn_ops(struct instruction *insn, struct insn_state *state) list_for_each_entry(op, &insn->stack_ops, list) { int res; + if (insn->alt_group) { + WARN_FUNC("alternative modifies stack", insn->sec, insn->offset); + return -1; + } + res = update_cfi_state(insn, &state->cfi, op); if (res) return res; @@ -2150,6 +2155,30 @@ static int validate_return(struct symbol *func, struct instruction *insn, struct } /* + * Alternatives should not contain any ORC entries, this in turn means they + * should not contain any CFI ops, which implies all instructions should have + * the same same CFI state. + * + * It is possible to constuct alternatives that have unreachable holes that go + * unreported (because they're NOPs), such holes would result in CFI_UNDEFINED + * states which then results in ORC entries, which we just said we didn't want. + * + * Avoid them by copying the CFI entry of the first instruction into the whole + * alternative. + */ +static void fill_alternative_cfi(struct objtool_file *file, struct instruction *insn) +{ + struct instruction *first_insn = insn; + int alt_group = insn->alt_group; + + sec_for_each_insn_continue(file, insn) { + if (insn->alt_group != alt_group) + break; + insn->cfi = first_insn->cfi; + } +} + +/* * Follow the branch starting at the given instruction, and recursively follow * any other branches (jumps). Meanwhile, track the frame pointer state at * each instruction and validate all the rules described in @@ -2200,7 +2229,7 @@ static int validate_branch(struct objtool_file *file, struct symbol *func, insn->visited |= visited; - if (!insn->ignore_alts) { + if (!insn->ignore_alts && !list_empty(&insn->alts)) { bool skip_orig = false; list_for_each_entry(alt, &insn->alts, list) { @@ -2215,6 +2244,9 @@ static int validate_branch(struct objtool_file *file, struct symbol *func, } } + if (insn->alt_group) + fill_alternative_cfi(file, insn); + if (skip_orig) return 0; } |