[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20180116194917.GH2228@hirez.programming.kicks-ass.net>
Date: Tue, 16 Jan 2018 20:49:17 +0100
From: Peter Zijlstra <peterz@...radead.org>
To: David Woodhouse <dwmw2@...radead.org>,
Josh Poimboeuf <jpoimboe@...hat.com>
Cc: linux-kernel@...r.kernel.org, Dave Hansen <dave.hansen@...el.com>,
Ashok Raj <ashok.raj@...el.com>,
Thomas Gleixner <tglx@...utronix.de>,
Tim Chen <tim.c.chen@...ux.intel.com>,
Andy Lutomirski <luto@...nel.org>,
Linus Torvalds <torvalds@...ux-foundation.org>,
Greg KH <gregkh@...uxfoundation.org>,
Andrea Arcangeli <aarcange@...hat.com>,
Andi Kleen <ak@...ux.intel.com>,
Arjan Van De Ven <arjan.van.de.ven@...el.com>,
Dan Williams <dan.j.williams@...el.com>,
Paolo Bonzini <pbonzini@...hat.com>,
Jun Nakajima <jun.nakajima@...el.com>,
Asit Mallick <asit.k.mallick@...el.com>
Subject: [PATCH v2 11/10] objtool: Even more complex static block checks
Subject: objtool: Even more complex static block checks
From: Peter Zijlstra <peterz@...radead.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@...radead.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 {
Powered by blists - more mailing lists