From ef753d66051ca03bee1982ce047f9eaf90f81ab4 Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Mon, 24 Mar 2025 14:55:51 -0700 Subject: objtool: Fix detection of consecutive jump tables on Clang 20 The jump table detection code assumes jump tables are in the same order as their corresponding indirect branches. That's apparently not always true with Clang 20. Fix that by changing how multiple jump tables are detected. In the first detection pass, mark the beginning of each jump table so the second pass can tell where one ends and the next one begins. Fixes the following warnings: vmlinux.o: warning: objtool: SiS_GetCRT2Ptr+0x1ad: stack state mismatch: cfa1=4+8 cfa2=5+16 sound/core/seq/snd-seq.o: warning: objtool: cc_ev_to_ump_midi2+0x589: return with modified stack frame Fixes: be2f0b1e1264 ("objtool: Get rid of reloc->jump_table_start") Reported-by: kernel test robot Signed-off-by: Josh Poimboeuf Signed-off-by: Ingo Molnar Cc: Nathan Chancellor Cc: Nick Desaulniers Cc: Linus Torvalds Link: https://lore.kernel.org/r/141752fff614eab962dba6bdfaa54aa67ff03bba.1742852846.git.jpoimboe@kernel.org Closes: https://lore.kernel.org/oe-kbuild-all/202503171547.LlCTJLQL-lkp@intel.com/ Closes: https://lore.kernel.org/oe-kbuild-all/202503200535.J3hAvcjw-lkp@intel.com/ --- tools/objtool/check.c | 26 ++++++++------------------ 1 file changed, 8 insertions(+), 18 deletions(-) (limited to 'tools/objtool/check.c') diff --git a/tools/objtool/check.c b/tools/objtool/check.c index ca3435acc326..dae17ed4a5d7 100644 --- a/tools/objtool/check.c +++ b/tools/objtool/check.c @@ -1941,8 +1941,7 @@ __weak unsigned long arch_jump_table_sym_offset(struct reloc *reloc, struct relo return reloc->sym->offset + reloc_addend(reloc); } -static int add_jump_table(struct objtool_file *file, struct instruction *insn, - struct reloc *next_table) +static int add_jump_table(struct objtool_file *file, struct instruction *insn) { unsigned long table_size = insn_jump_table_size(insn); struct symbol *pfunc = insn_func(insn)->pfunc; @@ -1962,7 +1961,7 @@ static int add_jump_table(struct objtool_file *file, struct instruction *insn, /* Check for the end of the table: */ if (table_size && reloc_offset(reloc) - reloc_offset(table) >= table_size) break; - if (reloc != table && reloc == next_table) + if (reloc != table && is_jump_table(reloc)) break; /* Make sure the table entries are consecutive: */ @@ -2053,8 +2052,10 @@ static void find_jump_table(struct objtool_file *file, struct symbol *func, if (!dest_insn || !insn_func(dest_insn) || insn_func(dest_insn)->pfunc != func) continue; + set_jump_table(table_reloc); orig_insn->_jump_table = table_reloc; orig_insn->_jump_table_size = table_size; + break; } } @@ -2096,31 +2097,20 @@ static void mark_func_jump_tables(struct objtool_file *file, static int add_func_jump_tables(struct objtool_file *file, struct symbol *func) { - struct instruction *insn, *insn_t1 = NULL, *insn_t2; - int ret = 0; + struct instruction *insn; + int ret; func_for_each_insn(file, func, insn) { if (!insn_jump_table(insn)) continue; - if (!insn_t1) { - insn_t1 = insn; - continue; - } - - insn_t2 = insn; - ret = add_jump_table(file, insn_t1, insn_jump_table(insn_t2)); + ret = add_jump_table(file, insn); if (ret) return ret; - - insn_t1 = insn_t2; } - if (insn_t1) - ret = add_jump_table(file, insn_t1, NULL); - - return ret; + return 0; } /* -- cgit v1.2.3 From c84301d706c5456b1460439b2987a0f0b6362a82 Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Mon, 24 Mar 2025 14:55:53 -0700 Subject: objtool: Ignore entire functions rather than instructions STACK_FRAME_NON_STANDARD applies to functions. Use a function-specific ignore attribute in preparation for getting rid of insn->ignore. Signed-off-by: Josh Poimboeuf Signed-off-by: Ingo Molnar Cc: Linus Torvalds Link: https://lore.kernel.org/r/4af13376567f83331a9372ae2bb25e11a3d0f055.1742852846.git.jpoimboe@kernel.org --- tools/objtool/check.c | 35 ++++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-) (limited to 'tools/objtool/check.c') diff --git a/tools/objtool/check.c b/tools/objtool/check.c index dae17ed4a5d7..f4650205854d 100644 --- a/tools/objtool/check.c +++ b/tools/objtool/check.c @@ -981,7 +981,6 @@ static int create_direct_call_sections(struct objtool_file *file) */ static void add_ignores(struct objtool_file *file) { - struct instruction *insn; struct section *rsec; struct symbol *func; struct reloc *reloc; @@ -1008,8 +1007,7 @@ static void add_ignores(struct objtool_file *file) continue; } - func_for_each_insn(file, func, insn) - insn->ignore = true; + func->ignore = true; } } @@ -1612,6 +1610,7 @@ static int add_call_destinations(struct objtool_file *file) struct reloc *reloc; for_each_insn(file, insn) { + struct symbol *func = insn_func(insn); if (insn->type != INSN_CALL) continue; @@ -1622,7 +1621,7 @@ static int add_call_destinations(struct objtool_file *file) add_call_dest(file, insn, dest, false); - if (insn->ignore) + if (func && func->ignore) continue; if (!insn_call_dest(insn)) { @@ -1630,7 +1629,7 @@ static int add_call_destinations(struct objtool_file *file) return -1; } - if (insn_func(insn) && insn_call_dest(insn)->type != STT_FUNC) { + if (func && insn_call_dest(insn)->type != STT_FUNC) { WARN_INSN(insn, "unsupported call to non-function"); return -1; } @@ -3470,6 +3469,9 @@ static int validate_branch(struct objtool_file *file, struct symbol *func, u8 visited; int ret; + if (func && func->ignore) + return 0; + sec = insn->sec; while (1) { @@ -3715,7 +3717,7 @@ static int validate_unwind_hint(struct objtool_file *file, struct instruction *insn, struct insn_state *state) { - if (insn->hint && !insn->visited && !insn->ignore) { + if (insn->hint && !insn->visited) { int ret = validate_branch(file, insn_func(insn), insn, *state); if (ret) BT_INSN(insn, "<=== (hint)"); @@ -3929,10 +3931,11 @@ static bool is_ubsan_insn(struct instruction *insn) static bool ignore_unreachable_insn(struct objtool_file *file, struct instruction *insn) { - int i; + struct symbol *func = insn_func(insn); struct instruction *prev_insn; + int i; - if (insn->ignore || insn->type == INSN_NOP || insn->type == INSN_TRAP) + if (insn->ignore || insn->type == INSN_NOP || insn->type == INSN_TRAP || (func && func->ignore)) return true; /* @@ -3951,7 +3954,7 @@ static bool ignore_unreachable_insn(struct objtool_file *file, struct instructio * In this case we'll find a piece of code (whole function) that is not * covered by a !section symbol. Ignore them. */ - if (opts.link && !insn_func(insn)) { + if (opts.link && !func) { int size = find_symbol_hole_containing(insn->sec, insn->offset); unsigned long end = insn->offset + size; @@ -3977,19 +3980,17 @@ static bool ignore_unreachable_insn(struct objtool_file *file, struct instructio */ if (insn->jump_dest && insn_func(insn->jump_dest) && strstr(insn_func(insn->jump_dest)->name, ".cold")) { - struct instruction *dest = insn->jump_dest; - func_for_each_insn(file, insn_func(dest), dest) - dest->ignore = true; + insn_func(insn->jump_dest)->ignore = true; } } return false; } - if (!insn_func(insn)) + if (!func) return false; - if (insn_func(insn)->static_call_tramp) + if (func->static_call_tramp) return true; /* @@ -4020,7 +4021,7 @@ static bool ignore_unreachable_insn(struct objtool_file *file, struct instructio if (insn->type == INSN_JUMP_UNCONDITIONAL) { if (insn->jump_dest && - insn_func(insn->jump_dest) == insn_func(insn)) { + insn_func(insn->jump_dest) == func) { insn = insn->jump_dest; continue; } @@ -4028,7 +4029,7 @@ static bool ignore_unreachable_insn(struct objtool_file *file, struct instructio break; } - if (insn->offset + insn->len >= insn_func(insn)->offset + insn_func(insn)->len) + if (insn->offset + insn->len >= func->offset + func->len) break; insn = next_insn_same_sec(file, insn); @@ -4120,7 +4121,7 @@ static int validate_symbol(struct objtool_file *file, struct section *sec, return 0; insn = find_insn(file, sec, sym->offset); - if (!insn || insn->ignore || insn->visited) + if (!insn || insn->visited) return 0; state->uaccess = sym->uaccess_safe; -- cgit v1.2.3 From 1154bbd326de4453858cf78cf29420888b3ffd52 Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Mon, 24 Mar 2025 14:55:54 -0700 Subject: objtool: Fix X86_FEATURE_SMAP alternative handling For X86_FEATURE_SMAP alternatives which replace NOP with STAC or CLAC, uaccess validation skips the NOP branch to avoid following impossible code paths, e.g. where a STAC would be patched but a CLAC wouldn't. However, it's not safe to assume an X86_FEATURE_SMAP alternative is patching STAC/CLAC. There can be other alternatives, like static_cpu_has(), where both branches need to be validated. Fix that by repurposing ANNOTATE_IGNORE_ALTERNATIVE for skipping either original instructions or new ones. This is a more generic approach which enables the removal of the feature checking hacks and the insn->ignore bit. Fixes the following warnings: arch/x86/mm/fault.o: warning: objtool: do_user_addr_fault+0x8ec: __stack_chk_fail() missing __noreturn in .c/.h or NORETURN() in noreturns.h arch/x86/mm/fault.o: warning: objtool: do_user_addr_fault+0x8f1: unreachable instruction [ mingo: Fix up conflicts with recent x86 changes. ] Fixes: ea24213d8088 ("objtool: Add UACCESS validation") Reported-by: kernel test robot Signed-off-by: Josh Poimboeuf Signed-off-by: Ingo Molnar Cc: Linus Torvalds Link: https://lore.kernel.org/r/de0621ca242130156a55d5d74fed86994dfa4c9c.1742852846.git.jpoimboe@kernel.org Closes: https://lore.kernel.org/oe-kbuild-all/202503181736.zkZUBv4N-lkp@intel.com/ --- tools/objtool/check.c | 39 ++++++++++----------------------------- 1 file changed, 10 insertions(+), 29 deletions(-) (limited to 'tools/objtool/check.c') diff --git a/tools/objtool/check.c b/tools/objtool/check.c index f4650205854d..b2f6a7ff77b5 100644 --- a/tools/objtool/check.c +++ b/tools/objtool/check.c @@ -25,7 +25,6 @@ struct alternative { struct alternative *next; struct instruction *insn; - bool skip_orig; }; static unsigned long nr_cfi, nr_cfi_reused, nr_cfi_cache; @@ -1696,6 +1695,7 @@ static int handle_group_alt(struct objtool_file *file, orig_alt_group->first_insn = orig_insn; orig_alt_group->last_insn = last_orig_insn; orig_alt_group->nop = NULL; + orig_alt_group->ignore = orig_insn->ignore_alts; } else { if (orig_alt_group->last_insn->offset + orig_alt_group->last_insn->len - orig_alt_group->first_insn->offset != special_alt->orig_len) { @@ -1735,7 +1735,6 @@ static int handle_group_alt(struct objtool_file *file, nop->type = INSN_NOP; nop->sym = orig_insn->sym; nop->alt_group = new_alt_group; - nop->ignore = orig_insn->ignore_alts; } if (!special_alt->new_len) { @@ -1752,7 +1751,6 @@ static int handle_group_alt(struct objtool_file *file, last_new_insn = insn; - insn->ignore = orig_insn->ignore_alts; insn->sym = orig_insn->sym; insn->alt_group = new_alt_group; @@ -1799,6 +1797,7 @@ end: new_alt_group->first_insn = *new_insn; new_alt_group->last_insn = last_new_insn; new_alt_group->nop = nop; + new_alt_group->ignore = (*new_insn)->ignore_alts; new_alt_group->cfi = orig_alt_group->cfi; return 0; } @@ -1916,8 +1915,6 @@ static int add_special_section_alts(struct objtool_file *file) } alt->insn = new_insn; - alt->skip_orig = special_alt->skip_orig; - orig_insn->ignore_alts |= special_alt->skip_alt; alt->next = orig_insn->alts; orig_insn->alts = alt; @@ -2295,6 +2292,8 @@ static int read_annotate(struct objtool_file *file, static int __annotate_early(struct objtool_file *file, int type, struct instruction *insn) { switch (type) { + + /* Must be before add_special_section_alts() */ case ANNOTYPE_IGNORE_ALTS: insn->ignore_alts = true; break; @@ -3488,11 +3487,6 @@ static int validate_branch(struct objtool_file *file, struct symbol *func, return 1; } - if (func && insn->ignore) { - WARN_INSN(insn, "BUG: why am I validating an ignored function?"); - return 1; - } - visited = VISITED_BRANCH << state.uaccess; if (insn->visited & VISITED_BRANCH_MASK) { if (!insn->hint && !insn_cfi_match(insn, &state.cfi)) @@ -3564,24 +3558,19 @@ static int validate_branch(struct objtool_file *file, struct symbol *func, if (propagate_alt_cfi(file, insn)) return 1; - if (!insn->ignore_alts && insn->alts) { - bool skip_orig = false; - + if (insn->alts) { for (alt = insn->alts; alt; alt = alt->next) { - if (alt->skip_orig) - skip_orig = true; - ret = validate_branch(file, func, alt->insn, state); if (ret) { BT_INSN(insn, "(alt)"); return ret; } } - - if (skip_orig) - return 0; } + if (insn->alt_group && insn->alt_group->ignore) + return 0; + if (handle_insn_ops(insn, next_insn, &state)) return 1; @@ -3768,23 +3757,15 @@ static int validate_unret(struct objtool_file *file, struct instruction *insn) insn->visited |= VISITED_UNRET; - if (!insn->ignore_alts && insn->alts) { + if (insn->alts) { struct alternative *alt; - bool skip_orig = false; - for (alt = insn->alts; alt; alt = alt->next) { - if (alt->skip_orig) - skip_orig = true; - ret = validate_unret(file, alt->insn); if (ret) { BT_INSN(insn, "(alt)"); return ret; } } - - if (skip_orig) - return 0; } switch (insn->type) { @@ -3935,7 +3916,7 @@ static bool ignore_unreachable_insn(struct objtool_file *file, struct instructio struct instruction *prev_insn; int i; - if (insn->ignore || insn->type == INSN_NOP || insn->type == INSN_TRAP || (func && func->ignore)) + if (insn->type == INSN_NOP || insn->type == INSN_TRAP || (func && func->ignore)) return true; /* -- cgit v1.2.3 From 4fab2d7628dd38f3fa8a5e615199350ecaeb17a8 Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Mon, 24 Mar 2025 14:55:56 -0700 Subject: objtool: Fix init_module() handling If IBT is enabled and a module uses the deprecated init_module() magic function name rather than module_init(fn), its ENDBR will get removed, causing an IBT failure during module load. Objtool does print an obscure warning, but then does nothing to either correct it or return an error. Improve the usefulness of the warning and return an error so it will at least fail the build with CONFIG_OBJTOOL_WERROR. Signed-off-by: Josh Poimboeuf Signed-off-by: Ingo Molnar Cc: Linus Torvalds Link: https://lore.kernel.org/r/366bfdbe92736cde9fb01d5d3eb9b98e9070a1ec.1742852846.git.jpoimboe@kernel.org --- tools/objtool/check.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'tools/objtool/check.c') diff --git a/tools/objtool/check.c b/tools/objtool/check.c index b2f6a7ff77b5..2f7aff1c367d 100644 --- a/tools/objtool/check.c +++ b/tools/objtool/check.c @@ -828,8 +828,11 @@ static int create_ibt_endbr_seal_sections(struct objtool_file *file) if (opts.module && sym && sym->type == STT_FUNC && insn->offset == sym->offset && (!strcmp(sym->name, "init_module") || - !strcmp(sym->name, "cleanup_module"))) - WARN("%s(): not an indirect call target", sym->name); + !strcmp(sym->name, "cleanup_module"))) { + WARN("%s(): Magic init_module() function name is deprecated, use module_init(fn) instead", + sym->name); + return -1; + } if (!elf_init_reloc_text_sym(file->elf, sec, idx * sizeof(int), idx, -- cgit v1.2.3 From 6b023c7842048c4bbeede802f3cf36b96c7a8b25 Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Mon, 24 Mar 2025 14:55:57 -0700 Subject: objtool: Silence more KCOV warnings In the past there were issues with KCOV triggering unreachable instruction warnings, which is why unreachable warnings are now disabled with CONFIG_KCOV. Now some new KCOV warnings are showing up with GCC 14: vmlinux.o: warning: objtool: cpuset_write_resmask() falls through to next function cpuset_update_active_cpus.cold() drivers/usb/core/driver.o: error: objtool: usb_deregister() falls through to next function usb_match_device() sound/soc/codecs/snd-soc-wcd934x.o: warning: objtool: .text.wcd934x_slim_irq_handler: unexpected end of section All are caused by GCC KCOV not finishing an optimization, leaving behind a never-taken conditional branch to a basic block which falls through to the next function (or end of section). At a high level this is similar to the unreachable warnings mentioned above, in that KCOV isn't fully removing dead code. Treat it the same way by adding these to the list of warnings to ignore with CONFIG_KCOV. Reported-by: Ingo Molnar Reported-by: kernel test robot Signed-off-by: Josh Poimboeuf Signed-off-by: Ingo Molnar Cc: Linus Torvalds Link: https://lore.kernel.org/r/66a61a0b65d74e072d3dc02384e395edb2adc3c5.1742852846.git.jpoimboe@kernel.org Closes: https://lore.kernel.org/Z9iTsI09AEBlxlHC@gmail.com Closes: https://lore.kernel.org/oe-kbuild-all/202503180044.oH9gyPeg-lkp@intel.com/ --- tools/objtool/check.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'tools/objtool/check.c') diff --git a/tools/objtool/check.c b/tools/objtool/check.c index 2f7aff1c367d..cb66e6b16841 100644 --- a/tools/objtool/check.c +++ b/tools/objtool/check.c @@ -3485,6 +3485,9 @@ static int validate_branch(struct objtool_file *file, struct symbol *func, !strncmp(func->name, "__pfx_", 6)) return 0; + if (file->ignore_unreachables) + return 0; + WARN("%s() falls through to next function %s()", func->name, insn_func(insn)->name); return 1; @@ -3694,6 +3697,9 @@ static int validate_branch(struct objtool_file *file, struct symbol *func, if (!next_insn) { if (state.cfi.cfa.base == CFI_UNDEFINED) return 0; + if (file->ignore_unreachables) + return 0; + WARN("%s: unexpected end of section", sec->name); return 1; } -- cgit v1.2.3 From e1a9dda74dbffbc3fa2069ff418a1876dc99fb14 Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Mon, 24 Mar 2025 14:55:58 -0700 Subject: objtool: Properly disable uaccess validation If opts.uaccess isn't set, the uaccess validation is disabled, but only partially: it doesn't read the uaccess_safe_builtin list but still tries to do the validation. Disable it completely to prevent false warnings. Signed-off-by: Josh Poimboeuf Signed-off-by: Ingo Molnar Cc: Linus Torvalds Link: https://lore.kernel.org/r/0e95581c1d2107fb5f59418edf2b26bba38b0cbb.1742852846.git.jpoimboe@kernel.org --- tools/objtool/check.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) (limited to 'tools/objtool/check.c') diff --git a/tools/objtool/check.c b/tools/objtool/check.c index cb66e6b16841..b0ef14a5b1ff 100644 --- a/tools/objtool/check.c +++ b/tools/objtool/check.c @@ -3187,7 +3187,7 @@ static int handle_insn_ops(struct instruction *insn, if (update_cfi_state(insn, next_insn, &state->cfi, op)) return 1; - if (!insn->alt_group) + if (!opts.uaccess || !insn->alt_group) continue; if (op->dest.type == OP_DEST_PUSHF) { @@ -3647,6 +3647,9 @@ static int validate_branch(struct objtool_file *file, struct symbol *func, return 0; case INSN_STAC: + if (!opts.uaccess) + break; + if (state.uaccess) { WARN_INSN(insn, "recursive UACCESS enable"); return 1; @@ -3656,6 +3659,9 @@ static int validate_branch(struct objtool_file *file, struct symbol *func, break; case INSN_CLAC: + if (!opts.uaccess) + break; + if (!state.uaccess && func) { WARN_INSN(insn, "redundant UACCESS disable"); return 1; @@ -4114,7 +4120,8 @@ static int validate_symbol(struct objtool_file *file, struct section *sec, if (!insn || insn->visited) return 0; - state->uaccess = sym->uaccess_safe; + if (opts.uaccess) + state->uaccess = sym->uaccess_safe; ret = validate_branch(file, insn_func(insn), insn, *state); if (ret) -- cgit v1.2.3 From c5995abe15476798b2e2f0163a33404c41aafc8f Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Mon, 24 Mar 2025 14:55:59 -0700 Subject: objtool: Improve error handling Fix some error handling issues, improve error messages, properly distinguish betwee errors and warnings, and generally try to make all the error handling more consistent. Signed-off-by: Josh Poimboeuf Signed-off-by: Ingo Molnar Cc: Linus Torvalds Link: https://lore.kernel.org/r/3094bb4463dad29b6bd1bea03848d1571ace771c.1742852846.git.jpoimboe@kernel.org --- tools/objtool/check.c | 368 ++++++++++++++++++++++++++------------------------ 1 file changed, 188 insertions(+), 180 deletions(-) (limited to 'tools/objtool/check.c') diff --git a/tools/objtool/check.c b/tools/objtool/check.c index b0ef14a5b1ff..f4e7ee8e8fb5 100644 --- a/tools/objtool/check.c +++ b/tools/objtool/check.c @@ -353,7 +353,7 @@ static struct cfi_state *cfi_alloc(void) { struct cfi_state *cfi = calloc(1, sizeof(struct cfi_state)); if (!cfi) { - WARN("calloc failed"); + WARN_GLIBC("calloc"); exit(1); } nr_cfi++; @@ -409,7 +409,7 @@ static void *cfi_hash_alloc(unsigned long size) PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON, -1, 0); if (cfi_hash == (void *)-1L) { - WARN("mmap fail cfi_hash"); + WARN_GLIBC("mmap fail cfi_hash"); cfi_hash = NULL; } else if (opts.stats) { printf("cfi_bits: %d\n", cfi_bits); @@ -465,7 +465,7 @@ static int decode_instructions(struct objtool_file *file) if (!insns || idx == INSN_CHUNK_MAX) { insns = calloc(sizeof(*insn), INSN_CHUNK_SIZE); if (!insns) { - WARN("malloc failed"); + WARN_GLIBC("calloc"); return -1; } idx = 0; @@ -567,14 +567,21 @@ static int add_pv_ops(struct objtool_file *file, const char *symname) if (!reloc) break; + idx = (reloc_offset(reloc) - sym->offset) / sizeof(unsigned long); + func = reloc->sym; if (func->type == STT_SECTION) func = find_symbol_by_offset(reloc->sym->sec, reloc_addend(reloc)); + if (!func) { + WARN_FUNC("can't find func at %s[%d]", + reloc->sym->sec, reloc_addend(reloc), + symname, idx); + return -1; + } - idx = (reloc_offset(reloc) - sym->offset) / sizeof(unsigned long); - - objtool_pv_add(file, idx, func); + if (objtool_pv_add(file, idx, func)) + return -1; off = reloc_offset(reloc) + 1; if (off > end) @@ -598,7 +605,7 @@ static int init_pv_ops(struct objtool_file *file) }; const char *pv_ops; struct symbol *sym; - int idx, nr; + int idx, nr, ret; if (!opts.noinstr) return 0; @@ -611,14 +618,19 @@ static int init_pv_ops(struct objtool_file *file) nr = sym->len / sizeof(unsigned long); file->pv_ops = calloc(sizeof(struct pv_state), nr); - if (!file->pv_ops) + if (!file->pv_ops) { + WARN_GLIBC("calloc"); return -1; + } for (idx = 0; idx < nr; idx++) INIT_LIST_HEAD(&file->pv_ops[idx].targets); - for (idx = 0; (pv_ops = pv_ops_tables[idx]); idx++) - add_pv_ops(file, pv_ops); + for (idx = 0; (pv_ops = pv_ops_tables[idx]); idx++) { + ret = add_pv_ops(file, pv_ops); + if (ret) + return ret; + } return 0; } @@ -666,13 +678,12 @@ static int create_static_call_sections(struct objtool_file *file) /* find key symbol */ key_name = strdup(insn_call_dest(insn)->name); if (!key_name) { - perror("strdup"); + WARN_GLIBC("strdup"); return -1; } if (strncmp(key_name, STATIC_CALL_TRAMP_PREFIX_STR, STATIC_CALL_TRAMP_PREFIX_LEN)) { WARN("static_call: trampoline name malformed: %s", key_name); - free(key_name); return -1; } tmp = key_name + STATIC_CALL_TRAMP_PREFIX_LEN - STATIC_CALL_KEY_PREFIX_LEN; @@ -682,7 +693,6 @@ static int create_static_call_sections(struct objtool_file *file) if (!key_sym) { if (!opts.module) { WARN("static_call: can't find static_call_key symbol: %s", tmp); - free(key_name); return -1; } @@ -697,7 +707,6 @@ static int create_static_call_sections(struct objtool_file *file) */ key_sym = insn_call_dest(insn); } - free(key_name); /* populate reloc for 'key' */ if (!elf_init_reloc_data_sym(file->elf, sec, @@ -981,7 +990,7 @@ static int create_direct_call_sections(struct objtool_file *file) /* * Warnings shouldn't be reported for ignored functions. */ -static void add_ignores(struct objtool_file *file) +static int add_ignores(struct objtool_file *file) { struct section *rsec; struct symbol *func; @@ -989,7 +998,7 @@ static void add_ignores(struct objtool_file *file) rsec = find_section_by_name(file->elf, ".rela.discard.func_stack_frame_non_standard"); if (!rsec) - return; + return 0; for_each_reloc(rsec, reloc) { switch (reloc->sym->type) { @@ -1006,11 +1015,13 @@ static void add_ignores(struct objtool_file *file) default: WARN("unexpected relocation symbol type in %s: %d", rsec->name, reloc->sym->type); - continue; + return -1; } func->ignore = true; } + + return 0; } /* @@ -1275,7 +1286,7 @@ static void remove_insn_ops(struct instruction *insn) insn->stack_ops = NULL; } -static void annotate_call_site(struct objtool_file *file, +static int annotate_call_site(struct objtool_file *file, struct instruction *insn, bool sibling) { struct reloc *reloc = insn_reloc(file, insn); @@ -1286,12 +1297,12 @@ static void annotate_call_site(struct objtool_file *file, if (sym->static_call_tramp) { list_add_tail(&insn->call_node, &file->static_call_list); - return; + return 0; } if (sym->retpoline_thunk) { list_add_tail(&insn->call_node, &file->retpoline_call_list); - return; + return 0; } /* @@ -1303,10 +1314,12 @@ static void annotate_call_site(struct objtool_file *file, if (reloc) set_reloc_type(file->elf, reloc, R_NONE); - elf_write_insn(file->elf, insn->sec, - insn->offset, insn->len, - sibling ? arch_ret_insn(insn->len) - : arch_nop_insn(insn->len)); + if (elf_write_insn(file->elf, insn->sec, + insn->offset, insn->len, + sibling ? arch_ret_insn(insn->len) + : arch_nop_insn(insn->len))) { + return -1; + } insn->type = sibling ? INSN_RETURN : INSN_NOP; @@ -1320,7 +1333,7 @@ static void annotate_call_site(struct objtool_file *file, insn->retpoline_safe = true; } - return; + return 0; } if (opts.mcount && sym->fentry) { @@ -1330,15 +1343,17 @@ static void annotate_call_site(struct objtool_file *file, if (reloc) set_reloc_type(file->elf, reloc, R_NONE); - elf_write_insn(file->elf, insn->sec, - insn->offset, insn->len, - arch_nop_insn(insn->len)); + if (elf_write_insn(file->elf, insn->sec, + insn->offset, insn->len, + arch_nop_insn(insn->len))) { + return -1; + } insn->type = INSN_NOP; } list_add_tail(&insn->call_node, &file->mcount_loc_list); - return; + return 0; } if (insn->type == INSN_CALL && !insn->sec->init && @@ -1347,14 +1362,16 @@ static void annotate_call_site(struct objtool_file *file, if (!sibling && dead_end_function(file, sym)) insn->dead_end = true; + + return 0; } -static void add_call_dest(struct objtool_file *file, struct instruction *insn, +static int add_call_dest(struct objtool_file *file, struct instruction *insn, struct symbol *dest, bool sibling) { insn->_call_dest = dest; if (!dest) - return; + return 0; /* * Whatever stack impact regular CALLs have, should be undone @@ -1365,10 +1382,10 @@ static void add_call_dest(struct objtool_file *file, struct instruction *insn, */ remove_insn_ops(insn); - annotate_call_site(file, insn, sibling); + return annotate_call_site(file, insn, sibling); } -static void add_retpoline_call(struct objtool_file *file, struct instruction *insn) +static int add_retpoline_call(struct objtool_file *file, struct instruction *insn) { /* * Retpoline calls/jumps are really dynamic calls/jumps in disguise, @@ -1385,7 +1402,7 @@ static void add_retpoline_call(struct objtool_file *file, struct instruction *in insn->type = INSN_JUMP_DYNAMIC_CONDITIONAL; break; default: - return; + return 0; } insn->retpoline_safe = true; @@ -1399,7 +1416,7 @@ static void add_retpoline_call(struct objtool_file *file, struct instruction *in */ remove_insn_ops(insn); - annotate_call_site(file, insn, false); + return annotate_call_site(file, insn, false); } static void add_return_call(struct objtool_file *file, struct instruction *insn, bool add) @@ -1468,6 +1485,7 @@ static int add_jump_destinations(struct objtool_file *file) struct reloc *reloc; struct section *dest_sec; unsigned long dest_off; + int ret; for_each_insn(file, insn) { if (insn->jump_dest) { @@ -1488,7 +1506,9 @@ static int add_jump_destinations(struct objtool_file *file) dest_sec = reloc->sym->sec; dest_off = arch_dest_reloc_offset(reloc_addend(reloc)); } else if (reloc->sym->retpoline_thunk) { - add_retpoline_call(file, insn); + ret = add_retpoline_call(file, insn); + if (ret) + return ret; continue; } else if (reloc->sym->return_thunk) { add_return_call(file, insn, true); @@ -1498,7 +1518,9 @@ static int add_jump_destinations(struct objtool_file *file) * External sibling call or internal sibling call with * STT_FUNC reloc. */ - add_call_dest(file, insn, reloc->sym, true); + ret = add_call_dest(file, insn, reloc->sym, true); + if (ret) + return ret; continue; } else if (reloc->sym->sec->idx) { dest_sec = reloc->sym->sec; @@ -1538,7 +1560,9 @@ static int add_jump_destinations(struct objtool_file *file) */ if (jump_dest->sym && jump_dest->offset == jump_dest->sym->offset) { if (jump_dest->sym->retpoline_thunk) { - add_retpoline_call(file, insn); + ret = add_retpoline_call(file, insn); + if (ret) + return ret; continue; } if (jump_dest->sym->return_thunk) { @@ -1580,7 +1604,9 @@ static int add_jump_destinations(struct objtool_file *file) * Internal sibling call without reloc or with * STT_SECTION reloc. */ - add_call_dest(file, insn, insn_func(jump_dest), true); + ret = add_call_dest(file, insn, insn_func(jump_dest), true); + if (ret) + return ret; continue; } @@ -1610,6 +1636,7 @@ static int add_call_destinations(struct objtool_file *file) unsigned long dest_off; struct symbol *dest; struct reloc *reloc; + int ret; for_each_insn(file, insn) { struct symbol *func = insn_func(insn); @@ -1621,7 +1648,9 @@ static int add_call_destinations(struct objtool_file *file) dest_off = arch_jump_destination(insn); dest = find_call_destination(insn->sec, dest_off); - add_call_dest(file, insn, dest, false); + ret = add_call_dest(file, insn, dest, false); + if (ret) + return ret; if (func && func->ignore) continue; @@ -1645,13 +1674,20 @@ static int add_call_destinations(struct objtool_file *file) return -1; } - add_call_dest(file, insn, dest, false); + ret = add_call_dest(file, insn, dest, false); + if (ret) + return ret; } else if (reloc->sym->retpoline_thunk) { - add_retpoline_call(file, insn); + ret = add_retpoline_call(file, insn); + if (ret) + return ret; - } else - add_call_dest(file, insn, reloc->sym, false); + } else { + ret = add_call_dest(file, insn, reloc->sym, false); + if (ret) + return ret; + } } return 0; @@ -1674,15 +1710,15 @@ static int handle_group_alt(struct objtool_file *file, if (!orig_alt_group) { struct instruction *last_orig_insn = NULL; - orig_alt_group = malloc(sizeof(*orig_alt_group)); + orig_alt_group = calloc(1, sizeof(*orig_alt_group)); if (!orig_alt_group) { - WARN("malloc failed"); + WARN_GLIBC("calloc"); return -1; } orig_alt_group->cfi = calloc(special_alt->orig_len, sizeof(struct cfi_state *)); if (!orig_alt_group->cfi) { - WARN("calloc failed"); + WARN_GLIBC("calloc"); return -1; } @@ -1711,9 +1747,9 @@ static int handle_group_alt(struct objtool_file *file, } } - new_alt_group = malloc(sizeof(*new_alt_group)); + new_alt_group = calloc(1, sizeof(*new_alt_group)); if (!new_alt_group) { - WARN("malloc failed"); + WARN_GLIBC("calloc"); return -1; } @@ -1725,9 +1761,9 @@ static int handle_group_alt(struct objtool_file *file, * instruction affects the stack, the instruction after it (the * nop) will propagate the new state to the shared CFI array. */ - nop = malloc(sizeof(*nop)); + nop = calloc(1, sizeof(*nop)); if (!nop) { - WARN("malloc failed"); + WARN_GLIBC("calloc"); return -1; } memset(nop, 0, sizeof(*nop)); @@ -1827,9 +1863,13 @@ static int handle_jump_alt(struct objtool_file *file, if (reloc) set_reloc_type(file->elf, reloc, R_NONE); - elf_write_insn(file->elf, orig_insn->sec, - orig_insn->offset, orig_insn->len, - arch_nop_insn(orig_insn->len)); + + if (elf_write_insn(file->elf, orig_insn->sec, + orig_insn->offset, orig_insn->len, + arch_nop_insn(orig_insn->len))) { + return -1; + } + orig_insn->type = INSN_NOP; } @@ -1865,9 +1905,8 @@ static int add_special_section_alts(struct objtool_file *file) struct alternative *alt; int ret; - ret = special_get_alts(file->elf, &special_alts); - if (ret) - return ret; + if (special_get_alts(file->elf, &special_alts)) + return -1; list_for_each_entry_safe(special_alt, tmp, &special_alts, list) { @@ -1876,8 +1915,7 @@ static int add_special_section_alts(struct objtool_file *file) if (!orig_insn) { WARN_FUNC("special: can't find orig instruction", special_alt->orig_sec, special_alt->orig_off); - ret = -1; - goto out; + return -1; } new_insn = NULL; @@ -1888,8 +1926,7 @@ static int add_special_section_alts(struct objtool_file *file) WARN_FUNC("special: can't find new instruction", special_alt->new_sec, special_alt->new_off); - ret = -1; - goto out; + return -1; } } @@ -1902,19 +1939,19 @@ static int add_special_section_alts(struct objtool_file *file) ret = handle_group_alt(file, special_alt, orig_insn, &new_insn); if (ret) - goto out; + return ret; + } else if (special_alt->jump_or_nop) { ret = handle_jump_alt(file, special_alt, orig_insn, &new_insn); if (ret) - goto out; + return ret; } - alt = malloc(sizeof(*alt)); + alt = calloc(1, sizeof(*alt)); if (!alt) { - WARN("malloc failed"); - ret = -1; - goto out; + WARN_GLIBC("calloc"); + return -1; } alt->insn = new_insn; @@ -1931,8 +1968,7 @@ static int add_special_section_alts(struct objtool_file *file) printf("long:\t%ld\t%ld\n", file->jl_nop_long, file->jl_long); } -out: - return ret; + return 0; } __weak unsigned long arch_jump_table_sym_offset(struct reloc *reloc, struct reloc *table) @@ -1989,9 +2025,9 @@ static int add_jump_table(struct objtool_file *file, struct instruction *insn) if (!insn_func(dest_insn) || insn_func(dest_insn)->pfunc != pfunc) break; - alt = malloc(sizeof(*alt)); + alt = calloc(1, sizeof(*alt)); if (!alt) { - WARN("malloc failed"); + WARN_GLIBC("calloc"); return -1; } @@ -2039,7 +2075,7 @@ static void find_jump_table(struct objtool_file *file, struct symbol *func, insn->jump_dest && (insn->jump_dest->offset <= insn->offset || insn->jump_dest->offset > orig_insn->offset)) - break; + break; table_reloc = arch_find_switch_table(file, insn, &table_size); if (!table_reloc) @@ -2103,7 +2139,6 @@ static int add_func_jump_tables(struct objtool_file *file, if (!insn_jump_table(insn)) continue; - ret = add_jump_table(file, insn); if (ret) return ret; @@ -2221,6 +2256,7 @@ static int read_unwind_hints(struct objtool_file *file) if (sym && sym->bind == STB_GLOBAL) { if (opts.ibt && insn->type != INSN_ENDBR && !insn->noendbr) { WARN_INSN(insn, "UNWIND_HINT_IRET_REGS without ENDBR"); + return -1; } } } @@ -2390,7 +2426,7 @@ static int __annotate_late(struct objtool_file *file, int type, struct instructi default: WARN_INSN(insn, "Unknown annotation type: %d", type); - break; + return -1; } return 0; @@ -2503,7 +2539,10 @@ static int decode_sections(struct objtool_file *file) if (ret) return ret; - add_ignores(file); + ret = add_ignores(file); + if (ret) + return ret; + add_uaccess_safe(file); ret = read_annotate(file, __annotate_early); @@ -2723,7 +2762,7 @@ static int update_cfi_state(struct instruction *insn, if (cfa->base == CFI_UNDEFINED) { if (insn_func(insn)) { WARN_INSN(insn, "undefined stack state"); - return -1; + return 1; } return 0; } @@ -3166,9 +3205,8 @@ static int propagate_alt_cfi(struct objtool_file *file, struct instruction *insn if (cficmp(alt_cfi[group_off], insn->cfi)) { struct alt_group *orig_group = insn->alt_group->orig_group ?: insn->alt_group; struct instruction *orig = orig_group->first_insn; - char *where = offstr(insn->sec, insn->offset); - WARN_INSN(orig, "stack layout conflict in alternatives: %s", where); - free(where); + WARN_INSN(orig, "stack layout conflict in alternatives: %s", + offstr(insn->sec, insn->offset)); return -1; } } @@ -3181,11 +3219,13 @@ static int handle_insn_ops(struct instruction *insn, struct insn_state *state) { struct stack_op *op; + int ret; for (op = insn->stack_ops; op; op = op->next) { - if (update_cfi_state(insn, next_insn, &state->cfi, op)) - return 1; + ret = update_cfi_state(insn, next_insn, &state->cfi, op); + if (ret) + return ret; if (!opts.uaccess || !insn->alt_group) continue; @@ -3229,36 +3269,41 @@ static bool insn_cfi_match(struct instruction *insn, struct cfi_state *cfi2) WARN_INSN(insn, "stack state mismatch: cfa1=%d%+d cfa2=%d%+d", cfi1->cfa.base, cfi1->cfa.offset, cfi2->cfa.base, cfi2->cfa.offset); + return false; - } else if (memcmp(&cfi1->regs, &cfi2->regs, sizeof(cfi1->regs))) { + } + + if (memcmp(&cfi1->regs, &cfi2->regs, sizeof(cfi1->regs))) { for (i = 0; i < CFI_NUM_REGS; i++) { - if (!memcmp(&cfi1->regs[i], &cfi2->regs[i], - sizeof(struct cfi_reg))) + + if (!memcmp(&cfi1->regs[i], &cfi2->regs[i], sizeof(struct cfi_reg))) continue; WARN_INSN(insn, "stack state mismatch: reg1[%d]=%d%+d reg2[%d]=%d%+d", i, cfi1->regs[i].base, cfi1->regs[i].offset, i, cfi2->regs[i].base, cfi2->regs[i].offset); - break; } + return false; + } - } else if (cfi1->type != cfi2->type) { + if (cfi1->type != cfi2->type) { WARN_INSN(insn, "stack state mismatch: type1=%d type2=%d", cfi1->type, cfi2->type); + return false; + } - } else if (cfi1->drap != cfi2->drap || + if (cfi1->drap != cfi2->drap || (cfi1->drap && cfi1->drap_reg != cfi2->drap_reg) || (cfi1->drap && cfi1->drap_offset != cfi2->drap_offset)) { WARN_INSN(insn, "stack state mismatch: drap1=%d(%d,%d) drap2=%d(%d,%d)", cfi1->drap, cfi1->drap_reg, cfi1->drap_offset, cfi2->drap, cfi2->drap_reg, cfi2->drap_offset); + return false; + } - } else - return true; - - return false; + return true; } static inline bool func_uaccess_safe(struct symbol *func) @@ -3490,6 +3535,8 @@ static int validate_branch(struct objtool_file *file, struct symbol *func, WARN("%s() falls through to next function %s()", func->name, insn_func(insn)->name); + func->warnings++; + return 1; } @@ -3597,9 +3644,6 @@ static int validate_branch(struct objtool_file *file, struct symbol *func, return 1; } - if (insn->dead_end) - return 0; - break; case INSN_JUMP_CONDITIONAL: @@ -3706,7 +3750,9 @@ static int validate_branch(struct objtool_file *file, struct symbol *func, if (file->ignore_unreachables) return 0; - WARN("%s: unexpected end of section", sec->name); + WARN("%s%sunexpected end of section %s", + func ? func->name : "", func ? ": " : "", + sec->name); return 1; } @@ -3796,7 +3842,7 @@ static int validate_unret(struct objtool_file *file, struct instruction *insn) if (!is_sibling_call(insn)) { if (!insn->jump_dest) { WARN_INSN(insn, "unresolved jump target after linking?!?"); - return -1; + return 1; } ret = validate_unret(file, insn->jump_dest); if (ret) { @@ -3818,7 +3864,7 @@ static int validate_unret(struct objtool_file *file, struct instruction *insn) if (!dest) { WARN("Unresolved function after linking!?: %s", insn_call_dest(insn)->name); - return -1; + return 1; } ret = validate_unret(file, dest); @@ -3847,7 +3893,7 @@ static int validate_unret(struct objtool_file *file, struct instruction *insn) if (!next) { WARN_INSN(insn, "teh end!"); - return -1; + return 1; } insn = next; } @@ -3862,18 +3908,13 @@ static int validate_unret(struct objtool_file *file, struct instruction *insn) static int validate_unrets(struct objtool_file *file) { struct instruction *insn; - int ret, warnings = 0; + int warnings = 0; for_each_insn(file, insn) { if (!insn->unret) continue; - ret = validate_unret(file, insn); - if (ret < 0) { - WARN_INSN(insn, "Failed UNRET validation"); - return ret; - } - warnings += ret; + warnings += validate_unret(file, insn); } return warnings; @@ -3899,13 +3940,13 @@ static int validate_retpoline(struct objtool_file *file) if (insn->type == INSN_RETURN) { if (opts.rethunk) { WARN_INSN(insn, "'naked' return found in MITIGATION_RETHUNK build"); - } else - continue; - } else { - WARN_INSN(insn, "indirect %s found in MITIGATION_RETPOLINE build", - insn->type == INSN_JUMP_DYNAMIC ? "jump" : "call"); + warnings++; + } + continue; } + WARN_INSN(insn, "indirect %s found in MITIGATION_RETPOLINE build", + insn->type == INSN_JUMP_DYNAMIC ? "jump" : "call"); warnings++; } @@ -4472,7 +4513,7 @@ static int validate_reachable_instructions(struct objtool_file *file) } /* 'funcs' is a space-separated list of function names */ -static int disas_funcs(const char *funcs) +static void disas_funcs(const char *funcs) { const char *objdump_str, *cross_compile; int size, ret; @@ -4505,7 +4546,7 @@ static int disas_funcs(const char *funcs) size = snprintf(NULL, 0, objdump_str, cross_compile, objname, funcs) + 1; if (size <= 0) { WARN("objdump string size calculation failed"); - return -1; + return; } cmd = malloc(size); @@ -4515,13 +4556,11 @@ static int disas_funcs(const char *funcs) ret = system(cmd); if (ret) { WARN("disassembly failed: %d", ret); - return -1; + return; } - - return 0; } -static int disas_warned_funcs(struct objtool_file *file) +static void disas_warned_funcs(struct objtool_file *file) { struct symbol *sym; char *funcs = NULL, *tmp; @@ -4530,9 +4569,17 @@ static int disas_warned_funcs(struct objtool_file *file) if (sym->warnings) { if (!funcs) { funcs = malloc(strlen(sym->name) + 1); + if (!funcs) { + WARN_GLIBC("malloc"); + return; + } strcpy(funcs, sym->name); } else { tmp = malloc(strlen(funcs) + strlen(sym->name) + 2); + if (!tmp) { + WARN_GLIBC("malloc"); + return; + } sprintf(tmp, "%s %s", funcs, sym->name); free(funcs); funcs = tmp; @@ -4542,8 +4589,6 @@ static int disas_warned_funcs(struct objtool_file *file) if (funcs) disas_funcs(funcs); - - return 0; } struct insn_chunk { @@ -4576,7 +4621,7 @@ static void free_insns(struct objtool_file *file) int check(struct objtool_file *file) { - int ret, warnings = 0; + int ret = 0, warnings = 0; arch_initial_func_cfi_state(&initial_func_cfi); init_cfi_state(&init_cfi); @@ -4594,44 +4639,27 @@ int check(struct objtool_file *file) cfi_hash_add(&func_cfi); ret = decode_sections(file); - if (ret < 0) + if (ret) goto out; - warnings += ret; - if (!nr_insns) goto out; - if (opts.retpoline) { - ret = validate_retpoline(file); - if (ret < 0) - goto out; - warnings += ret; - } + if (opts.retpoline) + warnings += validate_retpoline(file); if (opts.stackval || opts.orc || opts.uaccess) { - ret = validate_functions(file); - if (ret < 0) - goto out; - warnings += ret; + int w = 0; - ret = validate_unwind_hints(file, NULL); - if (ret < 0) - goto out; - warnings += ret; + w += validate_functions(file); + w += validate_unwind_hints(file, NULL); + if (!w) + w += validate_reachable_instructions(file); - if (!warnings) { - ret = validate_reachable_instructions(file); - if (ret < 0) - goto out; - warnings += ret; - } + warnings += w; } else if (opts.noinstr) { - ret = validate_noinstr_sections(file); - if (ret < 0) - goto out; - warnings += ret; + warnings += validate_noinstr_sections(file); } if (opts.unret) { @@ -4639,87 +4667,67 @@ int check(struct objtool_file *file) * Must be after validate_branch() and friends, it plays * further games with insn->visited. */ - ret = validate_unrets(file); - if (ret < 0) - goto out; - warnings += ret; + warnings += validate_unrets(file); } - if (opts.ibt) { - ret = validate_ibt(file); - if (ret < 0) - goto out; - warnings += ret; - } + if (opts.ibt) + warnings += validate_ibt(file); - if (opts.sls) { - ret = validate_sls(file); - if (ret < 0) - goto out; - warnings += ret; - } + if (opts.sls) + warnings += validate_sls(file); if (opts.static_call) { ret = create_static_call_sections(file); - if (ret < 0) + if (ret) goto out; - warnings += ret; } if (opts.retpoline) { ret = create_retpoline_sites_sections(file); - if (ret < 0) + if (ret) goto out; - warnings += ret; } if (opts.cfi) { ret = create_cfi_sections(file); - if (ret < 0) + if (ret) goto out; - warnings += ret; } if (opts.rethunk) { ret = create_return_sites_sections(file); - if (ret < 0) + if (ret) goto out; - warnings += ret; if (opts.hack_skylake) { ret = create_direct_call_sections(file); - if (ret < 0) + if (ret) goto out; - warnings += ret; } } if (opts.mcount) { ret = create_mcount_loc_sections(file); - if (ret < 0) + if (ret) goto out; - warnings += ret; } if (opts.prefix) { ret = add_prefix_symbols(file); - if (ret < 0) + if (ret) goto out; - warnings += ret; } if (opts.ibt) { ret = create_ibt_endbr_seal_sections(file); - if (ret < 0) + if (ret) goto out; - warnings += ret; } if (opts.orc && nr_insns) { ret = orc_create(file); - if (ret < 0) + if (ret) goto out; - warnings += ret; } free_insns(file); -- cgit v1.2.3 From d39f82a058c0269392a797cb04f2a6064e5dcab6 Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Mon, 24 Mar 2025 14:56:00 -0700 Subject: objtool: Reduce CONFIG_OBJTOOL_WERROR verbosity Remove the following from CONFIG_OBJTOOL_WERROR: * backtrace * "upgraded warnings to errors" message * cmdline args This makes the default output less cluttered and makes it easier to spot the actual warnings. Note the above options are still are available with --verbose or OBJTOOL_VERBOSE=1. Also, do the cmdline arg printing on all warnings, regardless of werror. Signed-off-by: Josh Poimboeuf Signed-off-by: Ingo Molnar Cc: Linus Torvalds Link: https://lore.kernel.org/r/d61df69f64b396fa6b2a1335588aad7a34ea9e71.1742852846.git.jpoimboe@kernel.org --- tools/objtool/check.c | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) (limited to 'tools/objtool/check.c') diff --git a/tools/objtool/check.c b/tools/objtool/check.c index f4e7ee8e8fb5..ac21f2846ebc 100644 --- a/tools/objtool/check.c +++ b/tools/objtool/check.c @@ -4732,9 +4732,6 @@ int check(struct objtool_file *file) free_insns(file); - if (opts.verbose) - disas_warned_funcs(file); - if (opts.stats) { printf("nr_insns_visited: %ld\n", nr_insns_visited); printf("nr_cfi: %ld\n", nr_cfi); @@ -4743,19 +4740,25 @@ int check(struct objtool_file *file) } out: + if (!ret && !warnings) + return 0; + + if (opts.verbose) { + if (opts.werror && warnings) + WARN("%d warning(s) upgraded to errors", warnings); + print_args(); + disas_warned_funcs(file); + } + /* * CONFIG_OBJTOOL_WERROR upgrades all warnings (and errors) to actual * errors. * - * Note that even "fatal" type errors don't actually return an error - * without CONFIG_OBJTOOL_WERROR. That probably needs improved at some - * point. + * Note that even fatal errors don't yet actually return an error + * without CONFIG_OBJTOOL_WERROR. That will be fixed soon-ish. */ - if (opts.werror && (ret || warnings)) { - if (warnings) - WARN("%d warning(s) upgraded to errors", warnings); + if (opts.werror) return 1; - } return 0; } -- cgit v1.2.3 From a8d39a62c6f5ad76b8a1ebfbf10dd9fe8ca2bbcc Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Mon, 24 Mar 2025 14:56:03 -0700 Subject: objtool: Remove redundant opts.noinstr dependency The --noinstr dependecy on --link is already enforced in the cmdline arg parsing code. Remove the redundant check. Signed-off-by: Josh Poimboeuf Signed-off-by: Ingo Molnar Cc: Linus Torvalds Link: https://lore.kernel.org/r/0ead7ffa0f5be2e81aebbcc585e07b2c98702b44.1742852847.git.jpoimboe@kernel.org --- tools/objtool/check.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) (limited to 'tools/objtool/check.c') diff --git a/tools/objtool/check.c b/tools/objtool/check.c index ac21f2846ebc..0caabf0e8faf 100644 --- a/tools/objtool/check.c +++ b/tools/objtool/check.c @@ -340,12 +340,7 @@ static void init_insn_state(struct objtool_file *file, struct insn_state *state, memset(state, 0, sizeof(*state)); init_cfi_state(&state->cfi); - /* - * We need the full vmlinux for noinstr validation, otherwise we can - * not correctly determine insn_call_dest(insn)->sec (external symbols - * do not have a section). - */ - if (opts.link && opts.noinstr && sec) + if (opts.noinstr && sec) state->noinstr = sec->noinstr; } -- cgit v1.2.3 From 72c774aa9d1e16bfd247096935e7dae194d84929 Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Mon, 24 Mar 2025 14:56:07 -0700 Subject: objtool, panic: Disable SMAP in __stack_chk_fail() __stack_chk_fail() can be called from uaccess-enabled code. Make sure uaccess gets disabled before calling panic(). Fixes the following warning: kernel/trace/trace_branch.o: error: objtool: ftrace_likely_update+0x1ea: call to __stack_chk_fail() with UACCESS enabled Signed-off-by: Josh Poimboeuf Signed-off-by: Ingo Molnar Cc: Kees Cook Cc: Andrew Morton Cc: Linus Torvalds Link: https://lore.kernel.org/r/a3e97e0119e1b04c725a8aa05f7bc83d98e657eb.1742852847.git.jpoimboe@kernel.org --- tools/objtool/check.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'tools/objtool/check.c') diff --git a/tools/objtool/check.c b/tools/objtool/check.c index 0caabf0e8faf..3bf29923d5c0 100644 --- a/tools/objtool/check.c +++ b/tools/objtool/check.c @@ -1194,12 +1194,15 @@ static const char *uaccess_safe_builtin[] = { "__ubsan_handle_load_invalid_value", /* STACKLEAK */ "stackleak_track_stack", + /* TRACE_BRANCH_PROFILING */ + "ftrace_likely_update", + /* STACKPROTECTOR */ + "__stack_chk_fail", /* misc */ "csum_partial_copy_generic", "copy_mc_fragile", "copy_mc_fragile_handle_tail", "copy_mc_enhanced_fast_string", - "ftrace_likely_update", /* CONFIG_TRACE_BRANCH_PROFILING */ "rep_stos_alternative", "rep_movs_alternative", "__copy_user_nocache", -- cgit v1.2.3 From 69d41d6dafff0967565b971d950bd10443e4076c Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Thu, 27 Mar 2025 22:04:21 -0700 Subject: objtool: Fix segfault in ignore_unreachable_insn() Check 'prev_insn' before dereferencing it. Fixes: bd841d6154f5 ("objtool: Fix CONFIG_UBSAN_TRAP unreachable warnings") Reported-by: Arnd Bergmann Reported-by: Ingo Molnar Signed-off-by: Josh Poimboeuf Signed-off-by: Ingo Molnar Link: https://lore.kernel.org/r/5df4ff89c9e4b9e788b77b0531234ffa7ba03e9e.1743136205.git.jpoimboe@kernel.org Closes: https://lore.kernel.org/d86b4cc6-0b97-4095-8793-a7384410b8ab@app.fastmail.com Closes: https://lore.kernel.org/Z-V_rruKY0-36pqA@gmail.com --- tools/objtool/check.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tools/objtool/check.c') diff --git a/tools/objtool/check.c b/tools/objtool/check.c index 3bf29923d5c0..29de1709ea00 100644 --- a/tools/objtool/check.c +++ b/tools/objtool/check.c @@ -4037,7 +4037,7 @@ static bool ignore_unreachable_insn(struct objtool_file *file, struct instructio * It may also insert a UD2 after calling a __noreturn function. */ prev_insn = prev_insn_same_sec(file, insn); - if (prev_insn->dead_end && + if (prev_insn && prev_insn->dead_end && (insn->type == INSN_BUG || (insn->type == INSN_JUMP_UNCONDITIONAL && insn->jump_dest && insn->jump_dest->type == INSN_BUG))) -- cgit v1.2.3 From b5e2cc57f551a1a1e2c0ea36f77c1e26d3d13c35 Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Thu, 27 Mar 2025 22:04:22 -0700 Subject: objtool: Fix STACK_FRAME_NON_STANDARD for cold subfunctions The recent STACK_FRAME_NON_STANDARD refactoring forgot about .cold subfunctions. They must also be ignored. Fixes the following warning: drivers/gpu/drm/vmwgfx/vmwgfx_msg.o: warning: objtool: vmw_recv_msg.cold+0x0: unreachable instruction Fixes: c84301d706c5 ("objtool: Ignore entire functions rather than instructions") Signed-off-by: Josh Poimboeuf Signed-off-by: Ingo Molnar Link: https://lore.kernel.org/r/70a09ec0b0704398b2bbfb3153ce3d7cb8a381be.1743136205.git.jpoimboe@kernel.org --- tools/objtool/check.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'tools/objtool/check.c') diff --git a/tools/objtool/check.c b/tools/objtool/check.c index 29de1709ea00..fff9d7a2947a 100644 --- a/tools/objtool/check.c +++ b/tools/objtool/check.c @@ -1014,6 +1014,8 @@ static int add_ignores(struct objtool_file *file) } func->ignore = true; + if (func->cfunc) + func->cfunc->ignore = true; } return 0; -- cgit v1.2.3 From 0d7597749f5a3ac67851d3836635d084df15fb66 Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Mon, 31 Mar 2025 21:26:37 -0700 Subject: objtool: Ignore end-of-section jumps for KCOV/GCOV When KCOV or GCOV is enabled, dead code can be left behind, in which case objtool silences unreachable and undefined behavior (fallthrough) warnings. Fallthrough warnings, and their variant "end of section" warnings, were silenced with the following commit: 6b023c784204 ("objtool: Silence more KCOV warnings") Another variant of a fallthrough warning is a jump to the end of a function. If that function happens to be at the end of a section, the jump destination doesn't actually exist. Normally that would be a fatal objtool error, but for KCOV/GCOV it's just another undefined behavior fallthrough. Silence it like the others. Fixes the following warning: drivers/iommu/dma-iommu.o: warning: objtool: iommu_dma_sw_msi+0x92: can't find jump dest instruction at .text+0x54d5 Fixes: 6b023c784204 ("objtool: Silence more KCOV warnings") Reported-by: Randy Dunlap Signed-off-by: Josh Poimboeuf Signed-off-by: Ingo Molnar Cc: Linus Torvalds Link: https://lore.kernel.org/r/08fbe7d7e1e20612206f1df253077b94f178d93e.1743481539.git.jpoimboe@kernel.org Closes: https://lore.kernel.org/314f8809-cd59-479b-97d7-49356bf1c8d1@infradead.org/ --- tools/objtool/check.c | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) (limited to 'tools/objtool/check.c') diff --git a/tools/objtool/check.c b/tools/objtool/check.c index fff9d7a2947a..e6c4eefe295b 100644 --- a/tools/objtool/check.c +++ b/tools/objtool/check.c @@ -1488,6 +1488,8 @@ static int add_jump_destinations(struct objtool_file *file) int ret; for_each_insn(file, insn) { + struct symbol *func = insn_func(insn); + if (insn->jump_dest) { /* * handle_group_alt() may have previously set @@ -1513,7 +1515,7 @@ static int add_jump_destinations(struct objtool_file *file) } else if (reloc->sym->return_thunk) { add_return_call(file, insn, true); continue; - } else if (insn_func(insn)) { + } else if (func) { /* * External sibling call or internal sibling call with * STT_FUNC reloc. @@ -1548,6 +1550,15 @@ static int add_jump_destinations(struct objtool_file *file) continue; } + /* + * GCOV/KCOV dead code can jump to the end of the + * function/section. + */ + if (file->ignore_unreachables && func && + dest_sec == insn->sec && + dest_off == func->offset + func->len) + continue; + WARN_INSN(insn, "can't find jump dest instruction at %s+0x%lx", dest_sec->name, dest_off); return -1; @@ -1574,8 +1585,7 @@ static int add_jump_destinations(struct objtool_file *file) /* * Cross-function jump. */ - if (insn_func(insn) && insn_func(jump_dest) && - insn_func(insn) != insn_func(jump_dest)) { + if (func && insn_func(jump_dest) && func != insn_func(jump_dest)) { /* * For GCC 8+, create parent/child links for any cold @@ -1592,10 +1602,10 @@ static int add_jump_destinations(struct objtool_file *file) * case where the parent function's only reference to a * subfunction is through a jump table. */ - if (!strstr(insn_func(insn)->name, ".cold") && + if (!strstr(func->name, ".cold") && strstr(insn_func(jump_dest)->name, ".cold")) { - insn_func(insn)->cfunc = insn_func(jump_dest); - insn_func(jump_dest)->pfunc = insn_func(insn); + func->cfunc = insn_func(jump_dest); + insn_func(jump_dest)->pfunc = func; } } -- cgit v1.2.3 From 188d90f817e13b66e03e110eb6f82e8f5f0d654b Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Mon, 31 Mar 2025 21:26:38 -0700 Subject: objtool: Append "()" to function name in "unexpected end of section" warning Append with "()" to clarify it's a function. Before: vmlinux.o: warning: objtool: cdns_mrvl_xspi_setup_clock: unexpected end of section .text.cdns_mrvl_xspi_setup_clock After: vmlinux.o: warning: objtool: cdns_mrvl_xspi_setup_clock(): unexpected end of section .text.cdns_mrvl_xspi_setup_clock Fixes: c5995abe1547 ("objtool: Improve error handling") Signed-off-by: Josh Poimboeuf Signed-off-by: Ingo Molnar Cc: Linus Torvalds Link: https://lore.kernel.org/r/692e1e0d0b15a71bd35c6b4b87f3c75cd5a57358.1743481539.git.jpoimboe@kernel.org --- tools/objtool/check.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tools/objtool/check.c') diff --git a/tools/objtool/check.c b/tools/objtool/check.c index e6c4eefe295b..bd0c78bfe90c 100644 --- a/tools/objtool/check.c +++ b/tools/objtool/check.c @@ -3761,7 +3761,7 @@ static int validate_branch(struct objtool_file *file, struct symbol *func, return 0; WARN("%s%sunexpected end of section %s", - func ? func->name : "", func ? ": " : "", + func ? func->name : "", func ? "(): " : "", sec->name); return 1; } -- cgit v1.2.3 From c5610071a69d1c94c70e681874298b4fc6942098 Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Mon, 31 Mar 2025 21:26:39 -0700 Subject: Revert "objtool: Increase per-function WARN_FUNC() rate limit" This reverts commit 0a7fb6f07e3ad497d31ae9a2082d2cacab43d54a. The "skipping duplicate warnings" warning is technically not an actual warning, which can cause confusion. This feature isn't all that useful anyway. It's exceedingly rare for a function to have more than one unrelated warning. Suggested-by: Ingo Molnar Signed-off-by: Josh Poimboeuf Signed-off-by: Ingo Molnar Cc: Linus Torvalds Link: https://lore.kernel.org/r/e5abe5e858acf1a9207a5dfa0f37d17ac9dca872.1743481539.git.jpoimboe@kernel.org --- tools/objtool/check.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'tools/objtool/check.c') diff --git a/tools/objtool/check.c b/tools/objtool/check.c index bd0c78bfe90c..c8b3c8e7090c 100644 --- a/tools/objtool/check.c +++ b/tools/objtool/check.c @@ -3545,7 +3545,7 @@ static int validate_branch(struct objtool_file *file, struct symbol *func, WARN("%s() falls through to next function %s()", func->name, insn_func(insn)->name); - func->warnings++; + func->warned = 1; return 1; } @@ -4576,7 +4576,7 @@ static void disas_warned_funcs(struct objtool_file *file) char *funcs = NULL, *tmp; for_each_sym(file, sym) { - if (sym->warnings) { + if (sym->warned) { if (!funcs) { funcs = malloc(strlen(sym->name) + 1); if (!funcs) { -- cgit v1.2.3 From 0b10177114d1e434af850b377cf5e6620dd1d525 Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Mon, 31 Mar 2025 21:26:40 -0700 Subject: objtool: Always fail on fatal errors Objtool writes several object annotations which are used to enable critical kernel runtime functionalities like static calls and retpoline/rethunk patching. In the rare case where it fails to read or write an object, the annotations don't get written, causing runtime code patching to fail and code to become corrupted. Due to the catastrophic nature of such warnings, convert them to errors which fail the build regardless of CONFIG_OBJTOOL_WERROR. Reported-by: Chaitanya Kumar Borah Signed-off-by: Josh Poimboeuf Signed-off-by: Ingo Molnar Cc: Linus Torvalds Link: https://lore.kernel.org/r/7d35684ca61eac56eb2424f300ca43c5d257b170.1743481539.git.jpoimboe@kernel.org Closes: https://lore.kernel.org/SJ1PR11MB61295789E25C2F5197EFF2F6B9A72@SJ1PR11MB6129.namprd11.prod.outlook.com --- tools/objtool/check.c | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) (limited to 'tools/objtool/check.c') diff --git a/tools/objtool/check.c b/tools/objtool/check.c index c8b3c8e7090c..cde669923b72 100644 --- a/tools/objtool/check.c +++ b/tools/objtool/check.c @@ -4753,6 +4753,9 @@ out: if (!ret && !warnings) return 0; + if (opts.werror && warnings) + ret = 1; + if (opts.verbose) { if (opts.werror && warnings) WARN("%d warning(s) upgraded to errors", warnings); @@ -4760,15 +4763,5 @@ out: disas_warned_funcs(file); } - /* - * CONFIG_OBJTOOL_WERROR upgrades all warnings (and errors) to actual - * errors. - * - * Note that even fatal errors don't yet actually return an error - * without CONFIG_OBJTOOL_WERROR. That will be fixed soon-ish. - */ - if (opts.werror) - return 1; - - return 0; + return ret; } -- cgit v1.2.3 From 3e7be635937d19b91bab70695328214a3d789d51 Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Mon, 31 Mar 2025 21:26:41 -0700 Subject: objtool: Change "warning:" to "error: " for fatal errors This is similar to GCC's behavior and makes it more obvious why the build failed. Signed-off-by: Josh Poimboeuf Signed-off-by: Ingo Molnar Cc: Linus Torvalds Link: https://lore.kernel.org/r/0ea76f4b0e7a370711ed9f75fd0792bb5979c2bf.1743481539.git.jpoimboe@kernel.org --- tools/objtool/check.c | 127 ++++++++++++++++++++++++-------------------------- 1 file changed, 60 insertions(+), 67 deletions(-) (limited to 'tools/objtool/check.c') diff --git a/tools/objtool/check.c b/tools/objtool/check.c index cde669923b72..ff83be1aab1d 100644 --- a/tools/objtool/check.c +++ b/tools/objtool/check.c @@ -348,7 +348,7 @@ static struct cfi_state *cfi_alloc(void) { struct cfi_state *cfi = calloc(1, sizeof(struct cfi_state)); if (!cfi) { - WARN_GLIBC("calloc"); + ERROR_GLIBC("calloc"); exit(1); } nr_cfi++; @@ -404,7 +404,7 @@ static void *cfi_hash_alloc(unsigned long size) PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON, -1, 0); if (cfi_hash == (void *)-1L) { - WARN_GLIBC("mmap fail cfi_hash"); + ERROR_GLIBC("mmap fail cfi_hash"); cfi_hash = NULL; } else if (opts.stats) { printf("cfi_bits: %d\n", cfi_bits); @@ -460,7 +460,7 @@ static int decode_instructions(struct objtool_file *file) if (!insns || idx == INSN_CHUNK_MAX) { insns = calloc(sizeof(*insn), INSN_CHUNK_SIZE); if (!insns) { - WARN_GLIBC("calloc"); + ERROR_GLIBC("calloc"); return -1; } idx = 0; @@ -495,8 +495,6 @@ static int decode_instructions(struct objtool_file *file) nr_insns++; } -// printf("%s: last chunk used: %d\n", sec->name, (int)idx); - sec_for_each_sym(sec, func) { if (func->type != STT_NOTYPE && func->type != STT_FUNC) continue; @@ -505,8 +503,7 @@ static int decode_instructions(struct objtool_file *file) /* Heuristic: likely an "end" symbol */ if (func->type == STT_NOTYPE) continue; - WARN("%s(): STT_FUNC at end of section", - func->name); + ERROR("%s(): STT_FUNC at end of section", func->name); return -1; } @@ -514,8 +511,7 @@ static int decode_instructions(struct objtool_file *file) continue; if (!find_insn(file, sec, func->offset)) { - WARN("%s(): can't find starting instruction", - func->name); + ERROR("%s(): can't find starting instruction", func->name); return -1; } @@ -569,9 +565,8 @@ static int add_pv_ops(struct objtool_file *file, const char *symname) func = find_symbol_by_offset(reloc->sym->sec, reloc_addend(reloc)); if (!func) { - WARN_FUNC("can't find func at %s[%d]", - reloc->sym->sec, reloc_addend(reloc), - symname, idx); + ERROR_FUNC(reloc->sym->sec, reloc_addend(reloc), + "can't find func at %s[%d]", symname, idx); return -1; } @@ -614,7 +609,7 @@ static int init_pv_ops(struct objtool_file *file) nr = sym->len / sizeof(unsigned long); file->pv_ops = calloc(sizeof(struct pv_state), nr); if (!file->pv_ops) { - WARN_GLIBC("calloc"); + ERROR_GLIBC("calloc"); return -1; } @@ -673,12 +668,12 @@ static int create_static_call_sections(struct objtool_file *file) /* find key symbol */ key_name = strdup(insn_call_dest(insn)->name); if (!key_name) { - WARN_GLIBC("strdup"); + ERROR_GLIBC("strdup"); return -1; } if (strncmp(key_name, STATIC_CALL_TRAMP_PREFIX_STR, STATIC_CALL_TRAMP_PREFIX_LEN)) { - WARN("static_call: trampoline name malformed: %s", key_name); + ERROR("static_call: trampoline name malformed: %s", key_name); return -1; } tmp = key_name + STATIC_CALL_TRAMP_PREFIX_LEN - STATIC_CALL_KEY_PREFIX_LEN; @@ -687,7 +682,7 @@ static int create_static_call_sections(struct objtool_file *file) key_sym = find_symbol_by_name(file->elf, tmp); if (!key_sym) { if (!opts.module) { - WARN("static_call: can't find static_call_key symbol: %s", tmp); + ERROR("static_call: can't find static_call_key symbol: %s", tmp); return -1; } @@ -833,8 +828,8 @@ static int create_ibt_endbr_seal_sections(struct objtool_file *file) insn->offset == sym->offset && (!strcmp(sym->name, "init_module") || !strcmp(sym->name, "cleanup_module"))) { - WARN("%s(): Magic init_module() function name is deprecated, use module_init(fn) instead", - sym->name); + ERROR("%s(): Magic init_module() function name is deprecated, use module_init(fn) instead", + sym->name); return -1; } @@ -1008,8 +1003,8 @@ static int add_ignores(struct objtool_file *file) break; default: - WARN("unexpected relocation symbol type in %s: %d", - rsec->name, reloc->sym->type); + ERROR("unexpected relocation symbol type in %s: %d", + rsec->name, reloc->sym->type); return -1; } @@ -1559,8 +1554,8 @@ static int add_jump_destinations(struct objtool_file *file) dest_off == func->offset + func->len) continue; - WARN_INSN(insn, "can't find jump dest instruction at %s+0x%lx", - dest_sec->name, dest_off); + ERROR_INSN(insn, "can't find jump dest instruction at %s+0x%lx", + dest_sec->name, dest_off); return -1; } @@ -1666,12 +1661,12 @@ static int add_call_destinations(struct objtool_file *file) continue; if (!insn_call_dest(insn)) { - WARN_INSN(insn, "unannotated intra-function call"); + ERROR_INSN(insn, "unannotated intra-function call"); return -1; } if (func && insn_call_dest(insn)->type != STT_FUNC) { - WARN_INSN(insn, "unsupported call to non-function"); + ERROR_INSN(insn, "unsupported call to non-function"); return -1; } @@ -1679,8 +1674,8 @@ static int add_call_destinations(struct objtool_file *file) dest_off = arch_dest_reloc_offset(reloc_addend(reloc)); dest = find_call_destination(reloc->sym->sec, dest_off); if (!dest) { - WARN_INSN(insn, "can't find call dest symbol at %s+0x%lx", - reloc->sym->sec->name, dest_off); + ERROR_INSN(insn, "can't find call dest symbol at %s+0x%lx", + reloc->sym->sec->name, dest_off); return -1; } @@ -1722,13 +1717,13 @@ static int handle_group_alt(struct objtool_file *file, orig_alt_group = calloc(1, sizeof(*orig_alt_group)); if (!orig_alt_group) { - WARN_GLIBC("calloc"); + ERROR_GLIBC("calloc"); return -1; } orig_alt_group->cfi = calloc(special_alt->orig_len, sizeof(struct cfi_state *)); if (!orig_alt_group->cfi) { - WARN_GLIBC("calloc"); + ERROR_GLIBC("calloc"); return -1; } @@ -1748,18 +1743,18 @@ static int handle_group_alt(struct objtool_file *file, } else { if (orig_alt_group->last_insn->offset + orig_alt_group->last_insn->len - orig_alt_group->first_insn->offset != special_alt->orig_len) { - WARN_INSN(orig_insn, "weirdly overlapping alternative! %ld != %d", - orig_alt_group->last_insn->offset + - orig_alt_group->last_insn->len - - orig_alt_group->first_insn->offset, - special_alt->orig_len); + ERROR_INSN(orig_insn, "weirdly overlapping alternative! %ld != %d", + orig_alt_group->last_insn->offset + + orig_alt_group->last_insn->len - + orig_alt_group->first_insn->offset, + special_alt->orig_len); return -1; } } new_alt_group = calloc(1, sizeof(*new_alt_group)); if (!new_alt_group) { - WARN_GLIBC("calloc"); + ERROR_GLIBC("calloc"); return -1; } @@ -1773,7 +1768,7 @@ static int handle_group_alt(struct objtool_file *file, */ nop = calloc(1, sizeof(*nop)); if (!nop) { - WARN_GLIBC("calloc"); + ERROR_GLIBC("calloc"); return -1; } memset(nop, 0, sizeof(*nop)); @@ -1815,7 +1810,7 @@ static int handle_group_alt(struct objtool_file *file, if (alt_reloc && arch_pc_relative_reloc(alt_reloc) && !arch_support_alt_relocation(special_alt, insn, alt_reloc)) { - WARN_INSN(insn, "unsupported relocation in alternatives section"); + ERROR_INSN(insn, "unsupported relocation in alternatives section"); return -1; } @@ -1829,15 +1824,15 @@ static int handle_group_alt(struct objtool_file *file, if (dest_off == special_alt->new_off + special_alt->new_len) { insn->jump_dest = next_insn_same_sec(file, orig_alt_group->last_insn); if (!insn->jump_dest) { - WARN_INSN(insn, "can't find alternative jump destination"); + ERROR_INSN(insn, "can't find alternative jump destination"); return -1; } } } if (!last_new_insn) { - WARN_FUNC("can't find last new alternative instruction", - special_alt->new_sec, special_alt->new_off); + ERROR_FUNC(special_alt->new_sec, special_alt->new_off, + "can't find last new alternative instruction"); return -1; } @@ -1864,7 +1859,7 @@ static int handle_jump_alt(struct objtool_file *file, if (orig_insn->type != INSN_JUMP_UNCONDITIONAL && orig_insn->type != INSN_NOP) { - WARN_INSN(orig_insn, "unsupported instruction at jump label"); + ERROR_INSN(orig_insn, "unsupported instruction at jump label"); return -1; } @@ -1923,8 +1918,8 @@ static int add_special_section_alts(struct objtool_file *file) orig_insn = find_insn(file, special_alt->orig_sec, special_alt->orig_off); if (!orig_insn) { - WARN_FUNC("special: can't find orig instruction", - special_alt->orig_sec, special_alt->orig_off); + ERROR_FUNC(special_alt->orig_sec, special_alt->orig_off, + "special: can't find orig instruction"); return -1; } @@ -1933,16 +1928,15 @@ static int add_special_section_alts(struct objtool_file *file) new_insn = find_insn(file, special_alt->new_sec, special_alt->new_off); if (!new_insn) { - WARN_FUNC("special: can't find new instruction", - special_alt->new_sec, - special_alt->new_off); + ERROR_FUNC(special_alt->new_sec, special_alt->new_off, + "special: can't find new instruction"); return -1; } } if (special_alt->group) { if (!special_alt->orig_len) { - WARN_INSN(orig_insn, "empty alternative entry"); + ERROR_INSN(orig_insn, "empty alternative entry"); continue; } @@ -1960,7 +1954,7 @@ static int add_special_section_alts(struct objtool_file *file) alt = calloc(1, sizeof(*alt)); if (!alt) { - WARN_GLIBC("calloc"); + ERROR_GLIBC("calloc"); return -1; } @@ -2037,7 +2031,7 @@ static int add_jump_table(struct objtool_file *file, struct instruction *insn) alt = calloc(1, sizeof(*alt)); if (!alt) { - WARN_GLIBC("calloc"); + ERROR_GLIBC("calloc"); return -1; } @@ -2049,7 +2043,7 @@ next: } if (!prev_offset) { - WARN_INSN(insn, "can't find switch jump table"); + ERROR_INSN(insn, "can't find switch jump table"); return -1; } @@ -2207,12 +2201,12 @@ static int read_unwind_hints(struct objtool_file *file) return 0; if (!sec->rsec) { - WARN("missing .rela.discard.unwind_hints section"); + ERROR("missing .rela.discard.unwind_hints section"); return -1; } if (sec->sh.sh_size % sizeof(struct unwind_hint)) { - WARN("struct unwind_hint size mismatch"); + ERROR("struct unwind_hint size mismatch"); return -1; } @@ -2223,7 +2217,7 @@ static int read_unwind_hints(struct objtool_file *file) reloc = find_reloc_by_dest(file->elf, sec, i * sizeof(*hint)); if (!reloc) { - WARN("can't find reloc for unwind_hints[%d]", i); + ERROR("can't find reloc for unwind_hints[%d]", i); return -1; } @@ -2232,13 +2226,13 @@ static int read_unwind_hints(struct objtool_file *file) } else if (reloc->sym->local_label) { offset = reloc->sym->offset; } else { - WARN("unexpected relocation symbol type in %s", sec->rsec->name); + ERROR("unexpected relocation symbol type in %s", sec->rsec->name); return -1; } insn = find_insn(file, reloc->sym->sec, offset); if (!insn) { - WARN("can't find insn for unwind_hints[%d]", i); + ERROR("can't find insn for unwind_hints[%d]", i); return -1; } @@ -2265,7 +2259,7 @@ static int read_unwind_hints(struct objtool_file *file) if (sym && sym->bind == STB_GLOBAL) { if (opts.ibt && insn->type != INSN_ENDBR && !insn->noendbr) { - WARN_INSN(insn, "UNWIND_HINT_IRET_REGS without ENDBR"); + ERROR_INSN(insn, "UNWIND_HINT_IRET_REGS without ENDBR"); return -1; } } @@ -2280,7 +2274,7 @@ static int read_unwind_hints(struct objtool_file *file) cfi = *(insn->cfi); if (arch_decode_hint_reg(hint->sp_reg, &cfi.cfa.base)) { - WARN_INSN(insn, "unsupported unwind_hint sp base reg %d", hint->sp_reg); + ERROR_INSN(insn, "unsupported unwind_hint sp base reg %d", hint->sp_reg); return -1; } @@ -2326,7 +2320,7 @@ static int read_annotate(struct objtool_file *file, insn = find_insn(file, reloc->sym->sec, offset); if (!insn) { - WARN("bad .discard.annotate_insn entry: %d of type %d", reloc_idx(reloc), type); + ERROR("bad .discard.annotate_insn entry: %d of type %d", reloc_idx(reloc), type); return -1; } @@ -2369,7 +2363,7 @@ static int __annotate_ifc(struct objtool_file *file, int type, struct instructio return 0; if (insn->type != INSN_CALL) { - WARN_INSN(insn, "intra_function_call not a direct call"); + ERROR_INSN(insn, "intra_function_call not a direct call"); return -1; } @@ -2383,8 +2377,8 @@ static int __annotate_ifc(struct objtool_file *file, int type, struct instructio dest_off = arch_jump_destination(insn); insn->jump_dest = find_insn(file, insn->sec, dest_off); if (!insn->jump_dest) { - WARN_INSN(insn, "can't find call dest at %s+0x%lx", - insn->sec->name, dest_off); + ERROR_INSN(insn, "can't find call dest at %s+0x%lx", + insn->sec->name, dest_off); return -1; } @@ -2403,7 +2397,7 @@ static int __annotate_late(struct objtool_file *file, int type, struct instructi insn->type != INSN_CALL_DYNAMIC && insn->type != INSN_RETURN && insn->type != INSN_NOP) { - WARN_INSN(insn, "retpoline_safe hint not an indirect jump/call/ret/nop"); + ERROR_INSN(insn, "retpoline_safe hint not an indirect jump/call/ret/nop"); return -1; } @@ -2435,7 +2429,7 @@ static int __annotate_late(struct objtool_file *file, int type, struct instructi break; default: - WARN_INSN(insn, "Unknown annotation type: %d", type); + ERROR_INSN(insn, "Unknown annotation type: %d", type); return -1; } @@ -4393,9 +4387,8 @@ static int validate_ibt_data_reloc(struct objtool_file *file, if (dest->noendbr) return 0; - WARN_FUNC("data relocation to !ENDBR: %s", - reloc->sec->base, reloc_offset(reloc), - offstr(dest->sec, dest->offset)); + WARN_FUNC(reloc->sec->base, reloc_offset(reloc), + "data relocation to !ENDBR: %s", offstr(dest->sec, dest->offset)); return 1; } @@ -4580,14 +4573,14 @@ static void disas_warned_funcs(struct objtool_file *file) if (!funcs) { funcs = malloc(strlen(sym->name) + 1); if (!funcs) { - WARN_GLIBC("malloc"); + ERROR_GLIBC("malloc"); return; } strcpy(funcs, sym->name); } else { tmp = malloc(strlen(funcs) + strlen(sym->name) + 2); if (!tmp) { - WARN_GLIBC("malloc"); + ERROR_GLIBC("malloc"); return; } sprintf(tmp, "%s %s", funcs, sym->name); -- cgit v1.2.3 From e77956e4e5c11218e60a1fe8cdbccd02476f2e56 Mon Sep 17 00:00:00 2001 From: David Laight Date: Mon, 31 Mar 2025 21:26:42 -0700 Subject: objtool: Fix verbose disassembly if CROSS_COMPILE isn't set In verbose mode, when printing the disassembly of affected functions, if CROSS_COMPILE isn't set, the objdump command string gets prefixed with "(null)". Somehow this worked before. Maybe some versions of glibc return an empty string instead of NULL. Fix it regardless. [ jpoimboe: Rewrite commit log. ] Fixes: ca653464dd097 ("objtool: Add verbose option for disassembling affected functions") Signed-off-by: David Laight Signed-off-by: Josh Poimboeuf Signed-off-by: Ingo Molnar Cc: Linus Torvalds Link: https://lore.kernel.org/r/20250215142321.14081-1-david.laight.linux@gmail.com Link: https://lore.kernel.org/r/b931a4786bc0127aa4c94e8b35ed617dcbd3d3da.1743481539.git.jpoimboe@kernel.org --- tools/objtool/check.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'tools/objtool/check.c') diff --git a/tools/objtool/check.c b/tools/objtool/check.c index ff83be1aab1d..4a1f6c3169b3 100644 --- a/tools/objtool/check.c +++ b/tools/objtool/check.c @@ -4523,6 +4523,8 @@ static void disas_funcs(const char *funcs) char *cmd; cross_compile = getenv("CROSS_COMPILE"); + if (!cross_compile) + cross_compile = ""; objdump_str = "%sobjdump -wdr %s | gawk -M -v _funcs='%s' '" "BEGIN { split(_funcs, funcs); }" -- cgit v1.2.3