Subject: objtool: Even more complex static block checks
From: Peter Zijlstra <[email protected]>
Date: Tue Jan 16 20:17:01 CET 2018

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) <[email protected]>
---
 tools/objtool/check.c |   77 ++++++++++++++++++++++++++++++++++++--------------
 tools/objtool/elf.h   |    1 
 2 files changed, 57 insertions(+), 21 deletions(-)

--- a/tools/objtool/check.c
+++ b/tools/objtool/check.c
@@ -1199,36 +1199,71 @@ static int read_retpoline_hints(struct o
        return 0;
 }
 
+static void __grow_static_block(struct instruction *insn, bool *state)
+{
+       if (!*state && !insn->static_jump_dest)
+               return;
+
+       if (insn->static_jump_dest) {
+               *state = true;
+               return;
+       }
+
+       if (insn->branch_target) {
+               *state = false;
+               return;
+
+       } else switch (insn->type) {
+       case INSN_JUMP_CONDITIONAL:
+       case INSN_JUMP_UNCONDITIONAL:
+       case INSN_JUMP_DYNAMIC:
+       case INSN_CALL_DYNAMIC:
+       case INSN_RETURN:
+       case INSN_BUG:
+               *state = false;
+               return;
+       }
+
+       insn->static_jump_dest = *state;
+}
+
 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;
+               __grow_static_block(insn, &static_block);
 
-               if (insn->static_jump_dest) {
-                       static_block = true;
-                       continue;
-               }
+               if (insn->type == INSN_CALL) {
+                       func = insn->call_dest;
 
-               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;
+                       if (static_block)
+                               func->static_call = true;
+                       else
+                               func->non_static_call = true;
                }
+       }
+
+       for_each_sec(file, sec) {
+               list_for_each_entry_safe(func, tmp, &sec->symbol_list, list) {
+                       if (!func->static_call)
+                               continue;
 
-               insn->static_jump_dest = static_block;
+                       if (func->non_static_call)
+                               continue;
+
+                       /* static && !non_static -- only static callers */
+
+                       static_block = true;
+                       func_for_each_insn(file, func, insn) {
+                               __grow_static_block(insn, &static_block);
+                               if (!static_block)
+                                       break;
+                       }
+               }
        }
 
        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