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 |   53 +++++++++++++++++++++++++++++++++++++++++++++++---
 tools/objtool/elf.h   |    2 -
 2 files changed, 51 insertions(+), 4 deletions(-)

--- a/tools/objtool/check.c
+++ b/tools/objtool/check.c
@@ -1207,6 +1207,8 @@ static int assert_static_jumps(struct ob
        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)
 {
@@ -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);
                insn->static_jump_dest = true;
                return false;
 
@@ -1238,6 +1241,50 @@ static bool __grow_static_block(struct o
        return true;
 }
 
+static void jmp_grow_static_block(struct objtool_file *file, struct 
instruction *insn)
+{
+       struct instruction *dest = insn->jump_dest;
+       bool old_sjd, static_block;
+
+       if (!dest)
+               return;
+
+       /* more than a single site jumps here, can't be certain */
+       if (dest->branch_target > 1)
+               return;
+
+       /* mark the destination, so we can continue there */
+       old_sjd = dest->static_jump_dest;
+       dest->static_jump_dest = true;
+
+       if (dest->offset > insn->offset) {
+               /* fwd jump, the main iteration will still get there. */
+               return;
+       }
+
+       /* backwards jump, we need to revisit the instructions */
+       do {
+               static_block = __grow_static_block(file, dest);
+
+               if (insn->type == INSN_CALL && !old_sjd && 
dest->static_jump_dest) {
+                       struct symbol *func = insn->call_dest;
+                       if (!func || func->bind != STB_LOCAL)
+                               goto next;
+
+                       /*
+                        * we flipped the call to static, update the stats.
+                        */
+                       if (insn->static_jump_dest) {
+                               func->non_static_call--;
+                               func->static_call++;
+                       }
+               }
+next:
+               dest = list_next_entry(dest, list);
+               old_sjd = dest->static_jump_dest;
+       } while (static_block && dest != insn);
+}
+
 static int grow_static_blocks(struct objtool_file *file)
 {
        bool static_block = false;
@@ -1255,9 +1302,9 @@ static int grow_static_blocks(struct obj
                                continue;
 
                        if (static_block)
-                               func->static_call = true;
+                               func->static_call++;
                        else
-                               func->non_static_call = true;
+                               func->non_static_call++;
                }
        }
 
--- a/tools/objtool/elf.h
+++ b/tools/objtool/elf.h
@@ -61,7 +61,7 @@ struct symbol {
        unsigned char bind, type;
        unsigned long offset;
        unsigned int len;
-       bool static_call, non_static_call;
+       unsigned int static_call, non_static_call;
 };
 
 struct rela {


Reply via email to