[PATCH v4 15/22] objtool: Prevent kCFI hashes from being decoded as instructions
Josh Poimboeuf
jpoimboe at kernel.org
Sat Aug 8 16:17:19 PDT 2026
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 <jpoimboe at kernel.org>
---
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
More information about the linux-arm-kernel
mailing list