Messages in this thread | | | Subject | Re: [PATCH 2/7] objtool: Allow branches within the same alternative. | From | Alexandre Chartre <> | Date | Thu, 2 Apr 2020 14:38:16 +0200 |
| |
On 4/2/20 2:03 PM, Julien Thierry wrote: > Hi Alexandre, > > I ran into the same issue for the arm64 work: > https://lkml.org/lkml/2020/1/9/656
Thanks for the reference, I didn't notice that change, but I saw a more recent one where you were just removing the branch to alternative check (https://lkml.org/lkml/2020/3/25/151).
> Your solution seems nicer however. > > On 4/2/20 9:22 AM, Alexandre Chartre wrote: >> Currently objtool prevents any branch to an alternative. While preventing >> branching from the outside to the middle of an alternative makes perfect >> sense, branching within the same alternative should be allowed. To do so, >> identify each alternative and check that a branch to an alternative comes >> from the same alternative. >> >> Signed-off-by: Alexandre Chartre <alexandre.chartre@oracle.com> >> --- >> tools/objtool/check.c | 19 +++++++++++++------ >> tools/objtool/check.h | 3 ++- >> 2 files changed, 15 insertions(+), 7 deletions(-) >> >> diff --git a/tools/objtool/check.c b/tools/objtool/check.c >> index 708562fb89e1..214809ac2776 100644 >> --- a/tools/objtool/check.c >> +++ b/tools/objtool/check.c >> @@ -712,7 +712,9 @@ static int handle_group_alt(struct objtool_file *file, >> struct instruction *orig_insn, >> struct instruction **new_insn) >> { >> + static unsigned int alt_group_next_index = 1; >> struct instruction *last_orig_insn, *last_new_insn, *insn, *fake_jump = NULL; >> + unsigned int alt_group = alt_group_next_index++; >> unsigned long dest_off; >> last_orig_insn = NULL; >> @@ -721,7 +723,7 @@ static int handle_group_alt(struct objtool_file *file, >> if (insn->offset >= special_alt->orig_off + special_alt->orig_len) >> break; >> - insn->alt_group = true; >> + insn->alt_group = alt_group; >> last_orig_insn = insn; >> } >> @@ -1942,6 +1944,7 @@ static int validate_sibling_call(struct instruction *insn, struct insn_state *st >> * tools/objtool/Documentation/stack-validation.txt. >> */ >> static int validate_branch(struct objtool_file *file, struct symbol *func, >> + struct instruction *from, > > Maybe instead of passing a new instruction pointer, just the > alt_group could be passed? 0 Meaning it was not in an alt group> >> struct instruction *first, struct insn_state state) >> { >> struct alternative *alt; >> @@ -1953,7 +1956,9 @@ static int validate_branch(struct objtool_file *file, struct symbol *func, >> insn = first; >> sec = insn->sec; >> - if (insn->alt_group && list_empty(&insn->alts)) { >> + if (insn->alt_group && >> + (!from || from->alt_group != insn->alt_group) && >> + list_empty(&insn->alts)) { > > This would become > > if (insn->alt_group != alt_group && list_empty(&insn->alts)) > > And the recursive validate_branch() calls would just take > insn->alt_group as parameter (and the calls in validate_functions() > and validate_unwind_hints() would take 0). > > Any opinions on that?
Yes, that would work too. I choose to pass the instruction pointer because I was thinking that having the "from" instruction might be useful in the future if there's a need to do additional check about the origin of the branch.
alex.
>> WARN_FUNC("don't know how to handle branch to middle of alternative instruction group", >> sec, insn->offset); >> return 1; >> @@ -2035,7 +2040,8 @@ static int validate_branch(struct objtool_file *file, struct symbol *func, >> if (alt->skip_orig) >> skip_orig = true; >> - ret = validate_branch(file, func, alt->insn, state); >> + ret = validate_branch(file, func, >> + NULL, alt->insn, state); >> if (ret) { >> if (backtrace) >> BT_FUNC("(alt)", insn); >> @@ -2105,7 +2111,7 @@ static int validate_branch(struct objtool_file *file, struct symbol *func, >> return ret; >> } else if (insn->jump_dest) { >> - ret = validate_branch(file, func, >> + ret = validate_branch(file, func, insn, >> insn->jump_dest, state); >> if (ret) { >> if (backtrace) >> @@ -2236,7 +2242,8 @@ static int validate_unwind_hints(struct objtool_file *file) >> for_each_insn(file, insn) { >> if (insn->hint && !insn->visited) { >> - ret = validate_branch(file, insn->func, insn, state); >> + ret = validate_branch(file, insn->func, >> + NULL, insn, state); >> if (ret && backtrace) >> BT_FUNC("<=== (hint)", insn); >> warnings += ret; >> @@ -2377,7 +2384,7 @@ static int validate_functions(struct objtool_file *file) >> state.uaccess = func->uaccess_safe; >> - ret = validate_branch(file, func, insn, state); >> + ret = validate_branch(file, func, NULL, insn, state); >> if (ret && backtrace) >> BT_FUNC("<=== (func)", insn); >> warnings += ret; >> diff --git a/tools/objtool/check.h b/tools/objtool/check.h >> index 6d875ca6fce0..cffb23d81782 100644 >> --- a/tools/objtool/check.h >> +++ b/tools/objtool/check.h >> @@ -33,7 +33,8 @@ struct instruction { >> unsigned int len; >> enum insn_type type; >> unsigned long immediate; >> - bool alt_group, dead_end, ignore, hint, save, restore, ignore_alts; >> + unsigned int alt_group; >> + bool dead_end, ignore, hint, save, restore, ignore_alts; >> bool retpoline_safe; >> u8 visited; >> struct symbol *call_dest; >> > > Cheers, >
| |