On arm64 with CONFIG_CFI=y, Clang places a 4-byte kCFI type hash
immediately before each address-taken function entry.  Since these
hashes are in the text section, objtool tries to decode them, leading to
unpredictable results (e.g., ""unannotated intra-function call").

arm64 toolchains use mapping symbols to annotate boundaries between code
and data.  Use those to just mark such "instructions" as NOP so objtool
ignores them.

Signed-off-by: Josh Poimboeuf <[email protected]>
---
 tools/objtool/check.c               | 14 ++++++++++++++
 tools/objtool/include/objtool/elf.h | 23 +++++++++++++++++++++++
 2 files changed, 37 insertions(+)

diff --git a/tools/objtool/check.c b/tools/objtool/check.c
index c034676e1b8c7..79b9e2b5d96de 100644
--- a/tools/objtool/check.c
+++ b/tools/objtool/check.c
@@ -430,6 +430,8 @@ static int decode_instructions(struct objtool_file *file)
 
        for_each_sec(file->elf, sec) {
                struct instruction *insns = NULL;
+               struct symbol *map_sym;
+               bool is_data = false;
                u8 prev_len = 0;
                u8 idx = 0;
 
@@ -456,6 +458,8 @@ static int decode_instructions(struct objtool_file *file)
                if (!strcmp(sec->name, ".init.text") && !opts.module)
                        sec->init = true;
 
+               map_sym = list_first_entry(&sec->symbol_list, struct symbol, 
list);
+
                for (offset = 0; offset < sec_size(sec); offset += insn->len) {
                        if (!insns || idx == INSN_CHUNK_MAX) {
                                insns = calloc(INSN_CHUNK_SIZE, sizeof(*insn));
@@ -480,6 +484,16 @@ static int decode_instructions(struct objtool_file *file)
 
                        prev_len = insn->len;
 
+                       /* Use mapping symbols to skip data in text sections */
+                       sec_for_each_sym_from(sec, map_sym) {
+                               if (map_sym->offset > offset)
+                                       break;
+                               if (is_mapping_sym(map_sym))
+                                       is_data = is_data_mapping_sym(map_sym);
+                       }
+                       if (is_data)
+                               insn->type = INSN_NOP;
+
                        /*
                         * By default, "ud2" is a dead end unless otherwise
                         * annotated, because GCC 7 inserts it for certain
diff --git a/tools/objtool/include/objtool/elf.h 
b/tools/objtool/include/objtool/elf.h
index 5cba96c6392cb..796d154d447b6 100644
--- a/tools/objtool/include/objtool/elf.h
+++ b/tools/objtool/include/objtool/elf.h
@@ -296,6 +296,26 @@ static inline bool is_notype_sym(struct symbol *sym)
        return sym->type == STT_NOTYPE;
 }
 
+/*
+ * ARM64 mapping symbols ($d, $x, $a, __pi_$d, etc) which mark transitions
+ * between code and data.
+ */
+static inline bool is_mapping_sym(struct symbol *sym)
+{
+       return is_notype_sym(sym) && strchr(sym->name, '$');
+}
+
+static inline bool is_data_mapping_sym(struct symbol *sym)
+{
+       const char *dollar;
+
+       if (!is_mapping_sym(sym))
+               return false;
+
+       dollar = strchr(sym->name, '$');
+       return dollar && dollar[1] == 'd';
+}
+
 static inline bool is_global_sym(struct symbol *sym)
 {
        return sym->bind == STB_GLOBAL;
@@ -508,6 +528,9 @@ static inline void set_sym_next_reloc(struct reloc *reloc, 
struct reloc *next)
 #define sec_for_each_sym(sec, sym)                                     \
        list_for_each_entry(sym, &sec->symbol_list, list)
 
+#define sec_for_each_sym_from(sec, sym)                                        
\
+       list_for_each_entry_from(sym, &sec->symbol_list, list)
+
 #define sec_prev_sym(sym)                                              \
        sym->sec && sym->list.prev != &sym->sec->symbol_list ?          \
        list_prev_entry(sym, list) : NULL
-- 
2.54.0


Reply via email to