lkml.org 
[lkml]   [2018]   [Jan]   [23]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    Date
    From
    Subject[PATCH 19/24] objtool: Even more complex static block checks
    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.

    Limit to static functions and do not apply recursive.

    Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
    ---
    tools/objtool/check.c | 84 +++++++++++++++++++++++++++++++++++++-------------
    tools/objtool/elf.h | 1
    2 files changed, 64 insertions(+), 21 deletions(-)

    --- a/tools/objtool/check.c
    +++ b/tools/objtool/check.c
    @@ -1207,36 +1207,78 @@ static int assert_static_jumps(struct ob
    return 0;
    }

    +static bool __grow_static_block(struct objtool_file *file,
    + struct instruction *insn)
    +{
    + /* if a !static jump can come in here, terminate */
    + if (insn->branch_target && !insn->static_jump_dest)
    + return false;
    +
    + switch (insn->type) {
    + case INSN_JUMP_UNCONDITIONAL:
    + /* mark this instruction, terminate this section */
    + insn->static_jump_dest = true;
    + return false;
    +
    + /* these disturb unconditional code flow, terminate */
    + case INSN_JUMP_CONDITIONAL:
    + case INSN_JUMP_DYNAMIC:
    + case INSN_RETURN:
    + case INSN_BUG:
    + return false;
    +
    + /* these return right back and don't disturb the code flow */
    + case INSN_CALL:
    + case INSN_CALL_DYNAMIC:
    + break;
    + }
    +
    + /* mark this insn, and continue the section */
    + insn->static_jump_dest = true;
    + return true;
    +}
    +
    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;
    + if (static_block || insn->static_jump_dest)
    + static_block = __grow_static_block(file, insn);

    - if (insn->static_jump_dest) {
    - static_block = true;
    - continue;
    + if (insn->type == INSN_CALL) {
    + func = insn->call_dest;
    + if (!func)
    + continue;
    +
    + if (static_block)
    + func->static_call = true;
    + else
    + func->non_static_call = true;
    }
    + }

    - 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;
    + for_each_sec(file, sec) {
    + list_for_each_entry_safe(func, tmp, &sec->symbol_list, list) {
    + if (func->bind != STB_LOCAL)
    + continue;
    +
    + if (!func->static_call)
    + continue;
    +
    + if (func->non_static_call)
    + continue;
    +
    + /* static && !non_static -- only static callers */
    +
    + func_for_each_insn(file, func, insn) {
    + if (!__grow_static_block(file, insn))
    + break;
    + }
    }
    -
    - insn->static_jump_dest = static_block;
    }

    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 {

    \
     
     \ /
      Last update: 2018-01-23 16:45    [W:4.124 / U:0.016 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site