[PATCH 18/35] objtool: Another static block fail
From: Peter Zijlstra
Date: Thu Jan 18 2018 - 09:58:02 EST
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) <peterz@xxxxxxxxxxxxx>
---
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;