[PATCH 16/24] objtool: Implement jump_assert for _static_cpu_has()

From: Peter Zijlstra
Date: Tue Jan 23 2018 - 10:40:40 EST


Unlike the jump_label bits, static_cpu_has is implemented with
alternatives. We use the new type field to distinguish them from any
other alternatives

Like jump_labels, make static_cpu_has set static_jump_dest on the
instructions after the static branch such that we can assert on it.

Cc: Thomas Gleixner <tglx@xxxxxxxxxxxxx>
Cc: Borislav Petkov <bp@xxxxxxxxx>
Cc: Josh Poimboeuf <jpoimboe@xxxxxxxxxx>
Signed-off-by: Peter Zijlstra (Intel) <peterz@xxxxxxxxxxxxx>
---
tools/objtool/check.c | 21 +++++++++++++++++++++
tools/objtool/special.c | 26 +++++++++++++++-----------
tools/objtool/special.h | 1 +
3 files changed, 37 insertions(+), 11 deletions(-)

--- 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_cpu_has)
+ fake_jump->jump_dest->static_jump_dest = 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_cpu_has) {
+ 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->static_jump_dest = true;
+ }
}

if (!last_new_insn) {
--- a/tools/objtool/special.c
+++ b/tools/objtool/special.c
@@ -40,6 +40,10 @@
#define ALT_FEATURE_OFFSET 8
#define ALT_ORIG_LEN_OFFSET 10
#define ALT_NEW_LEN_OFFSET 11
+#define ALT_TYPE_OFFSET 13
+
+#define ALT_TYPE_DEFAULT 0
+#define ALT_TYPE_STATIC_CPU_HAS 1

#define X86_FEATURE_POPCNT (4*32+23)

@@ -48,7 +52,6 @@ struct special_entry {
bool group, jump_or_nop;
unsigned char size, orig, new;
unsigned char orig_len, new_len; /* group only */
- unsigned char feature; /* ALTERNATIVE macro CPU feature */
};

struct special_entry entries[] = {
@@ -60,7 +63,6 @@ struct special_entry entries[] = {
.orig_len = ALT_ORIG_LEN_OFFSET,
.new = ALT_NEW_OFFSET,
.new_len = ALT_NEW_LEN_OFFSET,
- .feature = ALT_FEATURE_OFFSET,
},
{
.sec = "__jump_table",
@@ -84,24 +86,23 @@ static int get_alt_entry(struct elf *elf
{
struct rela *orig_rela, *new_rela;
unsigned long offset;
+ void *data;

offset = idx * entry->size;
+ data = sec->data->d_buf + offset;

alt->group = entry->group;
alt->jump_or_nop = entry->jump_or_nop;

if (alt->group) {
- alt->orig_len = *(unsigned char *)(sec->data->d_buf + offset +
- entry->orig_len);
- alt->new_len = *(unsigned char *)(sec->data->d_buf + offset +
- entry->new_len);
- }
-
- if (entry->feature) {
unsigned short feature;
+ unsigned char type;

- feature = *(unsigned short *)(sec->data->d_buf + offset +
- entry->feature);
+ alt->orig_len = *(unsigned char *)(data + entry->orig_len);
+ alt->new_len = *(unsigned char *)(data + entry->new_len);
+
+ feature = *(unsigned short *)(data + ALT_FEATURE_OFFSET);
+ type = *(unsigned char *)(data + ALT_TYPE_OFFSET);

/*
* It has been requested that we don't validate the !POPCNT
@@ -110,6 +111,9 @@ static int get_alt_entry(struct elf *elf
*/
if (feature == X86_FEATURE_POPCNT)
alt->skip_orig = true;
+
+ if (type == ALT_TYPE_STATIC_CPU_HAS)
+ alt->static_cpu_has = true;
}

orig_rela = find_rela_by_dest(sec, offset + entry->orig);
--- 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_cpu_has;

struct section *orig_sec;
unsigned long orig_off;