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) --- 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;