objtool: Refactor sibling call detection logic
authorJosh Poimboeuf <jpoimboe@redhat.com>
Thu, 18 Jul 2019 01:36:52 +0000 (20:36 -0500)
committerThomas Gleixner <tglx@linutronix.de>
Thu, 18 Jul 2019 19:01:08 +0000 (21:01 +0200)
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
tools/objtool/check.c

index 0d2a8e5..7fe31e0 100644 (file)
@@ -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,34 +181,25 @@ 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.
-                                        */
-                                       return false;
-                               }
-
-                               return __dead_end_function(file, dest->func,
-                                                          recursion + 1);
+                       /* 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.
+                                */
+                               return false;
                        }
-               }
 
-               if (insn->type == INSN_JUMP_DYNAMIC && list_empty(&insn->alts))
-                       /* sibling call */
-                       return false;
+                       return __dead_end_function(file, dest->func, recursion+1);
+               }
        }
 
        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;