Commit 0c1ddd33 authored by Josh Poimboeuf's avatar Josh Poimboeuf Committed by Thomas Gleixner
Browse files

objtool: Refactor sibling call detection logic

parent c9bab22b
Loading
Loading
Loading
Loading
+33 −32
Original line number Diff line number Diff line
@@ -97,6 +97,20 @@ static struct instruction *next_insn_same_func(struct objtool_file *file,
	for (insn = next_insn_same_sec(file, insn); insn;		\
	     insn = next_insn_same_sec(file, insn))

static bool is_sibling_call(struct instruction *insn)
{
	/* An indirect jump is either a sibling call or a jump to a table. */
	if (insn->type == INSN_JUMP_DYNAMIC)
		return list_empty(&insn->alts);

	if (insn->type != INSN_JUMP_CONDITIONAL &&
	    insn->type != INSN_JUMP_UNCONDITIONAL)
		return false;

	/* add_jump_destinations() sets insn->call_dest for sibling calls. */
	return !!insn->call_dest;
}

/*
 * This checks to see if the given function is a "noreturn" function.
 *
@@ -167,36 +181,27 @@ static bool __dead_end_function(struct objtool_file *file, struct symbol *func,
	 * of the sibling call returns.
	 */
	func_for_each_insn_all(file, func, insn) {
		if (insn->type == INSN_JUMP_UNCONDITIONAL) {
		if (is_sibling_call(insn)) {
			struct instruction *dest = insn->jump_dest;

			if (!dest)
				/* sibling call to another file */
				return false;

			if (dest->func && dest->func->pfunc != insn->func->pfunc) {

			/* local sibling call */
			if (recursion == 5) {
				/*
					 * Infinite recursion: two functions
					 * have sibling calls to each other.
					 * This is a very rare case.  It means
					 * they aren't dead ends.
				 * Infinite recursion: two functions have
				 * sibling calls to each other.  This is a very
				 * rare case.  It means they aren't dead ends.
				 */
				return false;
			}

				return __dead_end_function(file, dest->func,
							   recursion + 1);
			return __dead_end_function(file, dest->func, recursion+1);
		}
	}

		if (insn->type == INSN_JUMP_DYNAMIC && list_empty(&insn->alts))
			/* sibling call */
			return false;
	}

	return true;
}

@@ -581,9 +586,8 @@ static int add_jump_destinations(struct objtool_file *file)
			insn->retpoline_safe = true;
			continue;
		} else {
			/* sibling call */
			/* external sibling call */
			insn->call_dest = rela->sym;
			insn->jump_dest = NULL;
			continue;
		}

@@ -633,9 +637,8 @@ static int add_jump_destinations(struct objtool_file *file)
			} else if (insn->jump_dest->func->pfunc != insn->func->pfunc &&
				   insn->jump_dest->offset == insn->jump_dest->func->offset) {

				/* sibling class */
				/* internal sibling call */
				insn->call_dest = insn->jump_dest->func;
				insn->jump_dest = NULL;
			}
		}
	}
@@ -1889,7 +1892,7 @@ static inline bool func_uaccess_safe(struct symbol *func)
	return false;
}

static inline const char *insn_dest_name(struct instruction *insn)
static inline const char *call_dest_name(struct instruction *insn)
{
	if (insn->call_dest)
		return insn->call_dest->name;
@@ -1901,13 +1904,13 @@ static int validate_call(struct instruction *insn, struct insn_state *state)
{
	if (state->uaccess && !func_uaccess_safe(insn->call_dest)) {
		WARN_FUNC("call to %s() with UACCESS enabled",
				insn->sec, insn->offset, insn_dest_name(insn));
				insn->sec, insn->offset, call_dest_name(insn));
		return 1;
	}

	if (state->df) {
		WARN_FUNC("call to %s() with DF set",
				insn->sec, insn->offset, insn_dest_name(insn));
				insn->sec, insn->offset, call_dest_name(insn));
		return 1;
	}

@@ -2088,14 +2091,12 @@ static int validate_branch(struct objtool_file *file, struct symbol *func,

		case INSN_JUMP_CONDITIONAL:
		case INSN_JUMP_UNCONDITIONAL:
			if (func && !insn->jump_dest) {
			if (func && is_sibling_call(insn)) {
				ret = validate_sibling_call(insn, &state);
				if (ret)
					return ret;

			} else if (insn->jump_dest &&
				   (!func || !insn->jump_dest->func ||
				    insn->jump_dest->func->pfunc == func)) {
			} else if (insn->jump_dest) {
				ret = validate_branch(file, func,
						      insn->jump_dest, state);
				if (ret) {
@@ -2111,7 +2112,7 @@ static int validate_branch(struct objtool_file *file, struct symbol *func,
			break;

		case INSN_JUMP_DYNAMIC:
			if (func && list_empty(&insn->alts)) {
			if (func && is_sibling_call(insn)) {
				ret = validate_sibling_call(insn, &state);
				if (ret)
					return ret;