I've observed GCC transform:

  f()
  {
          if (!static_branch_unlikely())
                  return;

          static_assert();
          A;
  }

  g()
  {
          f();
  }

Into:

  f()
  {
          static_assert();
          A;
  }

  g()
  {
          if (static_branch_unlikely())
                  f();
  }

Which results in the assertion landing at f+0. The transformation is
valid and useful; it avoids a pointless CALL+RET sequence, so we'll
have to teach objtool how to deal with this.

Do this by marking all CALL destinations with static_call when called
from a static_block and non_static_call when called outside a
static_block. This allows us to identify functions called exclusively
from a static_block and start them with a static_block.

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

--- a/tools/objtool/check.c
+++ b/tools/objtool/check.c
@@ -1207,36 +1207,75 @@ static int read_retpoline_hints(struct o
        return 0;
 }
 
+static bool __grow_static_block(struct objtool_file *file,
+                               struct instruction *insn, bool ign_bt)
+{
+       /* if a jump can come in here, terminate */
+       if (insn->branch_target && !ign_bt)
+               return false;
+
+       switch (insn->type) {
+       case INSN_JUMP_UNCONDITIONAL:
+               /* mark this instruction, terminate this section */
+               insn->static_jump_dest = true;
+               return false;
+
+       /* these disturb unconditional code flow, terminate */
+       case INSN_JUMP_CONDITIONAL:
+       case INSN_JUMP_DYNAMIC:
+       case INSN_RETURN:
+       case INSN_BUG:
+               return false;
+
+       /* these return right back and don't disturb the code flow */
+       case INSN_CALL:
+       case INSN_CALL_DYNAMIC:
+               break;
+       }
+
+       /* mark this insn, and continue the section */
+       insn->static_jump_dest = true;
+       return true;
+}
+
 static int grow_static_blocks(struct objtool_file *file)
 {
-       struct instruction *insn;
        bool static_block = false;
+       struct symbol *func, *tmp;
+       struct instruction *insn;
+       struct section *sec;
 
        for_each_insn(file, insn) {
-               if (!static_block && !insn->static_jump_dest)
-                       continue;
+               if (static_block || insn->static_jump_dest)
+                       static_block = __grow_static_block(file, insn, 
!static_block);
 
-               if (insn->static_jump_dest) {
-                       static_block = true;
-                       continue;
+               if (insn->type == INSN_CALL) {
+                       func = insn->call_dest;
+                       if (!func)
+                               continue;
+
+                       if (static_block)
+                               func->static_call = true;
+                       else
+                               func->non_static_call = true;
                }
+       }
 
-               if (insn->branch_target) {
-                       static_block = false;
-                       continue;
-               } else switch (insn->type) {
-               case INSN_JUMP_CONDITIONAL:
-               case INSN_JUMP_UNCONDITIONAL:
-               case INSN_JUMP_DYNAMIC:
-               case INSN_CALL:
-               case INSN_CALL_DYNAMIC:
-               case INSN_RETURN:
-               case INSN_BUG:
-                       static_block = false;
-                       continue;
+       for_each_sec(file, sec) {
+               list_for_each_entry_safe(func, tmp, &sec->symbol_list, list) {
+                       if (!func->static_call)
+                               continue;
+
+                       if (func->non_static_call)
+                               continue;
+
+                       /* static && !non_static -- only static callers */
+
+                       func_for_each_insn(file, func, insn) {
+                               if (!__grow_static_block(file, insn, false))
+                                       break;
+                       }
                }
-
-               insn->static_jump_dest = static_block;
        }
 
        return 0;
--- a/tools/objtool/elf.h
+++ b/tools/objtool/elf.h
@@ -61,6 +61,7 @@ struct symbol {
        unsigned char bind, type;
        unsigned long offset;
        unsigned int len;
+       bool static_call, non_static_call;
 };
 
 struct rela {


Reply via email to