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 uses mapping symbols to annotate where code ends and data begins
(and vice versa).  Use those to just mark such "instructions" as NOP so
objtool will ignore them.

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

diff --git a/tools/objtool/check.c b/tools/objtool/check.c
index e05dc7a93dc1e..2b03a2d6fc952 100644
--- a/tools/objtool/check.c
+++ b/tools/objtool/check.c
@@ -25,6 +25,7 @@
 #include <linux/kernel.h>
 #include <linux/static_call_types.h>
 #include <linux/string.h>
+#include <linux/kconfig.h>
 
 static unsigned long nr_cfi, nr_cfi_reused, nr_cfi_cache;
 
@@ -428,6 +429,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;
 
@@ -454,6 +457,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));
@@ -478,6 +483,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 d895023674673..9d36b14f420e2 100644
--- a/tools/objtool/include/objtool/elf.h
+++ b/tools/objtool/include/objtool/elf.h
@@ -507,6 +507,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.53.0


Reply via email to