[RFC v5 12/57] objtool: check: Allow jumps from an alternative group to itself

From: Julien Thierry
Date: Thu Jan 09 2020 - 11:04:37 EST


Alternatives can contain instructions that jump to another instruction
in the same alternative group. This is actually a common pattern on
arm64.

Keep track of instructions jumping within their own alternative group
and carry on validating such branches.

Signed-off-by: Julien Thierry <jthierry@xxxxxxxxxx>
---
tools/objtool/check.c | 48 ++++++++++++++++++++++++++++++++++---------
tools/objtool/check.h | 1 +
2 files changed, 39 insertions(+), 10 deletions(-)

diff --git a/tools/objtool/check.c b/tools/objtool/check.c
index 8f2ff030936d..c7b3f1e2a628 100644
--- a/tools/objtool/check.c
+++ b/tools/objtool/check.c
@@ -722,6 +722,14 @@ static int handle_group_alt(struct objtool_file *file,
sec_for_each_insn_from(file, insn) {
if (insn->offset >= special_alt->orig_off + special_alt->orig_len)
break;
+ /* Is insn a jump to an instruction within the alt_group */
+ if (insn->jump_dest && insn->sec == insn->jump_dest->sec &&
+ (insn->type == INSN_JUMP_CONDITIONAL ||
+ insn->type == INSN_JUMP_UNCONDITIONAL)) {
+ dest_off = insn->jump_dest->offset;
+ insn->intra_group_jump = special_alt->orig_off <= dest_off &&
+ dest_off < special_alt->orig_off + special_alt->orig_len;
+ }

insn->alt_group = true;
last_orig_insn = insn;
@@ -1853,14 +1861,33 @@ static int validate_sibling_call(struct instruction *insn, struct insn_state *st
return validate_call(insn, state);
}

+static int validate_branch_alt_safe(struct objtool_file *file,
+ struct symbol *func,
+ struct instruction *first,
+ struct insn_state state);
+
+static int validate_branch(struct objtool_file *file, struct symbol *func,
+ struct instruction *first, struct insn_state state)
+{
+ if (first->alt_group && list_empty(&first->alts)) {
+ WARN_FUNC("don't know how to handle branch to middle of alternative instruction group",
+ first->sec, first->offset);
+ return 1;
+ }
+
+ return validate_branch_alt_safe(file, func, first, state);
+}
+
/*
* 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
* tools/objtool/Documentation/stack-validation.txt.
*/
-static int validate_branch(struct objtool_file *file, struct symbol *func,
- struct instruction *first, struct insn_state state)
+static int validate_branch_alt_safe(struct objtool_file *file,
+ struct symbol *func,
+ struct instruction *first,
+ struct insn_state state)
{
struct alternative *alt;
struct instruction *insn, *next_insn;
@@ -1871,12 +1898,6 @@ static int validate_branch(struct objtool_file *file, struct symbol *func,
insn = first;
sec = insn->sec;

- if (insn->alt_group && list_empty(&insn->alts)) {
- WARN_FUNC("don't know how to handle branch to middle of alternative instruction group",
- sec, insn->offset);
- return 1;
- }
-
while (1) {
next_insn = next_insn_same_sec(file, insn);

@@ -2023,8 +2044,15 @@ static int validate_branch(struct objtool_file *file, struct symbol *func,
return ret;

} else if (insn->jump_dest) {
- ret = validate_branch(file, func,
- insn->jump_dest, state);
+ if (insn->intra_group_jump)
+ ret = validate_branch_alt_safe(file,
+ func,
+ insn->jump_dest,
+ state);
+ else
+ ret = validate_branch(file, func,
+ insn->jump_dest,
+ state);
if (ret) {
if (backtrace)
BT_FUNC("(branch)", insn);
diff --git a/tools/objtool/check.h b/tools/objtool/check.h
index af87b55db454..d13ee02f28a4 100644
--- a/tools/objtool/check.h
+++ b/tools/objtool/check.h
@@ -46,6 +46,7 @@ struct instruction {
struct stack_op stack_op;
struct insn_state state;
struct orc_entry orc;
+ bool intra_group_jump;
};

struct objtool_file {
--
2.21.0