[PATCH 2/4] objtool: Implement jump_assert for _static_cpu_has()
From: Peter Zijlstra
Date: Mon Jan 15 2018 - 11:50:51 EST
Unlike the jump_label bits, static_cpu_has is implemented with
alternatives. Sadly it doesn't readily distinguish itself from any
other alternatives.
Use a heuristic to guess at it :/
But like jump_labels, make static_cpu_has set br_static on the
instructions after the static branch such that we can assert on it.
Cc: Josh Poimboeuf <jpoimboe@xxxxxxxxxx>
Cc: Thomas Gleixner <tglx@xxxxxxxxxxxxx>
Cc: Borislav Petkov <bp@xxxxxxxxx>
Signed-off-by: Peter Zijlstra (Intel) <peterz@xxxxxxxxxxxxx>
---
tools/objtool/check.c | 21 +++++++++++++++++++++
tools/objtool/special.c | 27 ++++++++++++++++++++++++++-
tools/objtool/special.h | 1 +
3 files changed, 48 insertions(+), 1 deletion(-)
--- a/tools/objtool/check.c
+++ b/tools/objtool/check.c
@@ -636,6 +636,12 @@ static int handle_group_alt(struct objto
fake_jump->ignore = true;
if (!special_alt->new_len) {
+ /*
+ * The NOP case for _static_cpu_has()
+ */
+ if (special_alt->static_feat)
+ fake_jump->jump_dest->br_static = true;
+
*new_insn = fake_jump;
return 0;
}
@@ -664,6 +670,21 @@ static int handle_group_alt(struct objto
insn->sec, insn->offset);
return -1;
}
+
+ if (special_alt->static_feat) {
+ if (insn->type != INSN_JUMP_UNCONDITIONAL) {
+ WARN_FUNC("not an unconditional jump in _static_cpu_has()",
+ insn->sec, insn->offset);
+ }
+ if (insn->jump_dest == fake_jump) {
+ WARN_FUNC("jump inside alternative for _static_cpu_has()",
+ insn->sec, insn->offset);
+ }
+ /*
+ * The JMP+disp case for _static_cpu_has()
+ */
+ insn->jump_dest->br_static = true;
+ }
}
if (!last_new_insn) {
--- a/tools/objtool/special.c
+++ b/tools/objtool/special.c
@@ -41,6 +41,7 @@
#define ALT_ORIG_LEN_OFFSET 10
#define ALT_NEW_LEN_OFFSET 11
+#define X86_FEATURE_ALWAYS (3*32+21)
#define X86_FEATURE_POPCNT (4*32+23)
struct special_entry {
@@ -110,6 +111,9 @@ static int get_alt_entry(struct elf *elf
*/
if (feature == X86_FEATURE_POPCNT)
alt->skip_orig = true;
+
+ if (feature == X86_FEATURE_ALWAYS)
+ alt->static_feat = true;
}
orig_rela = find_rela_by_dest(sec, offset + entry->orig);
@@ -155,7 +159,7 @@ int special_get_alts(struct elf *elf, st
struct special_entry *entry;
struct section *sec;
unsigned int nr_entries;
- struct special_alt *alt;
+ struct special_alt *alt, *last = NULL;
int idx, ret;
INIT_LIST_HEAD(alts);
@@ -186,6 +190,27 @@ int special_get_alts(struct elf *elf, st
return ret;
list_add_tail(&alt->list, alts);
+
+ if (last) {
+ /*
+ * If we have two consecutive alternatives for
+ * the same location, for which the first has
+ * FEATURE_ALWAYS set and the second has no
+ * replacement, then assume this is
+ * _static_cpu_has() and will have to set
+ * br_static to make jump_assert work.
+ */
+ if (alt->orig_sec == last->orig_sec &&
+ alt->orig_off == last->orig_off &&
+ alt->orig_len == last->orig_len &&
+ !alt->new_len && !alt->new_sec && !alt->new_off &&
+ last->static_feat)
+ alt->static_feat = true;
+ else
+ last->static_feat = false;
+ }
+
+ last = alt;
}
}
--- a/tools/objtool/special.h
+++ b/tools/objtool/special.h
@@ -27,6 +27,7 @@ struct special_alt {
bool group;
bool skip_orig;
bool jump_or_nop;
+ bool static_feat;
struct section *orig_sec;
unsigned long orig_off;