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 | 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 {