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


Reply via email to