Messages in this thread Patch in this message | | | Date | Tue, 16 Jan 2018 20:49:17 +0100 | From | Peter Zijlstra <> | Subject | [PATCH v2 11/10] objtool: Even more complex static block checks |
| |
Subject: objtool: Even more complex static block checks From: Peter Zijlstra <peterz@infradead.org> 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) <peterz@infradead.org> --- 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 {
| |