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 Cc: Thomas Gleixner Cc: Borislav Petkov Signed-off-by: Peter Zijlstra (Intel) --- 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;