mirror of https://github.com/torvalds/linux.git
objtool: Extract code to validate instruction from the validate branch loop
The code to validate a branch loops through all instructions of the branch and validate each instruction. Move the code to validate an instruction to a separated function. Signed-off-by: Alexandre Chartre <alexandre.chartre@oracle.com> Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org> Acked-by: Josh Poimboeuf <jpoimboe@kernel.org> Link: https://patch.msgid.link/20251121095340.464045-9-alexandre.chartre@oracle.com
This commit is contained in:
parent
0bb080ba64
commit
a0e5bf9fd6
|
|
@ -3654,47 +3654,27 @@ static void checksum_update_insn(struct objtool_file *file, struct symbol *func,
|
|||
checksum_update(func, insn, &offset, sizeof(offset));
|
||||
}
|
||||
|
||||
/*
|
||||
* 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/objtool.txt.
|
||||
*/
|
||||
static int validate_branch(struct objtool_file *file, struct symbol *func,
|
||||
struct instruction *insn, struct insn_state state)
|
||||
struct instruction *insn, struct insn_state state);
|
||||
|
||||
static int validate_insn(struct objtool_file *file, struct symbol *func,
|
||||
struct instruction *insn, struct insn_state *statep,
|
||||
struct instruction *prev_insn, struct instruction *next_insn,
|
||||
bool *dead_end)
|
||||
{
|
||||
struct alternative *alt;
|
||||
struct instruction *next_insn, *prev_insn = NULL;
|
||||
u8 visited;
|
||||
int ret;
|
||||
|
||||
if (func && func->ignore)
|
||||
return 0;
|
||||
/*
|
||||
* Any returns before the end of this function are effectively dead
|
||||
* ends, i.e. validate_branch() has reached the end of the branch.
|
||||
*/
|
||||
*dead_end = true;
|
||||
|
||||
while (1) {
|
||||
next_insn = next_insn_to_validate(file, insn);
|
||||
|
||||
if (opts.checksum && func && insn->sec)
|
||||
checksum_update_insn(file, func, insn);
|
||||
|
||||
if (func && insn_func(insn) && func != insn_func(insn)->pfunc) {
|
||||
/* Ignore KCFI type preambles, which always fall through */
|
||||
if (is_prefix_func(func))
|
||||
return 0;
|
||||
|
||||
if (file->ignore_unreachables)
|
||||
return 0;
|
||||
|
||||
WARN("%s() falls through to next function %s()",
|
||||
func->name, insn_func(insn)->name);
|
||||
func->warned = 1;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
visited = VISITED_BRANCH << state.uaccess;
|
||||
visited = VISITED_BRANCH << statep->uaccess;
|
||||
if (insn->visited & VISITED_BRANCH_MASK) {
|
||||
if (!insn->hint && !insn_cfi_match(insn, &state.cfi))
|
||||
if (!insn->hint && !insn_cfi_match(insn, &statep->cfi))
|
||||
return 1;
|
||||
|
||||
if (insn->visited & visited)
|
||||
|
|
@ -3703,8 +3683,8 @@ static int validate_branch(struct objtool_file *file, struct symbol *func,
|
|||
nr_insns_visited++;
|
||||
}
|
||||
|
||||
if (state.noinstr)
|
||||
state.instr += insn->instr;
|
||||
if (statep->noinstr)
|
||||
statep->instr += insn->instr;
|
||||
|
||||
if (insn->hint) {
|
||||
if (insn->restore) {
|
||||
|
|
@ -3746,15 +3726,15 @@ static int validate_branch(struct objtool_file *file, struct symbol *func,
|
|||
nr_cfi_reused++;
|
||||
}
|
||||
|
||||
state.cfi = *insn->cfi;
|
||||
statep->cfi = *insn->cfi;
|
||||
} else {
|
||||
/* XXX track if we actually changed state.cfi */
|
||||
/* XXX track if we actually changed statep->cfi */
|
||||
|
||||
if (prev_insn && !cficmp(prev_insn->cfi, &state.cfi)) {
|
||||
if (prev_insn && !cficmp(prev_insn->cfi, &statep->cfi)) {
|
||||
insn->cfi = prev_insn->cfi;
|
||||
nr_cfi_reused++;
|
||||
} else {
|
||||
insn->cfi = cfi_hash_find_or_add(&state.cfi);
|
||||
insn->cfi = cfi_hash_find_or_add(&statep->cfi);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -3765,7 +3745,7 @@ static int validate_branch(struct objtool_file *file, struct symbol *func,
|
|||
|
||||
if (insn->alts) {
|
||||
for (alt = insn->alts; alt; alt = alt->next) {
|
||||
ret = validate_branch(file, func, alt->insn, state);
|
||||
ret = validate_branch(file, func, alt->insn, *statep);
|
||||
if (ret) {
|
||||
BT_INSN(insn, "(alt)");
|
||||
return ret;
|
||||
|
|
@ -3776,22 +3756,22 @@ static int validate_branch(struct objtool_file *file, struct symbol *func,
|
|||
if (skip_alt_group(insn))
|
||||
return 0;
|
||||
|
||||
if (handle_insn_ops(insn, next_insn, &state))
|
||||
if (handle_insn_ops(insn, next_insn, statep))
|
||||
return 1;
|
||||
|
||||
switch (insn->type) {
|
||||
|
||||
case INSN_RETURN:
|
||||
return validate_return(func, insn, &state);
|
||||
return validate_return(func, insn, statep);
|
||||
|
||||
case INSN_CALL:
|
||||
case INSN_CALL_DYNAMIC:
|
||||
ret = validate_call(file, insn, &state);
|
||||
ret = validate_call(file, insn, statep);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (opts.stackval && func && !is_special_call(insn) &&
|
||||
!has_valid_stack_frame(&state)) {
|
||||
!has_valid_stack_frame(statep)) {
|
||||
WARN_INSN(insn, "call without frame pointer save/setup");
|
||||
return 1;
|
||||
}
|
||||
|
|
@ -3801,13 +3781,13 @@ static int validate_branch(struct objtool_file *file, struct symbol *func,
|
|||
case INSN_JUMP_CONDITIONAL:
|
||||
case INSN_JUMP_UNCONDITIONAL:
|
||||
if (is_sibling_call(insn)) {
|
||||
ret = validate_sibling_call(file, insn, &state);
|
||||
ret = validate_sibling_call(file, insn, statep);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
} else if (insn->jump_dest) {
|
||||
ret = validate_branch(file, func,
|
||||
insn->jump_dest, state);
|
||||
insn->jump_dest, *statep);
|
||||
if (ret) {
|
||||
BT_INSN(insn, "(branch)");
|
||||
return ret;
|
||||
|
|
@ -3822,7 +3802,7 @@ static int validate_branch(struct objtool_file *file, struct symbol *func,
|
|||
case INSN_JUMP_DYNAMIC:
|
||||
case INSN_JUMP_DYNAMIC_CONDITIONAL:
|
||||
if (is_sibling_call(insn)) {
|
||||
ret = validate_sibling_call(file, insn, &state);
|
||||
ret = validate_sibling_call(file, insn, statep);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -3852,55 +3832,99 @@ static int validate_branch(struct objtool_file *file, struct symbol *func,
|
|||
if (!opts.uaccess)
|
||||
break;
|
||||
|
||||
if (state.uaccess) {
|
||||
if (statep->uaccess) {
|
||||
WARN_INSN(insn, "recursive UACCESS enable");
|
||||
return 1;
|
||||
}
|
||||
|
||||
state.uaccess = true;
|
||||
statep->uaccess = true;
|
||||
break;
|
||||
|
||||
case INSN_CLAC:
|
||||
if (!opts.uaccess)
|
||||
break;
|
||||
|
||||
if (!state.uaccess && func) {
|
||||
if (!statep->uaccess && func) {
|
||||
WARN_INSN(insn, "redundant UACCESS disable");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (func_uaccess_safe(func) && !state.uaccess_stack) {
|
||||
if (func_uaccess_safe(func) && !statep->uaccess_stack) {
|
||||
WARN_INSN(insn, "UACCESS-safe disables UACCESS");
|
||||
return 1;
|
||||
}
|
||||
|
||||
state.uaccess = false;
|
||||
statep->uaccess = false;
|
||||
break;
|
||||
|
||||
case INSN_STD:
|
||||
if (state.df) {
|
||||
if (statep->df) {
|
||||
WARN_INSN(insn, "recursive STD");
|
||||
return 1;
|
||||
}
|
||||
|
||||
state.df = true;
|
||||
statep->df = true;
|
||||
break;
|
||||
|
||||
case INSN_CLD:
|
||||
if (!state.df && func) {
|
||||
if (!statep->df && func) {
|
||||
WARN_INSN(insn, "redundant CLD");
|
||||
return 1;
|
||||
}
|
||||
|
||||
state.df = false;
|
||||
statep->df = false;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (insn->dead_end)
|
||||
*dead_end = insn->dead_end;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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/objtool.txt.
|
||||
*/
|
||||
static int validate_branch(struct objtool_file *file, struct symbol *func,
|
||||
struct instruction *insn, struct insn_state state)
|
||||
{
|
||||
struct instruction *next_insn, *prev_insn = NULL;
|
||||
bool dead_end;
|
||||
int ret;
|
||||
|
||||
if (func && func->ignore)
|
||||
return 0;
|
||||
|
||||
while (1) {
|
||||
next_insn = next_insn_to_validate(file, insn);
|
||||
|
||||
if (opts.checksum && func && insn->sec)
|
||||
checksum_update_insn(file, func, insn);
|
||||
|
||||
if (func && insn_func(insn) && func != insn_func(insn)->pfunc) {
|
||||
/* Ignore KCFI type preambles, which always fall through */
|
||||
if (is_prefix_func(func))
|
||||
return 0;
|
||||
|
||||
if (file->ignore_unreachables)
|
||||
return 0;
|
||||
|
||||
WARN("%s() falls through to next function %s()",
|
||||
func->name, insn_func(insn)->name);
|
||||
func->warned = 1;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
ret = validate_insn(file, func, insn, &state, prev_insn, next_insn,
|
||||
&dead_end);
|
||||
if (dead_end)
|
||||
break;
|
||||
|
||||
if (!next_insn) {
|
||||
if (state.cfi.cfa.base == CFI_UNDEFINED)
|
||||
|
|
@ -3918,7 +3942,7 @@ static int validate_branch(struct objtool_file *file, struct symbol *func,
|
|||
insn = next_insn;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int validate_unwind_hint(struct objtool_file *file,
|
||||
|
|
|
|||
Loading…
Reference in New Issue