From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1754098AbeARPEl (ORCPT ); Thu, 18 Jan 2018 10:04:41 -0500 Received: from merlin.infradead.org ([205.233.59.134]:46724 "EHLO merlin.infradead.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1755132AbeARO6D (ORCPT ); Thu, 18 Jan 2018 09:58:03 -0500 Message-Id: <20180118140152.477421083@infradead.org> User-Agent: quilt/0.63-1 Date: Thu, 18 Jan 2018 14:48:17 +0100 From: Peter Zijlstra From: Peter Zijlstra To: David Woodhouse , Thomas Gleixner , Josh Poimboeuf Cc: linux-kernel@vger.kernel.org, Dave Hansen , Ashok Raj , Tim Chen , Andy Lutomirski , Linus Torvalds , Greg KH , Andrea Arcangeli , Andi Kleen , Arjan Van De Ven , Dan Williams , Paolo Bonzini , Jun Nakajima , Asit Mallick , Jason Baron , Peter Zijlstra Subject: [PATCH 17/35] objtool: Even more complex static block checks References: <20180118134800.711245485@infradead.org> MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Disposition: inline; filename=peterz-objtool-more-clever.patch Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org 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) --- tools/objtool/check.c | 81 +++++++++++++++++++++++++++++++++++++------------- tools/objtool/elf.h | 1 2 files changed, 61 insertions(+), 21 deletions(-) --- a/tools/objtool/check.c +++ b/tools/objtool/check.c @@ -1207,36 +1207,75 @@ static int read_retpoline_hints(struct o return 0; } +static bool __grow_static_block(struct objtool_file *file, + struct instruction *insn, bool ign_bt) +{ + /* if a jump can come in here, terminate */ + if (insn->branch_target && !ign_bt) + 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, !static_block); - 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->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, false)) + 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 {