Commit a0e5bf9f authored by Alexandre Chartre's avatar Alexandre Chartre Committed by Peter Zijlstra
Browse files

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: default avatarAlexandre Chartre <alexandre.chartre@oracle.com>
Signed-off-by: default avatarPeter Zijlstra (Intel) <peterz@infradead.org>
Acked-by: default avatarJosh Poimboeuf <jpoimboe@kernel.org>
Link: https://patch.msgid.link/20251121095340.464045-9-alexandre.chartre@oracle.com
parent 0bb080ba
Loading
Loading
Loading
Loading
+205 −181
Original line number Diff line number Diff line
@@ -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;

	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;
		}
	/*
	 * 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;

		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,