objtool: Refactor sibling call detection logic
Simplify the sibling call detection logic a bit. Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com> Signed-off-by: Thomas Gleixner <tglx@linutronix.de> Tested-by: Nick Desaulniers <ndesaulniers@google.com> Acked-by: Peter Zijlstra (Intel) <peterz@infradead.org> Link: https://lkml.kernel.org/r/8357dbef9e7f5512e76bf83a76c81722fc09eb5e.1563413318.git.jpoimboe@redhat.com
This commit is contained in:
parent
c9bab22bc4
commit
0c1ddd3317
|
@ -97,6 +97,20 @@ static struct instruction *next_insn_same_func(struct objtool_file *file,
|
||||||
for (insn = next_insn_same_sec(file, insn); insn; \
|
for (insn = next_insn_same_sec(file, insn); insn; \
|
||||||
insn = next_insn_same_sec(file, 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.
|
* This checks to see if the given function is a "noreturn" function.
|
||||||
*
|
*
|
||||||
|
@ -167,34 +181,25 @@ static bool __dead_end_function(struct objtool_file *file, struct symbol *func,
|
||||||
* of the sibling call returns.
|
* of the sibling call returns.
|
||||||
*/
|
*/
|
||||||
func_for_each_insn_all(file, func, insn) {
|
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;
|
struct instruction *dest = insn->jump_dest;
|
||||||
|
|
||||||
if (!dest)
|
if (!dest)
|
||||||
/* sibling call to another file */
|
/* sibling call to another file */
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (dest->func && dest->func->pfunc != insn->func->pfunc) {
|
/* local sibling call */
|
||||||
|
if (recursion == 5) {
|
||||||
/* local sibling call */
|
/*
|
||||||
if (recursion == 5) {
|
* Infinite recursion: two functions have
|
||||||
/*
|
* sibling calls to each other. This is a very
|
||||||
* Infinite recursion: two functions
|
* rare case. It means they aren't dead ends.
|
||||||
* have sibling calls to each other.
|
*/
|
||||||
* This is a very rare case. It means
|
return false;
|
||||||
* they aren't dead ends.
|
|
||||||
*/
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return __dead_end_function(file, dest->func,
|
|
||||||
recursion + 1);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (insn->type == INSN_JUMP_DYNAMIC && list_empty(&insn->alts))
|
return __dead_end_function(file, dest->func, recursion+1);
|
||||||
/* sibling call */
|
}
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -581,9 +586,8 @@ static int add_jump_destinations(struct objtool_file *file)
|
||||||
insn->retpoline_safe = true;
|
insn->retpoline_safe = true;
|
||||||
continue;
|
continue;
|
||||||
} else {
|
} else {
|
||||||
/* sibling call */
|
/* external sibling call */
|
||||||
insn->call_dest = rela->sym;
|
insn->call_dest = rela->sym;
|
||||||
insn->jump_dest = NULL;
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -633,9 +637,8 @@ static int add_jump_destinations(struct objtool_file *file)
|
||||||
} else if (insn->jump_dest->func->pfunc != insn->func->pfunc &&
|
} else if (insn->jump_dest->func->pfunc != insn->func->pfunc &&
|
||||||
insn->jump_dest->offset == insn->jump_dest->func->offset) {
|
insn->jump_dest->offset == insn->jump_dest->func->offset) {
|
||||||
|
|
||||||
/* sibling class */
|
/* internal sibling call */
|
||||||
insn->call_dest = insn->jump_dest->func;
|
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;
|
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)
|
if (insn->call_dest)
|
||||||
return insn->call_dest->name;
|
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)) {
|
if (state->uaccess && !func_uaccess_safe(insn->call_dest)) {
|
||||||
WARN_FUNC("call to %s() with UACCESS enabled",
|
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;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state->df) {
|
if (state->df) {
|
||||||
WARN_FUNC("call to %s() with DF set",
|
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;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2088,14 +2091,12 @@ static int validate_branch(struct objtool_file *file, struct symbol *func,
|
||||||
|
|
||||||
case INSN_JUMP_CONDITIONAL:
|
case INSN_JUMP_CONDITIONAL:
|
||||||
case INSN_JUMP_UNCONDITIONAL:
|
case INSN_JUMP_UNCONDITIONAL:
|
||||||
if (func && !insn->jump_dest) {
|
if (func && is_sibling_call(insn)) {
|
||||||
ret = validate_sibling_call(insn, &state);
|
ret = validate_sibling_call(insn, &state);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
} else if (insn->jump_dest &&
|
} else if (insn->jump_dest) {
|
||||||
(!func || !insn->jump_dest->func ||
|
|
||||||
insn->jump_dest->func->pfunc == func)) {
|
|
||||||
ret = validate_branch(file, func,
|
ret = validate_branch(file, func,
|
||||||
insn->jump_dest, state);
|
insn->jump_dest, state);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
|
@ -2111,7 +2112,7 @@ static int validate_branch(struct objtool_file *file, struct symbol *func,
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case INSN_JUMP_DYNAMIC:
|
case INSN_JUMP_DYNAMIC:
|
||||||
if (func && list_empty(&insn->alts)) {
|
if (func && is_sibling_call(insn)) {
|
||||||
ret = validate_sibling_call(insn, &state);
|
ret = validate_sibling_call(insn, &state);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
Loading…
Reference in New Issue