I've observed GCC generate:

  sym:
     NOP/JMP 1f (static_branch)
     JMP 2f
  1: /* crud */
     JMP 3f
  2: /* other crud */

  3: RETQ


This means we need to follow unconditional jumps; be conservative and
only follow if its a unique jump.

(I've not yet figured out which CONFIG option is responsible for this,
 a normal defconfig build does not generate crap like this)

Signed-off-by: Peter Zijlstra (Intel) <pet...@infradead.org>
---
 tools/objtool/check.c |   33 +++++++++++++++++++++++++++++++--
 tools/objtool/check.h |    3 ++-
 2 files changed, 33 insertions(+), 3 deletions(-)

--- a/tools/objtool/check.c
+++ b/tools/objtool/check.c
@@ -521,7 +521,7 @@ static int add_jump_destinations(struct
                        return -1;
                }
 
-               insn->jump_dest->branch_target = true;
+               insn->jump_dest->branch_target++;
        }
 
        return 0;
@@ -1207,6 +1207,8 @@ static int read_retpoline_hints(struct o
        return 0;
 }
 
+static void jmp_grow_static_block(struct objtool_file *file, struct 
instruction *insn);
+
 static bool __grow_static_block(struct objtool_file *file,
                                struct instruction *insn, bool ign_bt)
 {
@@ -1216,7 +1218,8 @@ static bool __grow_static_block(struct o
 
        switch (insn->type) {
        case INSN_JUMP_UNCONDITIONAL:
-               /* mark this instruction, terminate this section */
+               /* follow the jump, mark this instruction, terminate this 
section */
+               jmp_grow_static_block(file, insn->jump_dest);
                insn->static_jump_dest = true;
                return false;
 
@@ -1238,6 +1241,32 @@ static bool __grow_static_block(struct o
        return true;
 }
 
+static void jmp_grow_static_block(struct objtool_file *file, struct 
instruction *insn)
+{
+       bool ignore = true;
+
+       /* !jump_dest */
+       if (!insn)
+               return;
+
+       /* more than a single site jumps here, can't be certain */
+       if (insn->branch_target > 1)
+               return;
+
+       for (; &insn->list != &file->insn_list;
+            insn = list_next_entry(insn, list)) {
+
+               /*
+                * Per definition the first insn of a jump is a branch target,
+                * don't terminate because of that.
+                */
+               if (!__grow_static_block(file, insn, ignore))
+                       break;
+
+               ignore = false;
+       }
+}
+
 static int grow_static_blocks(struct objtool_file *file)
 {
        bool static_block = false;
--- a/tools/objtool/check.h
+++ b/tools/objtool/check.h
@@ -45,7 +45,8 @@ struct instruction {
        unsigned char type;
        unsigned long immediate;
        bool alt_group, visited, dead_end, ignore, hint, save, restore, 
ignore_alts;
-       bool static_jump_dest, retpoline_safe, branch_target;
+       bool static_jump_dest, retpoline_safe;
+       int branch_target;
        struct symbol *call_dest;
        struct instruction *jump_dest;
        struct list_head alts;


Reply via email to