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

