On Sat, Jul 06, 2019 at 10:50:01AM -0500, Josh Poimboeuf wrote:
> On Tue, Jul 02, 2019 at 11:58:27PM +0200, Thomas Gleixner wrote:
> > platform-quirks.o:
> > 
> >         if (x86_platform.set_legacy_features)
> >   74:   4c 8b 1d 00 00 00 00    mov    0x0(%rip),%r11        # 7b 
> > <x86_early_init_platform_quirks+0x7b>
> >   7b:   4d 85 db                test   %r11,%r11
> >   7e:   0f 85 00 00 00 00       jne    84 
> > <x86_early_init_platform_quirks+0x84>
> >                 x86_platform.set_legacy_features();
> > }
> >   84:   c3                      retq   
> > 
> > That jne jumps to __x86_indirect_thunk_r11, aka. ratpoutine.
> > 
> > No idea why objtool thinks that the instruction at 0x84 is not
> > reachable. Josh?
> 
> That's a conditional tail call, which is something GCC never does.
> Objtool doesn't understand that, so we'll need to fix it.

Can somebody test this patch to see if it fixes the platform-quirks.o
warning?

diff --git a/tools/objtool/check.c b/tools/objtool/check.c
index 27818a93f0b1..42fe09a93593 100644
--- a/tools/objtool/check.c
+++ b/tools/objtool/check.c
@@ -97,6 +97,36 @@ 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)
+{
+       struct symbol *insn_func, *dest_func;
+
+       /* Only C code does sibling calls. */
+       if (!insn->func)
+               return false;
+
+       /* 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;
+
+       /* A direct jump to outside the .o file is a sibling call. */
+       if (!insn->jump_dest)
+               return true;
+
+       if (!insn->jump_dest->func)
+               return false;
+
+       /* A direct jump to the beginning of a function is a sibling call. */
+       insn_func = insn->func->pfunc;
+       dest_func = insn->jump_dest->func->pfunc;
+       return insn_func != dest_func &&
+              insn->jump_dest->offset == dest_func->offset;
+}
+
 /*
  * This checks to see if the given function is a "noreturn" function.
  *
@@ -169,34 +199,25 @@ static int __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 0;
 
-                       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 0;
-                               }
-
-                               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 0;
                        }
-               }
 
-               if (insn->type == INSN_JUMP_DYNAMIC && list_empty(&insn->alts))
-                       /* sibling call */
-                       return 0;
+                       return __dead_end_function(file, dest->func, 
recursion+1);
+               }
        }
 
        return 1;
@@ -635,7 +656,7 @@ 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 */
+                               /* sibling calls */
                                insn->call_dest = insn->jump_dest->func;
                                insn->jump_dest = NULL;
                        }
@@ -2100,7 +2121,7 @@ static int validate_branch(struct objtool_file *file, 
struct instruction *first,
 
                case INSN_JUMP_CONDITIONAL:
                case INSN_JUMP_UNCONDITIONAL:
-                       if (func && !insn->jump_dest) {
+                       if (is_sibling_call(insn)) {
                                ret = validate_sibling_call(insn, &state);
                                if (ret)
                                        return ret;
@@ -2123,7 +2144,7 @@ static int validate_branch(struct objtool_file *file, 
struct instruction *first,
                        break;
 
                case INSN_JUMP_DYNAMIC:
-                       if (func && list_empty(&insn->alts)) {
+                       if (is_sibling_call(insn)) {
                                ret = validate_sibling_call(insn, &state);
                                if (ret)
                                        return ret;

Reply via email to