[PATCH 20/24] objtool: Another static block fail
From: Peter Zijlstra
Date: Tue Jan 23 2018 - 10:40:06 EST
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) <peterz@xxxxxxxxxxxxx>
---
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 {