From ef753d66051ca03bee1982ce047f9eaf90f81ab4 Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Mon, 24 Mar 2025 14:55:51 -0700 Subject: [PATCH 01/36] 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 ++++++++------------------ tools/objtool/elf.c | 6 +++--- tools/objtool/include/objtool/elf.h | 27 ++++++++++++++++++++++++++- 3 files changed, 37 insertions(+), 22 deletions(-) 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; } /* diff --git a/tools/objtool/elf.c b/tools/objtool/elf.c index be4f4b62730c..0f38167bd840 100644 --- a/tools/objtool/elf.c +++ b/tools/objtool/elf.c @@ -583,7 +583,7 @@ static int elf_update_sym_relocs(struct elf *elf, struct symbol *sym) { struct reloc *reloc; - for (reloc = sym->relocs; reloc; reloc = reloc->sym_next_reloc) + for (reloc = sym->relocs; reloc; reloc = sym_next_reloc(reloc)) set_reloc_sym(elf, reloc, reloc->sym->idx); return 0; @@ -880,7 +880,7 @@ static struct reloc *elf_init_reloc(struct elf *elf, struct section *rsec, set_reloc_addend(elf, reloc, addend); elf_hash_add(reloc, &reloc->hash, reloc_hash(reloc)); - reloc->sym_next_reloc = sym->relocs; + set_sym_next_reloc(reloc, sym->relocs); sym->relocs = reloc; return reloc; @@ -979,7 +979,7 @@ static int read_relocs(struct elf *elf) } elf_hash_add(reloc, &reloc->hash, reloc_hash(reloc)); - reloc->sym_next_reloc = sym->relocs; + set_sym_next_reloc(reloc, sym->relocs); sym->relocs = reloc; nr_reloc++; diff --git a/tools/objtool/include/objtool/elf.h b/tools/objtool/include/objtool/elf.h index 223ac1c24b90..4edc957a6f6b 100644 --- a/tools/objtool/include/objtool/elf.h +++ b/tools/objtool/include/objtool/elf.h @@ -77,7 +77,7 @@ struct reloc { struct elf_hash_node hash; struct section *sec; struct symbol *sym; - struct reloc *sym_next_reloc; + unsigned long _sym_next_reloc; }; struct elf { @@ -297,6 +297,31 @@ static inline void set_reloc_type(struct elf *elf, struct reloc *reloc, unsigned mark_sec_changed(elf, reloc->sec, true); } +#define RELOC_JUMP_TABLE_BIT 1UL + +/* Does reloc mark the beginning of a jump table? */ +static inline bool is_jump_table(struct reloc *reloc) +{ + return reloc->_sym_next_reloc & RELOC_JUMP_TABLE_BIT; +} + +static inline void set_jump_table(struct reloc *reloc) +{ + reloc->_sym_next_reloc |= RELOC_JUMP_TABLE_BIT; +} + +static inline struct reloc *sym_next_reloc(struct reloc *reloc) +{ + return (struct reloc *)(reloc->_sym_next_reloc & ~RELOC_JUMP_TABLE_BIT); +} + +static inline void set_sym_next_reloc(struct reloc *reloc, struct reloc *next) +{ + unsigned long bit = reloc->_sym_next_reloc & RELOC_JUMP_TABLE_BIT; + + reloc->_sym_next_reloc = (unsigned long)next | bit; +} + #define for_each_sec(file, sec) \ list_for_each_entry(sec, &file->elf->sections, list) From eeff7ac61526a4a9c3916cf46885622078ad886b Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Mon, 24 Mar 2025 14:55:52 -0700 Subject: [PATCH 02/36] objtool: Warn when disabling unreachable warnings Print a warning when disabling the unreachable warnings (due to a GCC bug). This will help determine if recent GCCs still have the issue and alert us if any other issues might be silently lurking behind the unreachable disablement. Signed-off-by: Josh Poimboeuf Signed-off-by: Ingo Molnar Cc: Linus Torvalds Link: https://lore.kernel.org/r/df243063787596e6031367e6659e7e43409d6c6d.1742852846.git.jpoimboe@kernel.org --- tools/objtool/arch/x86/special.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tools/objtool/arch/x86/special.c b/tools/objtool/arch/x86/special.c index 9c1c9df09aaa..5f46d4e7f7f8 100644 --- a/tools/objtool/arch/x86/special.c +++ b/tools/objtool/arch/x86/special.c @@ -3,6 +3,7 @@ #include #include +#include #define X86_FEATURE_POPCNT (4 * 32 + 23) #define X86_FEATURE_SMAP (9 * 32 + 20) @@ -156,8 +157,10 @@ struct reloc *arch_find_switch_table(struct objtool_file *file, * indicates a rare GCC quirk/bug which can leave dead * code behind. */ - if (reloc_type(text_reloc) == R_X86_64_PC32) + if (reloc_type(text_reloc) == R_X86_64_PC32) { + WARN_INSN(insn, "ignoring unreachables due to jump table quirk"); file->ignore_unreachables = true; + } *table_size = 0; return rodata_reloc; From c84301d706c5456b1460439b2987a0f0b6362a82 Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Mon, 24 Mar 2025 14:55:53 -0700 Subject: [PATCH 03/36] 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 +++++++++++++++-------------- tools/objtool/include/objtool/elf.h | 1 + 2 files changed, 19 insertions(+), 17 deletions(-) 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; diff --git a/tools/objtool/include/objtool/elf.h b/tools/objtool/include/objtool/elf.h index 4edc957a6f6b..eba04392c6fd 100644 --- a/tools/objtool/include/objtool/elf.h +++ b/tools/objtool/include/objtool/elf.h @@ -68,6 +68,7 @@ struct symbol { u8 embedded_insn : 1; u8 local_label : 1; u8 frame_pointer : 1; + u8 ignore : 1; u8 warnings : 2; struct list_head pv_target; struct reloc *relocs; From 1154bbd326de4453858cf78cf29420888b3ffd52 Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Mon, 24 Mar 2025 14:55:54 -0700 Subject: [PATCH 04/36] 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/ --- arch/x86/include/asm/arch_hweight.h | 6 ++-- arch/x86/include/asm/smap.h | 23 ++++++++++----- arch/x86/include/asm/xen/hypercall.h | 6 ++-- tools/objtool/arch/x86/special.c | 33 +-------------------- tools/objtool/check.c | 39 +++++++------------------ tools/objtool/include/objtool/check.h | 3 +- tools/objtool/include/objtool/special.h | 4 +-- tools/objtool/special.c | 12 ++------ 8 files changed, 37 insertions(+), 89 deletions(-) diff --git a/arch/x86/include/asm/arch_hweight.h b/arch/x86/include/asm/arch_hweight.h index b5982b94bdba..cbc6157f0b4b 100644 --- a/arch/x86/include/asm/arch_hweight.h +++ b/arch/x86/include/asm/arch_hweight.h @@ -16,7 +16,8 @@ static __always_inline unsigned int __arch_hweight32(unsigned int w) { unsigned int res; - asm_inline (ALTERNATIVE("call __sw_hweight32", + asm_inline (ALTERNATIVE(ANNOTATE_IGNORE_ALTERNATIVE + "call __sw_hweight32", "popcntl %[val], %[cnt]", X86_FEATURE_POPCNT) : [cnt] "=" REG_OUT (res), ASM_CALL_CONSTRAINT : [val] REG_IN (w)); @@ -45,7 +46,8 @@ static __always_inline unsigned long __arch_hweight64(__u64 w) { unsigned long res; - asm_inline (ALTERNATIVE("call __sw_hweight64", + asm_inline (ALTERNATIVE(ANNOTATE_IGNORE_ALTERNATIVE + "call __sw_hweight64", "popcntq %[val], %[cnt]", X86_FEATURE_POPCNT) : [cnt] "=" REG_OUT (res), ASM_CALL_CONSTRAINT : [val] REG_IN (w)); diff --git a/arch/x86/include/asm/smap.h b/arch/x86/include/asm/smap.h index daea94c2993c..55a5e656e4b9 100644 --- a/arch/x86/include/asm/smap.h +++ b/arch/x86/include/asm/smap.h @@ -16,23 +16,23 @@ #ifdef __ASSEMBLER__ #define ASM_CLAC \ - ALTERNATIVE "", "clac", X86_FEATURE_SMAP + ALTERNATIVE __stringify(ANNOTATE_IGNORE_ALTERNATIVE), "clac", X86_FEATURE_SMAP #define ASM_STAC \ - ALTERNATIVE "", "stac", X86_FEATURE_SMAP + ALTERNATIVE __stringify(ANNOTATE_IGNORE_ALTERNATIVE), "stac", X86_FEATURE_SMAP #else /* __ASSEMBLER__ */ static __always_inline void clac(void) { /* Note: a barrier is implicit in alternative() */ - alternative("", "clac", X86_FEATURE_SMAP); + alternative(ANNOTATE_IGNORE_ALTERNATIVE "", "clac", X86_FEATURE_SMAP); } static __always_inline void stac(void) { /* Note: a barrier is implicit in alternative() */ - alternative("", "stac", X86_FEATURE_SMAP); + alternative(ANNOTATE_IGNORE_ALTERNATIVE "", "stac", X86_FEATURE_SMAP); } static __always_inline unsigned long smap_save(void) @@ -40,7 +40,8 @@ static __always_inline unsigned long smap_save(void) unsigned long flags; asm volatile ("# smap_save\n\t" - ALTERNATIVE("", "pushf; pop %0; " "clac" "\n\t", + ALTERNATIVE(ANNOTATE_IGNORE_ALTERNATIVE + "", "pushf; pop %0; clac", X86_FEATURE_SMAP) : "=rm" (flags) : : "memory", "cc"); @@ -50,16 +51,22 @@ static __always_inline unsigned long smap_save(void) static __always_inline void smap_restore(unsigned long flags) { asm volatile ("# smap_restore\n\t" - ALTERNATIVE("", "push %0; popf\n\t", + ALTERNATIVE(ANNOTATE_IGNORE_ALTERNATIVE + "", "push %0; popf", X86_FEATURE_SMAP) : : "g" (flags) : "memory", "cc"); } /* These macros can be used in asm() statements */ #define ASM_CLAC \ - ALTERNATIVE("", "clac", X86_FEATURE_SMAP) + ALTERNATIVE(ANNOTATE_IGNORE_ALTERNATIVE "", "clac", X86_FEATURE_SMAP) #define ASM_STAC \ - ALTERNATIVE("", "stac", X86_FEATURE_SMAP) + ALTERNATIVE(ANNOTATE_IGNORE_ALTERNATIVE "", "stac", X86_FEATURE_SMAP) + +#define ASM_CLAC_UNSAFE \ + ALTERNATIVE("", ANNOTATE_IGNORE_ALTERNATIVE "clac", X86_FEATURE_SMAP) +#define ASM_STAC_UNSAFE \ + ALTERNATIVE("", ANNOTATE_IGNORE_ALTERNATIVE "stac", X86_FEATURE_SMAP) #endif /* __ASSEMBLER__ */ diff --git a/arch/x86/include/asm/xen/hypercall.h b/arch/x86/include/asm/xen/hypercall.h index 97771b9d33af..59a62c3780a2 100644 --- a/arch/x86/include/asm/xen/hypercall.h +++ b/arch/x86/include/asm/xen/hypercall.h @@ -231,14 +231,12 @@ static __always_inline void __xen_stac(void) * Suppress objtool seeing the STAC/CLAC and getting confused about it * calling random code with AC=1. */ - asm volatile(ANNOTATE_IGNORE_ALTERNATIVE - ASM_STAC ::: "memory", "flags"); + asm volatile(ASM_STAC_UNSAFE ::: "memory", "flags"); } static __always_inline void __xen_clac(void) { - asm volatile(ANNOTATE_IGNORE_ALTERNATIVE - ASM_CLAC ::: "memory", "flags"); + asm volatile(ASM_CLAC_UNSAFE ::: "memory", "flags"); } static inline long diff --git a/tools/objtool/arch/x86/special.c b/tools/objtool/arch/x86/special.c index 5f46d4e7f7f8..403e587676f1 100644 --- a/tools/objtool/arch/x86/special.c +++ b/tools/objtool/arch/x86/special.c @@ -5,10 +5,7 @@ #include #include -#define X86_FEATURE_POPCNT (4 * 32 + 23) -#define X86_FEATURE_SMAP (9 * 32 + 20) - -void arch_handle_alternative(unsigned short feature, struct special_alt *alt) +void arch_handle_alternative(struct special_alt *alt) { static struct special_alt *group, *prev; @@ -32,34 +29,6 @@ void arch_handle_alternative(unsigned short feature, struct special_alt *alt) } else group = alt; prev = alt; - - switch (feature) { - case X86_FEATURE_SMAP: - /* - * If UACCESS validation is enabled; force that alternative; - * otherwise force it the other way. - * - * What we want to avoid is having both the original and the - * alternative code flow at the same time, in that case we can - * find paths that see the STAC but take the NOP instead of - * CLAC and the other way around. - */ - if (opts.uaccess) - alt->skip_orig = true; - else - alt->skip_alt = true; - break; - case X86_FEATURE_POPCNT: - /* - * It has been requested that we don't validate the !POPCNT - * feature path which is a "very very small percentage of - * machines". - */ - alt->skip_orig = true; - break; - default: - break; - } } bool arch_support_alt_relocation(struct special_alt *special_alt, 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; /* diff --git a/tools/objtool/include/objtool/check.h b/tools/objtool/include/objtool/check.h index e1cd13cd28a3..00fb745e7233 100644 --- a/tools/objtool/include/objtool/check.h +++ b/tools/objtool/include/objtool/check.h @@ -34,6 +34,8 @@ struct alt_group { * This is shared with the other alt_groups in the same alternative. */ struct cfi_state **cfi; + + bool ignore; }; #define INSN_CHUNK_BITS 8 @@ -54,7 +56,6 @@ struct instruction { u32 idx : INSN_CHUNK_BITS, dead_end : 1, - ignore : 1, ignore_alts : 1, hint : 1, save : 1, diff --git a/tools/objtool/include/objtool/special.h b/tools/objtool/include/objtool/special.h index e049679bb17b..72d09c0adf1a 100644 --- a/tools/objtool/include/objtool/special.h +++ b/tools/objtool/include/objtool/special.h @@ -16,8 +16,6 @@ struct special_alt { struct list_head list; bool group; - bool skip_orig; - bool skip_alt; bool jump_or_nop; u8 key_addend; @@ -32,7 +30,7 @@ struct special_alt { int special_get_alts(struct elf *elf, struct list_head *alts); -void arch_handle_alternative(unsigned short feature, struct special_alt *alt); +void arch_handle_alternative(struct special_alt *alt); bool arch_support_alt_relocation(struct special_alt *special_alt, struct instruction *insn, diff --git a/tools/objtool/special.c b/tools/objtool/special.c index 097a69db82a0..6cd7b1be5331 100644 --- a/tools/objtool/special.c +++ b/tools/objtool/special.c @@ -54,7 +54,7 @@ static const struct special_entry entries[] = { {}, }; -void __weak arch_handle_alternative(unsigned short feature, struct special_alt *alt) +void __weak arch_handle_alternative(struct special_alt *alt) { } @@ -92,15 +92,7 @@ static int get_alt_entry(struct elf *elf, const struct special_entry *entry, reloc_to_sec_off(orig_reloc, &alt->orig_sec, &alt->orig_off); - if (entry->feature) { - unsigned short feature; - - feature = bswap_if_needed(elf, - *(unsigned short *)(sec->data->d_buf + - offset + - entry->feature)); - arch_handle_alternative(feature, alt); - } + arch_handle_alternative(alt); if (!entry->group || alt->new_len) { new_reloc = find_reloc_by_dest(elf, sec, offset + entry->new); From 4759670bc3e670069c055c2b33174813099fea4f Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Mon, 24 Mar 2025 14:55:55 -0700 Subject: [PATCH 05/36] objtool: Fix CONFIG_OBJTOOL_WERROR for vmlinux.o With (!X86_KERNEL_IBT && !LTO_CLANG && NOINSTR_VALIDATION), objtool runs on both translation units and vmlinux.o. With CONFIG_OBJTOOL_WERROR, the TUs get --Werror but vmlinux.o doesn't. Fix that. Signed-off-by: Josh Poimboeuf Signed-off-by: Ingo Molnar Cc: Linus Torvalds Link: https://lore.kernel.org/r/4f71ab9b947ffc47b6a87dd3b9aff4bb32b36d0a.1742852846.git.jpoimboe@kernel.org --- scripts/Makefile.vmlinux_o | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/scripts/Makefile.vmlinux_o b/scripts/Makefile.vmlinux_o index 0b6e2ebf60dc..f476f5605029 100644 --- a/scripts/Makefile.vmlinux_o +++ b/scripts/Makefile.vmlinux_o @@ -30,12 +30,20 @@ endif # objtool for vmlinux.o # --------------------------------------------------------------------------- # -# For LTO and IBT, objtool doesn't run on individual translation units. -# Run everything on vmlinux instead. +# For delay-objtool (IBT or LTO), objtool doesn't run on individual translation +# units. Instead it runs on vmlinux.o. +# +# For !delay-objtool + CONFIG_NOINSTR_VALIDATION, it runs on both translation +# units and vmlinux.o, with the latter only used for noinstr/unret validation. objtool-enabled := $(or $(delay-objtool),$(CONFIG_NOINSTR_VALIDATION)) -vmlinux-objtool-args-$(delay-objtool) += $(objtool-args-y) +ifeq ($(delay-objtool),y) +vmlinux-objtool-args-y += $(objtool-args-y) +else +vmlinux-objtool-args-$(CONFIG_OBJTOOL_WERROR) += --Werror +endif + vmlinux-objtool-args-$(CONFIG_GCOV_KERNEL) += --no-unreachable vmlinux-objtool-args-$(CONFIG_NOINSTR_VALIDATION) += --noinstr \ $(if $(or $(CONFIG_MITIGATION_UNRET_ENTRY),$(CONFIG_MITIGATION_SRSO)), --unret) From 4fab2d7628dd38f3fa8a5e615199350ecaeb17a8 Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Mon, 24 Mar 2025 14:55:56 -0700 Subject: [PATCH 06/36] 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(-) 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, From 6b023c7842048c4bbeede802f3cf36b96c7a8b25 Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Mon, 24 Mar 2025 14:55:57 -0700 Subject: [PATCH 07/36] 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(+) 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; } From e1a9dda74dbffbc3fa2069ff418a1876dc99fb14 Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Mon, 24 Mar 2025 14:55:58 -0700 Subject: [PATCH 08/36] 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(-) 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) From c5995abe15476798b2e2f0163a33404c41aafc8f Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Mon, 24 Mar 2025 14:55:59 -0700 Subject: [PATCH 09/36] 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/builtin-check.c | 37 ++- tools/objtool/check.c | 368 ++++++++++++------------ tools/objtool/elf.c | 22 +- tools/objtool/include/objtool/objtool.h | 2 +- tools/objtool/include/objtool/warn.h | 13 +- tools/objtool/objtool.c | 11 +- 6 files changed, 232 insertions(+), 221 deletions(-) diff --git a/tools/objtool/builtin-check.c b/tools/objtool/builtin-check.c index 5f761f420b8c..c973a751fb7d 100644 --- a/tools/objtool/builtin-check.c +++ b/tools/objtool/builtin-check.c @@ -8,15 +8,12 @@ #include #include #include +#include #include #include #include #include - -#define ERROR(format, ...) \ - fprintf(stderr, \ - "error: objtool: " format "\n", \ - ##__VA_ARGS__) +#include const char *objname; @@ -139,22 +136,22 @@ int cmd_parse_options(int argc, const char **argv, const char * const usage[]) static bool opts_valid(void) { if (opts.mnop && !opts.mcount) { - ERROR("--mnop requires --mcount"); + WARN("--mnop requires --mcount"); return false; } if (opts.noinstr && !opts.link) { - ERROR("--noinstr requires --link"); + WARN("--noinstr requires --link"); return false; } if (opts.ibt && !opts.link) { - ERROR("--ibt requires --link"); + WARN("--ibt requires --link"); return false; } if (opts.unret && !opts.link) { - ERROR("--unret requires --link"); + WARN("--unret requires --link"); return false; } @@ -171,7 +168,7 @@ static bool opts_valid(void) opts.static_call || opts.uaccess) { if (opts.dump_orc) { - ERROR("--dump can't be combined with other actions"); + WARN("--dump can't be combined with other actions"); return false; } @@ -181,7 +178,7 @@ static bool opts_valid(void) if (opts.dump_orc) return true; - ERROR("At least one action required"); + WARN("At least one action required"); return false; } @@ -194,30 +191,30 @@ static int copy_file(const char *src, const char *dst) src_fd = open(src, O_RDONLY); if (src_fd == -1) { - ERROR("can't open '%s' for reading", src); + WARN("can't open %s for reading: %s", src, strerror(errno)); return 1; } dst_fd = open(dst, O_WRONLY | O_CREAT | O_TRUNC, 0400); if (dst_fd == -1) { - ERROR("can't open '%s' for writing", dst); + WARN("can't open %s for writing: %s", dst, strerror(errno)); return 1; } if (fstat(src_fd, &stat) == -1) { - perror("fstat"); + WARN_GLIBC("fstat"); return 1; } if (fchmod(dst_fd, stat.st_mode) == -1) { - perror("fchmod"); + WARN_GLIBC("fchmod"); return 1; } for (to_copy = stat.st_size; to_copy > 0; to_copy -= copied) { copied = sendfile(dst_fd, src_fd, &offset, to_copy); if (copied == -1) { - perror("sendfile"); + WARN_GLIBC("sendfile"); return 1; } } @@ -233,14 +230,14 @@ static char **save_argv(int argc, const char **argv) orig_argv = calloc(argc, sizeof(char *)); if (!orig_argv) { - perror("calloc"); + WARN_GLIBC("calloc"); return NULL; } for (int i = 0; i < argc; i++) { orig_argv[i] = strdup(argv[i]); if (!orig_argv[i]) { - perror("strdup"); + WARN_GLIBC("strdup(%s)", orig_argv[i]); return NULL; } }; @@ -285,7 +282,7 @@ int objtool_run(int argc, const char **argv) goto err; if (!opts.link && has_multiple_files(file->elf)) { - ERROR("Linked object requires --link"); + WARN("Linked object requires --link"); goto err; } @@ -313,7 +310,7 @@ err: */ backup = malloc(strlen(objname) + strlen(ORIG_SUFFIX) + 1); if (!backup) { - perror("malloc"); + WARN_GLIBC("malloc"); return 1; } 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); diff --git a/tools/objtool/elf.c b/tools/objtool/elf.c index 0f38167bd840..b352a78b596c 100644 --- a/tools/objtool/elf.c +++ b/tools/objtool/elf.c @@ -331,7 +331,7 @@ static int read_sections(struct elf *elf) elf->section_data = calloc(sections_nr, sizeof(*sec)); if (!elf->section_data) { - perror("calloc"); + WARN_GLIBC("calloc"); return -1; } for (i = 0; i < sections_nr; i++) { @@ -467,7 +467,7 @@ static int read_symbols(struct elf *elf) elf->symbol_data = calloc(symbols_nr, sizeof(*sym)); if (!elf->symbol_data) { - perror("calloc"); + WARN_GLIBC("calloc"); return -1; } for (i = 0; i < symbols_nr; i++) { @@ -799,7 +799,7 @@ elf_create_section_symbol(struct elf *elf, struct section *sec) struct symbol *sym = calloc(1, sizeof(*sym)); if (!sym) { - perror("malloc"); + WARN_GLIBC("malloc"); return NULL; } @@ -829,7 +829,7 @@ elf_create_prefix_symbol(struct elf *elf, struct symbol *orig, long size) char *name = malloc(namelen); if (!sym || !name) { - perror("malloc"); + WARN_GLIBC("malloc"); return NULL; } @@ -963,7 +963,7 @@ static int read_relocs(struct elf *elf) nr_reloc = 0; rsec->relocs = calloc(sec_num_entries(rsec), sizeof(*reloc)); if (!rsec->relocs) { - perror("calloc"); + WARN_GLIBC("calloc"); return -1; } for (i = 0; i < sec_num_entries(rsec); i++) { @@ -1005,7 +1005,7 @@ struct elf *elf_open_read(const char *name, int flags) elf = malloc(sizeof(*elf)); if (!elf) { - perror("malloc"); + WARN_GLIBC("malloc"); return NULL; } memset(elf, 0, sizeof(*elf)); @@ -1099,7 +1099,7 @@ struct section *elf_create_section(struct elf *elf, const char *name, sec = malloc(sizeof(*sec)); if (!sec) { - perror("malloc"); + WARN_GLIBC("malloc"); return NULL; } memset(sec, 0, sizeof(*sec)); @@ -1114,7 +1114,7 @@ struct section *elf_create_section(struct elf *elf, const char *name, sec->name = strdup(name); if (!sec->name) { - perror("strdup"); + WARN_GLIBC("strdup"); return NULL; } @@ -1132,7 +1132,7 @@ struct section *elf_create_section(struct elf *elf, const char *name, if (size) { sec->data->d_buf = malloc(size); if (!sec->data->d_buf) { - perror("malloc"); + WARN_GLIBC("malloc"); return NULL; } memset(sec->data->d_buf, 0, size); @@ -1179,7 +1179,7 @@ static struct section *elf_create_rela_section(struct elf *elf, rsec_name = malloc(strlen(sec->name) + strlen(".rela") + 1); if (!rsec_name) { - perror("malloc"); + WARN_GLIBC("malloc"); return NULL; } strcpy(rsec_name, ".rela"); @@ -1199,7 +1199,7 @@ static struct section *elf_create_rela_section(struct elf *elf, rsec->relocs = calloc(sec_num_entries(rsec), sizeof(struct reloc)); if (!rsec->relocs) { - perror("calloc"); + WARN_GLIBC("calloc"); return NULL; } diff --git a/tools/objtool/include/objtool/objtool.h b/tools/objtool/include/objtool/objtool.h index 94a33ee7b363..c0dc86a78ff6 100644 --- a/tools/objtool/include/objtool/objtool.h +++ b/tools/objtool/include/objtool/objtool.h @@ -41,7 +41,7 @@ struct objtool_file { struct objtool_file *objtool_open_read(const char *_objname); -void objtool_pv_add(struct objtool_file *file, int idx, struct symbol *func); +int objtool_pv_add(struct objtool_file *file, int idx, struct symbol *func); int check(struct objtool_file *file); int orc_dump(const char *objname); diff --git a/tools/objtool/include/objtool/warn.h b/tools/objtool/include/objtool/warn.h index e72b9d630551..b29ac144e4f5 100644 --- a/tools/objtool/include/objtool/warn.h +++ b/tools/objtool/include/objtool/warn.h @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -43,8 +44,9 @@ static inline char *offstr(struct section *sec, unsigned long offset) #define WARN(format, ...) \ fprintf(stderr, \ - "%s: %s: objtool: " format "\n", \ - objname, \ + "%s%s%s: objtool: " format "\n", \ + objname ?: "", \ + objname ? ": " : "", \ opts.werror ? "error" : "warning", \ ##__VA_ARGS__) @@ -83,7 +85,10 @@ static inline char *offstr(struct section *sec, unsigned long offset) } \ }) -#define WARN_ELF(format, ...) \ - WARN(format ": %s", ##__VA_ARGS__, elf_errmsg(-1)) +#define WARN_ELF(format, ...) \ + WARN("%s: " format " failed: %s", __func__, ##__VA_ARGS__, elf_errmsg(-1)) + +#define WARN_GLIBC(format, ...) \ + WARN("%s: " format " failed: %s", __func__, ##__VA_ARGS__, strerror(errno)) #endif /* _WARN_H */ diff --git a/tools/objtool/objtool.c b/tools/objtool/objtool.c index 1c73fb62fd57..e4b49c534e4d 100644 --- a/tools/objtool/objtool.c +++ b/tools/objtool/objtool.c @@ -44,14 +44,14 @@ struct objtool_file *objtool_open_read(const char *filename) return &file; } -void objtool_pv_add(struct objtool_file *f, int idx, struct symbol *func) +int objtool_pv_add(struct objtool_file *f, int idx, struct symbol *func) { if (!opts.noinstr) - return; + return 0; if (!f->pv_ops) { WARN("paravirt confusion"); - return; + return -1; } /* @@ -60,14 +60,15 @@ void objtool_pv_add(struct objtool_file *f, int idx, struct symbol *func) */ if (!strcmp(func->name, "_paravirt_nop") || !strcmp(func->name, "_paravirt_ident_64")) - return; + return 0; /* already added this function */ if (!list_empty(&func->pv_target)) - return; + return 0; list_add(&func->pv_target, &f->pv_ops[idx].targets); f->pv_ops[idx].clean = false; + return 0; } int main(int argc, const char **argv) From d39f82a058c0269392a797cb04f2a6064e5dcab6 Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Mon, 24 Mar 2025 14:56:00 -0700 Subject: [PATCH 10/36] 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 --- scripts/Makefile.lib | 2 +- tools/objtool/builtin-check.c | 113 ++++++++++++------------ tools/objtool/check.c | 23 ++--- tools/objtool/include/objtool/builtin.h | 6 +- 4 files changed, 73 insertions(+), 71 deletions(-) diff --git a/scripts/Makefile.lib b/scripts/Makefile.lib index 57620b439a1f..b93597420daf 100644 --- a/scripts/Makefile.lib +++ b/scripts/Makefile.lib @@ -277,7 +277,7 @@ objtool-args-$(CONFIG_HAVE_STATIC_CALL_INLINE) += --static-call objtool-args-$(CONFIG_HAVE_UACCESS_VALIDATION) += --uaccess objtool-args-$(CONFIG_GCOV_KERNEL) += --no-unreachable objtool-args-$(CONFIG_PREFIX_SYMBOLS) += --prefix=$(CONFIG_FUNCTION_PADDING_BYTES) -objtool-args-$(CONFIG_OBJTOOL_WERROR) += --Werror --backtrace +objtool-args-$(CONFIG_OBJTOOL_WERROR) += --Werror objtool-args = $(objtool-args-y) \ $(if $(delay-objtool), --link) \ diff --git a/tools/objtool/builtin-check.c b/tools/objtool/builtin-check.c index c973a751fb7d..2bdff910430e 100644 --- a/tools/objtool/builtin-check.c +++ b/tools/objtool/builtin-check.c @@ -15,8 +15,11 @@ #include #include -const char *objname; +#define ORIG_SUFFIX ".orig" +int orig_argc; +static char **orig_argv; +const char *objname; struct opts opts; static const char * const check_usage[] = { @@ -224,39 +227,73 @@ static int copy_file(const char *src, const char *dst) return 0; } -static char **save_argv(int argc, const char **argv) +static void save_argv(int argc, const char **argv) { - char **orig_argv; - orig_argv = calloc(argc, sizeof(char *)); if (!orig_argv) { WARN_GLIBC("calloc"); - return NULL; + exit(1); } for (int i = 0; i < argc; i++) { orig_argv[i] = strdup(argv[i]); if (!orig_argv[i]) { WARN_GLIBC("strdup(%s)", orig_argv[i]); - return NULL; + exit(1); } }; - - return orig_argv; } -#define ORIG_SUFFIX ".orig" +void print_args(void) +{ + char *backup = NULL; + + if (opts.output || opts.dryrun) + goto print; + + /* + * Make a backup before kbuild deletes the file so the error + * can be recreated without recompiling or relinking. + */ + backup = malloc(strlen(objname) + strlen(ORIG_SUFFIX) + 1); + if (!backup) { + WARN_GLIBC("malloc"); + goto print; + } + + strcpy(backup, objname); + strcat(backup, ORIG_SUFFIX); + if (copy_file(objname, backup)) { + backup = NULL; + goto print; + } + +print: + /* + * Print the cmdline args to make it easier to recreate. If '--output' + * wasn't used, add it to the printed args with the backup as input. + */ + fprintf(stderr, "%s", orig_argv[0]); + + for (int i = 1; i < orig_argc; i++) { + char *arg = orig_argv[i]; + + if (backup && !strcmp(arg, objname)) + fprintf(stderr, " %s -o %s", backup, objname); + else + fprintf(stderr, " %s", arg); + } + + fprintf(stderr, "\n"); +} int objtool_run(int argc, const char **argv) { struct objtool_file *file; - char *backup = NULL; - char **orig_argv; int ret = 0; - orig_argv = save_argv(argc, argv); - if (!orig_argv) - return 1; + orig_argc = argc; + save_argv(argc, argv); cmd_parse_options(argc, argv, check_usage); @@ -279,59 +316,19 @@ int objtool_run(int argc, const char **argv) file = objtool_open_read(objname); if (!file) - goto err; + return 1; if (!opts.link && has_multiple_files(file->elf)) { WARN("Linked object requires --link"); - goto err; + return 1; } ret = check(file); if (ret) - goto err; + return ret; if (!opts.dryrun && file->elf->changed && elf_write(file->elf)) - goto err; + return 1; return 0; - -err: - if (opts.dryrun) - goto err_msg; - - if (opts.output) { - unlink(opts.output); - goto err_msg; - } - - /* - * Make a backup before kbuild deletes the file so the error - * can be recreated without recompiling or relinking. - */ - backup = malloc(strlen(objname) + strlen(ORIG_SUFFIX) + 1); - if (!backup) { - WARN_GLIBC("malloc"); - return 1; - } - - strcpy(backup, objname); - strcat(backup, ORIG_SUFFIX); - if (copy_file(objname, backup)) - return 1; - -err_msg: - fprintf(stderr, "%s", orig_argv[0]); - - for (int i = 1; i < argc; i++) { - char *arg = orig_argv[i]; - - if (backup && !strcmp(arg, objname)) - fprintf(stderr, " %s -o %s", backup, objname); - else - fprintf(stderr, " %s", arg); - } - - fprintf(stderr, "\n"); - - return 1; } 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; } diff --git a/tools/objtool/include/objtool/builtin.h b/tools/objtool/include/objtool/builtin.h index 0fafd0f7a209..6b08666fa69d 100644 --- a/tools/objtool/include/objtool/builtin.h +++ b/tools/objtool/include/objtool/builtin.h @@ -43,8 +43,10 @@ struct opts { extern struct opts opts; -extern int cmd_parse_options(int argc, const char **argv, const char * const usage[]); +int cmd_parse_options(int argc, const char **argv, const char * const usage[]); -extern int objtool_run(int argc, const char **argv); +int objtool_run(int argc, const char **argv); + +void print_args(void); #endif /* _BUILTIN_H */ From 24fe172b50b5749c315349e740e4a09e3a0165d5 Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Mon, 24 Mar 2025 14:56:01 -0700 Subject: [PATCH 11/36] objtool: Fix up some outdated references to ENTRY/ENDPROC ENTRY and ENDPROC were deprecated years ago and replaced with SYM_FUNC_{START,END}. Fix up a few outdated references in the objtool documentation and comments. Also fix a few typos. Suggested-by: Brendan Jackman Suggested-by: Miroslav Benes Signed-off-by: Josh Poimboeuf Signed-off-by: Ingo Molnar Cc: Linus Torvalds Link: https://lore.kernel.org/r/5eb7e06e1a0e87aaeda8d583ab060e7638a6ea8e.1742852846.git.jpoimboe@kernel.org --- include/linux/linkage.h | 4 ---- include/linux/objtool.h | 2 +- tools/objtool/Documentation/objtool.txt | 10 +++++----- 3 files changed, 6 insertions(+), 10 deletions(-) diff --git a/include/linux/linkage.h b/include/linux/linkage.h index 5c8865bb59d9..b11660b706c5 100644 --- a/include/linux/linkage.h +++ b/include/linux/linkage.h @@ -134,10 +134,6 @@ .size name, .-name #endif -/* If symbol 'name' is treated as a subroutine (gets called, and returns) - * then please use ENDPROC to mark 'name' as STT_FUNC for the benefit of - * static analysis tools such as stack depth analyzer. - */ #ifndef ENDPROC /* deprecated, use SYM_FUNC_END */ #define ENDPROC(name) \ diff --git a/include/linux/objtool.h b/include/linux/objtool.h index 3ca965a2ddc8..366ad004d794 100644 --- a/include/linux/objtool.h +++ b/include/linux/objtool.h @@ -69,7 +69,7 @@ * In asm, there are two kinds of code: normal C-type callable functions and * the rest. The normal callable functions can be called by other code, and * don't do anything unusual with the stack. Such normal callable functions - * are annotated with the ENTRY/ENDPROC macros. Most asm code falls in this + * are annotated with SYM_FUNC_{START,END}. Most asm code falls in this * category. In this case, no special debugging annotations are needed because * objtool can automatically generate the ORC data for the ORC unwinder to read * at runtime. diff --git a/tools/objtool/Documentation/objtool.txt b/tools/objtool/Documentation/objtool.txt index 28ac57b9e102..9e97fc25b2d8 100644 --- a/tools/objtool/Documentation/objtool.txt +++ b/tools/objtool/Documentation/objtool.txt @@ -34,7 +34,7 @@ Objtool has the following features: - Return thunk annotation -- annotates all return thunk sites so kernel can patch them inline, depending on enabled mitigations -- Return thunk training valiation -- validate that all entry paths +- Return thunk untraining validation -- validate that all entry paths untrain a "safe return" before the first return (or call) - Non-instrumentation validation -- validates non-instrumentable @@ -281,8 +281,8 @@ the objtool maintainers. If the error is for an asm file, and func() is indeed a callable function, add proper frame pointer logic using the FRAME_BEGIN and FRAME_END macros. Otherwise, if it's not a callable function, remove - its ELF function annotation by changing ENDPROC to END, and instead - use the manual unwind hint macros in asm/unwind_hints.h. + its ELF function annotation by using SYM_CODE_{START,END} and use the + manual unwind hint macros in asm/unwind_hints.h. If it's a GCC-compiled .c file, the error may be because the function uses an inline asm() statement which has a "call" instruction. An @@ -352,7 +352,7 @@ the objtool maintainers. This is a kernel entry/exit instruction like sysenter or iret. Such instructions aren't allowed in a callable function, and are most likely part of the kernel entry code. Such code should probably be - placed in a SYM_FUNC_CODE block with unwind hints. + placed in a SYM_CODE_{START,END} block with unwind hints. 6. file.o: warning: objtool: func()+0x26: sibling call from callable instruction with modified stack frame @@ -381,7 +381,7 @@ the objtool maintainers. Another possibility is that the code has some asm or inline asm which does some unusual things to the stack or the frame pointer. In such - cases it's probably appropriate to use SYM_FUNC_CODE with unwind + cases it's probably appropriate to use SYM_CODE_{START,END} with unwind hints. From 876a4bce3849b235d752d13ec3180e15a35e52de Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Mon, 24 Mar 2025 14:56:02 -0700 Subject: [PATCH 12/36] objtool: Remove --no-unreachable for noinstr-only vmlinux.o runs For (!X86_KERNEL_IBT && !LTO_CLANG && NOINSTR_VALIDATION), objtool runs on both translation units and vmlinux.o. The vmlinux.o run only does noinstr/noret validation. In that case --no-unreachable has no effect. Remove it. Note that for ((X86_KERNEL_IBT || LTO_CLANG) && KCOV), --no-unreachable still gets set in objtool-args-y by scripts/Makefile.lib. Signed-off-by: Josh Poimboeuf Signed-off-by: Ingo Molnar Cc: Linus Torvalds Link: https://lore.kernel.org/r/05414246a0124db2f21b0d071b652aa9043d039d.1742852847.git.jpoimboe@kernel.org --- scripts/Makefile.vmlinux_o | 1 - 1 file changed, 1 deletion(-) diff --git a/scripts/Makefile.vmlinux_o b/scripts/Makefile.vmlinux_o index f476f5605029..938c7457717e 100644 --- a/scripts/Makefile.vmlinux_o +++ b/scripts/Makefile.vmlinux_o @@ -44,7 +44,6 @@ else vmlinux-objtool-args-$(CONFIG_OBJTOOL_WERROR) += --Werror endif -vmlinux-objtool-args-$(CONFIG_GCOV_KERNEL) += --no-unreachable vmlinux-objtool-args-$(CONFIG_NOINSTR_VALIDATION) += --noinstr \ $(if $(or $(CONFIG_MITIGATION_UNRET_ENTRY),$(CONFIG_MITIGATION_SRSO)), --unret) From a8d39a62c6f5ad76b8a1ebfbf10dd9fe8ca2bbcc Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Mon, 24 Mar 2025 14:56:03 -0700 Subject: [PATCH 13/36] 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(-) 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; } From 76e51db43fe4aaaebcc5ddda67b0807f7c9bdecc Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Mon, 24 Mar 2025 14:56:04 -0700 Subject: [PATCH 14/36] objtool, spi: amd: Fix out-of-bounds stack access in amd_set_spi_freq() If speed_hz < AMD_SPI_MIN_HZ, amd_set_spi_freq() iterates over the entire amd_spi_freq array without breaking out early, causing 'i' to go beyond the array bounds. Fix that by stopping the loop when it gets to the last entry, so the low speed_hz value gets clamped up to AMD_SPI_MIN_HZ. Fixes the following warning with an UBSAN kernel: drivers/spi/spi-amd.o: error: objtool: amd_set_spi_freq() falls through to next function amd_spi_set_opcode() Fixes: 3fe26121dc3a ("spi: amd: Configure device speed") Reported-by: kernel test robot Signed-off-by: Josh Poimboeuf Signed-off-by: Ingo Molnar Acked-by: Mark Brown Cc: Raju Rangoju Cc: Linus Torvalds Link: https://lore.kernel.org/r/78fef0f2434f35be9095bcc9ffa23dd8cab667b9.1742852847.git.jpoimboe@kernel.org Closes: https://lore.kernel.org/r/202503161828.RUk9EhWx-lkp@intel.com/ --- drivers/spi/spi-amd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/spi/spi-amd.c b/drivers/spi/spi-amd.c index c85997478b81..17fc0b17e756 100644 --- a/drivers/spi/spi-amd.c +++ b/drivers/spi/spi-amd.c @@ -302,7 +302,7 @@ static void amd_set_spi_freq(struct amd_spi *amd_spi, u32 speed_hz) { unsigned int i, spd7_val, alt_spd; - for (i = 0; i < ARRAY_SIZE(amd_spi_freq); i++) + for (i = 0; i < ARRAY_SIZE(amd_spi_freq)-1; i++) if (speed_hz >= amd_spi_freq[i].speed_hz) break; From 107a23185d990e3df6638d9a84c835f963fe30a6 Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Mon, 24 Mar 2025 14:56:05 -0700 Subject: [PATCH 15/36] objtool, nvmet: Fix out-of-bounds stack access in nvmet_ctrl_state_show() The csts_state_names[] array only has six sparse entries, but the iteration code in nvmet_ctrl_state_show() iterates seven, resulting in a potential out-of-bounds stack read. Fix that. Fixes the following warning with an UBSAN kernel: vmlinux.o: warning: objtool: .text.nvmet_ctrl_state_show: unexpected end of section Fixes: 649fd41420a8 ("nvmet: add debugfs support") Reported-by: kernel test robot Signed-off-by: Josh Poimboeuf Signed-off-by: Ingo Molnar Cc: Christoph Hellwig Cc: Sagi Grimberg Cc: Chaitanya Kulkarni Cc: Linus Torvalds Link: https://lore.kernel.org/r/f1f60858ee7a941863dc7f5506c540cb9f97b5f6.1742852847.git.jpoimboe@kernel.org Closes: https://lore.kernel.org/oe-kbuild-all/202503171547.LlCTJLQL-lkp@intel.com/ --- drivers/nvme/target/debugfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/nvme/target/debugfs.c b/drivers/nvme/target/debugfs.c index 220c7391fc19..c6571fbd35e3 100644 --- a/drivers/nvme/target/debugfs.c +++ b/drivers/nvme/target/debugfs.c @@ -78,7 +78,7 @@ static int nvmet_ctrl_state_show(struct seq_file *m, void *p) bool sep = false; int i; - for (i = 0; i < 7; i++) { + for (i = 0; i < ARRAY_SIZE(csts_state_names); i++) { int state = BIT(i); if (!(ctrl->csts & state)) From e63d465f59011dede0a0f1d21718b59a64c3ff5c Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Mon, 24 Mar 2025 14:56:06 -0700 Subject: [PATCH 16/36] objtool, media: dib8000: Prevent divide-by-zero in dib8000_set_dds() If dib8000_set_dds()'s call to dib8000_read32() returns zero, the result is a divide-by-zero. Prevent that from happening. Fixes the following warning with an UBSAN kernel: drivers/media/dvb-frontends/dib8000.o: warning: objtool: dib8000_tune() falls through to next function dib8096p_cfg_DibRx() Fixes: 173a64cb3fcf ("[media] dib8000: enhancement") Reported-by: kernel test robot Signed-off-by: Josh Poimboeuf Signed-off-by: Ingo Molnar Cc: Mauro Carvalho Chehab Cc: Linus Torvalds Link: https://lore.kernel.org/r/bd1d504d930ae3f073b1e071bcf62cae7708773c.1742852847.git.jpoimboe@kernel.org Closes: https://lore.kernel.org/r/202503210602.fvH5DO1i-lkp@intel.com/ --- drivers/media/dvb-frontends/dib8000.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/media/dvb-frontends/dib8000.c b/drivers/media/dvb-frontends/dib8000.c index 2f5165918163..cfe59c3255f7 100644 --- a/drivers/media/dvb-frontends/dib8000.c +++ b/drivers/media/dvb-frontends/dib8000.c @@ -2701,8 +2701,11 @@ static void dib8000_set_dds(struct dib8000_state *state, s32 offset_khz) u8 ratio; if (state->revision == 0x8090) { + u32 internal = dib8000_read32(state, 23) / 1000; + ratio = 4; - unit_khz_dds_val = (1<<26) / (dib8000_read32(state, 23) / 1000); + + unit_khz_dds_val = (1<<26) / (internal ?: 1); if (offset_khz < 0) dds = (1 << 26) - (abs_offset_khz * unit_khz_dds_val); else From 72c774aa9d1e16bfd247096935e7dae194d84929 Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Mon, 24 Mar 2025 14:56:07 -0700 Subject: [PATCH 17/36] 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 --- kernel/panic.c | 6 ++++++ tools/objtool/check.c | 5 ++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/kernel/panic.c b/kernel/panic.c index d8635d5cecb2..f9f0c5148f6a 100644 --- a/kernel/panic.c +++ b/kernel/panic.c @@ -832,9 +832,15 @@ device_initcall(register_warn_debugfs); */ __visible noinstr void __stack_chk_fail(void) { + unsigned long flags; + instrumentation_begin(); + flags = user_access_save(); + panic("stack-protector: Kernel stack is corrupted in: %pB", __builtin_return_address(0)); + + user_access_restore(flags); instrumentation_end(); } EXPORT_SYMBOL(__stack_chk_fail); 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", From 7501153750b47416c9891038330359fad0205839 Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Mon, 24 Mar 2025 14:56:08 -0700 Subject: [PATCH 18/36] objtool, Input: cyapa - Remove undefined behavior in cyapa_update_fw_store() In cyapa_update_fw_store(), if 'count' is zero, the write to fw_name[count-1] underflows the array. Presumably that's not possible in normal operation, as its caller sysfs_kf_write() already checks for zero. Regardless it's a good idea to check for the error explicitly and avoid undefined behavior. Fixes the following warning with an UBSAN kernel: vmlinux.o: warning: objtool: .text.cyapa_update_fw_store: unexpected end of section Reported-by: kernel test robot Signed-off-by: Josh Poimboeuf Signed-off-by: Ingo Molnar Cc: Dmitry Torokhov Cc: Linus Torvalds Link: https://lore.kernel.org/r/73ae0bb3c656735890d914b74c9d6bb40c25d3cd.1742852847.git.jpoimboe@kernel.org Closes: https://lore.kernel.org/oe-kbuild-all/202503171547.LlCTJLQL-lkp@intel.com/ --- drivers/input/mouse/cyapa.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/input/mouse/cyapa.c b/drivers/input/mouse/cyapa.c index 2f2d925a55d7..00c87c0532a6 100644 --- a/drivers/input/mouse/cyapa.c +++ b/drivers/input/mouse/cyapa.c @@ -1080,8 +1080,8 @@ static ssize_t cyapa_update_fw_store(struct device *dev, char fw_name[NAME_MAX]; int ret, error; - if (count >= NAME_MAX) { - dev_err(dev, "File name too long\n"); + if (!count || count >= NAME_MAX) { + dev_err(dev, "Bad file name size\n"); return -EINVAL; } From 060aed9c0093b341480770457093449771cf1496 Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Mon, 24 Mar 2025 14:56:09 -0700 Subject: [PATCH 19/36] objtool, ASoC: codecs: wcd934x: Remove potential undefined behavior in wcd934x_slim_irq_handler() If 'port_id' is negative, the shift counts in wcd934x_slim_irq_handler() also become negative, resulting in undefined behavior due to shift out of bounds. If I'm reading the code correctly, that appears to be not possible, but with KCOV enabled, Clang's range analysis isn't always able to determine that and generates undefined behavior. As a result the code generation isn't optimal, and undefined behavior should be avoided regardless. Improve code generation and remove the undefined behavior by converting the signed variables to unsigned. Fixes the following warning with UBSAN: sound/soc/codecs/snd-soc-wcd934x.o: warning: objtool: .text.wcd934x_slim_irq_handler: unexpected end of section Reported-by: kernel test robot Signed-off-by: Josh Poimboeuf Signed-off-by: Ingo Molnar Acked-by: Mark Brown Cc: Srinivas Kandagatla Cc: Liam Girdwood Cc: Jaroslav Kysela Cc: Takashi Iwai Cc: Linus Torvalds Link: https://lore.kernel.org/r/7e863839ec7301bf9c0f429a03873d44e484c31c.1742852847.git.jpoimboe@kernel.org Closes: https://lore.kernel.org/oe-kbuild-all/202503180044.oH9gyPeg-lkp@intel.com/ --- sound/soc/codecs/wcd934x.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/codecs/wcd934x.c b/sound/soc/codecs/wcd934x.c index 910852eb9698..c7f1b28f3b23 100644 --- a/sound/soc/codecs/wcd934x.c +++ b/sound/soc/codecs/wcd934x.c @@ -2273,7 +2273,7 @@ static irqreturn_t wcd934x_slim_irq_handler(int irq, void *data) { struct wcd934x_codec *wcd = data; unsigned long status = 0; - int i, j, port_id; + unsigned int i, j, port_id; unsigned int val, int_val = 0; irqreturn_t ret = IRQ_NONE; bool tx; From 29c578c848402a34e8c8e115bf66cb6008b77062 Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Mon, 24 Mar 2025 14:56:10 -0700 Subject: [PATCH 20/36] objtool, regulator: rk808: Remove potential undefined behavior in rk806_set_mode_dcdc() If 'ctr_bit' is negative, the shift counts become negative, causing a shift of bounds and undefined behavior. Presumably that's not possible in normal operation, but the code generation isn't optimal. And undefined behavior should be avoided regardless. Improve code generation and remove the undefined behavior by converting the signed variables to unsigned. Fixes the following warning with an UBSAN kernel: vmlinux.o: warning: objtool: rk806_set_mode_dcdc() falls through to next function rk806_get_mode_dcdc() vmlinux.o: warning: objtool: .text.rk806_set_mode_dcdc: unexpected end of section Reported-by: kernel test robot Signed-off-by: Josh Poimboeuf Signed-off-by: Ingo Molnar Acked-by: Mark Brown Cc: Liam Girdwood Cc: Linus Torvalds Link: https://lore.kernel.org/r/2023abcddf3f524ba478d64339996f25dc4097d2.1742852847.git.jpoimboe@kernel.org Closes: https://lore.kernel.org/oe-kbuild-all/202503182350.52KeHGD4-lkp@intel.com/ --- drivers/regulator/rk808-regulator.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/regulator/rk808-regulator.c b/drivers/regulator/rk808-regulator.c index 7d82bd1b36df..1e8142479656 100644 --- a/drivers/regulator/rk808-regulator.c +++ b/drivers/regulator/rk808-regulator.c @@ -270,8 +270,8 @@ static const unsigned int rk817_buck1_4_ramp_table[] = { static int rk806_set_mode_dcdc(struct regulator_dev *rdev, unsigned int mode) { - int rid = rdev_get_id(rdev); - int ctr_bit, reg; + unsigned int rid = rdev_get_id(rdev); + unsigned int ctr_bit, reg; reg = RK806_POWER_FPWM_EN0 + rid / 8; ctr_bit = rid % 8; From 05026ea01e95ffdeb0e5ac8fb7fb1b551e3a8726 Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Mon, 24 Mar 2025 14:56:12 -0700 Subject: [PATCH 21/36] objtool, lkdtm: Obfuscate the do_nothing() pointer If execute_location()'s memcpy of do_nothing() gets inlined and unrolled by the compiler, it copies one word at a time: mov 0x0(%rip),%rax R_X86_64_PC32 .text+0x1374 mov %rax,0x38(%rbx) mov 0x0(%rip),%rax R_X86_64_PC32 .text+0x136c mov %rax,0x30(%rbx) ... Those .text references point to the middle of the function, causing objtool to complain about their lack of ENDBR. Prevent that by resolving the function pointer at runtime rather than build time. This fixes the following warning: drivers/misc/lkdtm/lkdtm.o: warning: objtool: execute_location+0x23: relocation to !ENDBR: .text+0x1378 Reported-by: kernel test robot Signed-off-by: Josh Poimboeuf Signed-off-by: Ingo Molnar Reviewed-by: Kees Cook Cc: Arnd Bergmann Cc: Greg Kroah-Hartman Cc: Linus Torvalds Link: https://lore.kernel.org/r/30b9abffbddeb43c4f6320b1270fa9b4d74c54ed.1742852847.git.jpoimboe@kernel.org Closes: https://lore.kernel.org/oe-kbuild-all/202503191453.uFfxQy5R-lkp@intel.com/ --- drivers/misc/lkdtm/perms.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/drivers/misc/lkdtm/perms.c b/drivers/misc/lkdtm/perms.c index 5b861dbff27e..6c24426104ba 100644 --- a/drivers/misc/lkdtm/perms.c +++ b/drivers/misc/lkdtm/perms.c @@ -28,6 +28,13 @@ static const unsigned long rodata = 0xAA55AA55; /* This is marked __ro_after_init, so it should ultimately be .rodata. */ static unsigned long ro_after_init __ro_after_init = 0x55AA5500; +/* + * This is a pointer to do_nothing() which is initialized at runtime rather + * than build time to avoid objtool IBT validation warnings caused by an + * inlined unrolled memcpy() in execute_location(). + */ +static void __ro_after_init *do_nothing_ptr; + /* * This just returns to the caller. It is designed to be copied into * non-executable memory regions. @@ -65,13 +72,12 @@ static noinline __nocfi void execute_location(void *dst, bool write) { void (*func)(void); func_desc_t fdesc; - void *do_nothing_text = dereference_function_descriptor(do_nothing); - pr_info("attempting ok execution at %px\n", do_nothing_text); + pr_info("attempting ok execution at %px\n", do_nothing_ptr); do_nothing(); if (write == CODE_WRITE) { - memcpy(dst, do_nothing_text, EXEC_SIZE); + memcpy(dst, do_nothing_ptr, EXEC_SIZE); flush_icache_range((unsigned long)dst, (unsigned long)dst + EXEC_SIZE); } @@ -267,6 +273,8 @@ static void lkdtm_ACCESS_NULL(void) void __init lkdtm_perms_init(void) { + do_nothing_ptr = dereference_function_descriptor(do_nothing); + /* Make sure we can write to __ro_after_init values during __init */ ro_after_init |= 0xAA; } From d9a595c3850ea4383628115df2bb533af3b29f4f Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Tue, 25 Mar 2025 18:30:37 -0700 Subject: [PATCH 22/36] objtool: Fix NULL printf() '%s' argument in builtin-check.c:save_argv() It's probably not the best idea to pass a string pointer to printf() right after confirming said pointer is NULL. Fix the typo and use argv[i] instead. Fixes: c5995abe1547 ("objtool: Improve error handling") Reported-by: Stephen Rothwell Signed-off-by: Josh Poimboeuf Signed-off-by: Ingo Molnar Tested-by: Stephen Rothwell Link: https://lore.kernel.org/r/a814ed8b08fb410be29498a20a5fbbb26e907ecf.1742952512.git.jpoimboe@kernel.org Closes: https://lore.kernel.org/20250326103854.309e3c60@canb.auug.org.au --- tools/objtool/builtin-check.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/objtool/builtin-check.c b/tools/objtool/builtin-check.c index 2bdff910430e..e364ab6345d3 100644 --- a/tools/objtool/builtin-check.c +++ b/tools/objtool/builtin-check.c @@ -238,7 +238,7 @@ static void save_argv(int argc, const char **argv) for (int i = 0; i < argc; i++) { orig_argv[i] = strdup(argv[i]); if (!orig_argv[i]) { - WARN_GLIBC("strdup(%s)", orig_argv[i]); + WARN_GLIBC("strdup(%s)", argv[i]); exit(1); } }; From 69d41d6dafff0967565b971d950bd10443e4076c Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Thu, 27 Mar 2025 22:04:21 -0700 Subject: [PATCH 23/36] 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(-) 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))) From b5e2cc57f551a1a1e2c0ea36f77c1e26d3d13c35 Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Thu, 27 Mar 2025 22:04:22 -0700 Subject: [PATCH 24/36] 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(+) 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; From ae958b12940bcd4ffa32c44684e4f2878bc5e140 Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Thu, 27 Mar 2025 22:04:23 -0700 Subject: [PATCH 25/36] objtool, drm/vmwgfx: Don't ignore vmw_send_msg() for ORC The following commit: 0b0d81e3b733 ("objtool, drm/vmwgfx: Fix "duplicate frame pointer save" warning") ... marked vmw_send_msg() STACK_FRAME_NON_STANDARD because it uses RBP in a non-standard way which violates frame pointer convention. That issue only affects the frame pointer unwinder. Remove the annotation for ORC. Signed-off-by: Josh Poimboeuf Signed-off-by: Ingo Molnar Link: https://lore.kernel.org/r/eff3102a7eeb77b4420fcb5e9d9cd9dd81d4514a.1743136205.git.jpoimboe@kernel.org --- drivers/gpu/drm/vmwgfx/vmwgfx_msg.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_msg.c b/drivers/gpu/drm/vmwgfx/vmwgfx_msg.c index 1f15990d3934..1d9a42cbc88f 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_msg.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_msg.c @@ -289,7 +289,7 @@ static int vmw_send_msg(struct rpc_channel *channel, const char *msg) return -EINVAL; } -STACK_FRAME_NON_STANDARD(vmw_send_msg); +STACK_FRAME_NON_STANDARD_FP(vmw_send_msg); /** From 55c78035a1a8dfb05f1472018ce2a651701adb7d Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Mon, 31 Mar 2025 21:26:36 -0700 Subject: [PATCH 26/36] objtool: Silence more KCOV warnings, part 2 Similar to GCOV, KCOV can leave behind dead code and undefined behavior. Warnings related to those should be ignored. The previous commit: 6b023c784204 ("objtool: Silence more KCOV warnings") ... only did so for CONFIG_CGOV_KERNEL. Also do it for CONFIG_KCOV, but for real this time. Fixes the following warning: vmlinux.o: warning: objtool: synaptics_report_mt_data: unexpected end of section .text.synaptics_report_mt_data Fixes: 6b023c784204 ("objtool: Silence more KCOV warnings") Reported-by: kernel test robot Signed-off-by: Josh Poimboeuf Signed-off-by: Ingo Molnar Cc: Linus Torvalds Link: https://lore.kernel.org/r/a44ba16e194bcbc52c1cef3d3cd9051a62622723.1743481539.git.jpoimboe@kernel.org Closes: https://lore.kernel.org/oe-kbuild-all/202503282236.UhfRsF3B-lkp@intel.com/ --- scripts/Makefile.lib | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/Makefile.lib b/scripts/Makefile.lib index b93597420daf..4d543054f723 100644 --- a/scripts/Makefile.lib +++ b/scripts/Makefile.lib @@ -275,7 +275,7 @@ objtool-args-$(CONFIG_MITIGATION_SLS) += --sls objtool-args-$(CONFIG_STACK_VALIDATION) += --stackval objtool-args-$(CONFIG_HAVE_STATIC_CALL_INLINE) += --static-call objtool-args-$(CONFIG_HAVE_UACCESS_VALIDATION) += --uaccess -objtool-args-$(CONFIG_GCOV_KERNEL) += --no-unreachable +objtool-args-$(or $(CONFIG_GCOV_KERNEL),$(CONFIG_KCOV)) += --no-unreachable objtool-args-$(CONFIG_PREFIX_SYMBOLS) += --prefix=$(CONFIG_FUNCTION_PADDING_BYTES) objtool-args-$(CONFIG_OBJTOOL_WERROR) += --Werror From 0d7597749f5a3ac67851d3836635d084df15fb66 Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Mon, 31 Mar 2025 21:26:37 -0700 Subject: [PATCH 27/36] 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(-) 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; } } From 188d90f817e13b66e03e110eb6f82e8f5f0d654b Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Mon, 31 Mar 2025 21:26:38 -0700 Subject: [PATCH 28/36] 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(-) 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; } From c5610071a69d1c94c70e681874298b4fc6942098 Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Mon, 31 Mar 2025 21:26:39 -0700 Subject: [PATCH 29/36] 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 ++-- tools/objtool/include/objtool/elf.h | 2 +- tools/objtool/include/objtool/warn.h | 14 +++----------- 3 files changed, 6 insertions(+), 14 deletions(-) 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) { diff --git a/tools/objtool/include/objtool/elf.h b/tools/objtool/include/objtool/elf.h index eba04392c6fd..c7c4e87ebe88 100644 --- a/tools/objtool/include/objtool/elf.h +++ b/tools/objtool/include/objtool/elf.h @@ -65,11 +65,11 @@ struct symbol { u8 return_thunk : 1; u8 fentry : 1; u8 profiling_func : 1; + u8 warned : 1; u8 embedded_insn : 1; u8 local_label : 1; u8 frame_pointer : 1; u8 ignore : 1; - u8 warnings : 2; struct list_head pv_target; struct reloc *relocs; }; diff --git a/tools/objtool/include/objtool/warn.h b/tools/objtool/include/objtool/warn.h index b29ac144e4f5..e3ad9b2caf87 100644 --- a/tools/objtool/include/objtool/warn.h +++ b/tools/objtool/include/objtool/warn.h @@ -57,22 +57,14 @@ static inline char *offstr(struct section *sec, unsigned long offset) free(_str); \ }) -#define WARN_LIMIT 2 - #define WARN_INSN(insn, format, ...) \ ({ \ struct instruction *_insn = (insn); \ - BUILD_BUG_ON(WARN_LIMIT > 2); \ - if (!_insn->sym || _insn->sym->warnings < WARN_LIMIT) { \ + if (!_insn->sym || !_insn->sym->warned) \ WARN_FUNC(format, _insn->sec, _insn->offset, \ ##__VA_ARGS__); \ - if (_insn->sym) \ - _insn->sym->warnings++; \ - } else if (_insn->sym && _insn->sym->warnings == WARN_LIMIT) { \ - WARN_FUNC("skipping duplicate warning(s)", \ - _insn->sec, _insn->offset); \ - _insn->sym->warnings++; \ - } \ + if (_insn->sym) \ + _insn->sym->warned = 1; \ }) #define BT_INSN(insn, format, ...) \ From 0b10177114d1e434af850b377cf5e6620dd1d525 Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Mon, 31 Mar 2025 21:26:40 -0700 Subject: [PATCH 30/36] 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(-) 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; } From 3e7be635937d19b91bab70695328214a3d789d51 Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Mon, 31 Mar 2025 21:26:41 -0700 Subject: [PATCH 31/36] 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/arch/loongarch/decode.c | 14 ++- tools/objtool/arch/loongarch/orc.c | 8 +- tools/objtool/arch/x86/decode.c | 15 ++- tools/objtool/arch/x86/orc.c | 6 +- tools/objtool/builtin-check.c | 30 +++--- tools/objtool/check.c | 127 +++++++++++----------- tools/objtool/elf.c | 150 ++++++++++++-------------- tools/objtool/include/objtool/warn.h | 51 ++++++--- tools/objtool/objtool.c | 4 +- tools/objtool/orc_dump.c | 30 +++--- tools/objtool/special.c | 13 +-- 11 files changed, 226 insertions(+), 222 deletions(-) diff --git a/tools/objtool/arch/loongarch/decode.c b/tools/objtool/arch/loongarch/decode.c index 02e490555966..b6fdc68053cc 100644 --- a/tools/objtool/arch/loongarch/decode.c +++ b/tools/objtool/arch/loongarch/decode.c @@ -63,7 +63,7 @@ static bool is_loongarch(const struct elf *elf) if (elf->ehdr.e_machine == EM_LOONGARCH) return true; - WARN("unexpected ELF machine type %d", elf->ehdr.e_machine); + ERROR("unexpected ELF machine type %d", elf->ehdr.e_machine); return false; } @@ -327,8 +327,10 @@ const char *arch_nop_insn(int len) { static u32 nop; - if (len != LOONGARCH_INSN_SIZE) - WARN("invalid NOP size: %d\n", len); + if (len != LOONGARCH_INSN_SIZE) { + ERROR("invalid NOP size: %d\n", len); + return NULL; + } nop = LOONGARCH_INSN_NOP; @@ -339,8 +341,10 @@ const char *arch_ret_insn(int len) { static u32 ret; - if (len != LOONGARCH_INSN_SIZE) - WARN("invalid RET size: %d\n", len); + if (len != LOONGARCH_INSN_SIZE) { + ERROR("invalid RET size: %d\n", len); + return NULL; + } emit_jirl((union loongarch_instruction *)&ret, LOONGARCH_GPR_RA, LOONGARCH_GPR_ZERO, 0); diff --git a/tools/objtool/arch/loongarch/orc.c b/tools/objtool/arch/loongarch/orc.c index 873536d009d9..b58c5ff443c9 100644 --- a/tools/objtool/arch/loongarch/orc.c +++ b/tools/objtool/arch/loongarch/orc.c @@ -41,7 +41,7 @@ int init_orc_entry(struct orc_entry *orc, struct cfi_state *cfi, struct instruct orc->type = ORC_TYPE_REGS_PARTIAL; break; default: - WARN_INSN(insn, "unknown unwind hint type %d", cfi->type); + ERROR_INSN(insn, "unknown unwind hint type %d", cfi->type); return -1; } @@ -55,7 +55,7 @@ int init_orc_entry(struct orc_entry *orc, struct cfi_state *cfi, struct instruct orc->sp_reg = ORC_REG_FP; break; default: - WARN_INSN(insn, "unknown CFA base reg %d", cfi->cfa.base); + ERROR_INSN(insn, "unknown CFA base reg %d", cfi->cfa.base); return -1; } @@ -72,7 +72,7 @@ int init_orc_entry(struct orc_entry *orc, struct cfi_state *cfi, struct instruct orc->fp_reg = ORC_REG_FP; break; default: - WARN_INSN(insn, "unknown FP base reg %d", fp->base); + ERROR_INSN(insn, "unknown FP base reg %d", fp->base); return -1; } @@ -89,7 +89,7 @@ int init_orc_entry(struct orc_entry *orc, struct cfi_state *cfi, struct instruct orc->ra_reg = ORC_REG_FP; break; default: - WARN_INSN(insn, "unknown RA base reg %d", ra->base); + ERROR_INSN(insn, "unknown RA base reg %d", ra->base); return -1; } diff --git a/tools/objtool/arch/x86/decode.c b/tools/objtool/arch/x86/decode.c index 7567c893f45e..33d861c04ebd 100644 --- a/tools/objtool/arch/x86/decode.c +++ b/tools/objtool/arch/x86/decode.c @@ -36,7 +36,7 @@ static int is_x86_64(const struct elf *elf) case EM_386: return 0; default: - WARN("unexpected ELF machine type %d", elf->ehdr.e_machine); + ERROR("unexpected ELF machine type %d", elf->ehdr.e_machine); return -1; } } @@ -173,7 +173,7 @@ int arch_decode_instruction(struct objtool_file *file, const struct section *sec ret = insn_decode(&ins, sec->data->d_buf + offset, maxlen, x86_64 ? INSN_MODE_64 : INSN_MODE_32); if (ret < 0) { - WARN("can't decode instruction at %s:0x%lx", sec->name, offset); + ERROR("can't decode instruction at %s:0x%lx", sec->name, offset); return -1; } @@ -321,7 +321,7 @@ int arch_decode_instruction(struct objtool_file *file, const struct section *sec break; default: - /* WARN ? */ + /* ERROR ? */ break; } @@ -561,8 +561,7 @@ int arch_decode_instruction(struct objtool_file *file, const struct section *sec if (ins.prefixes.nbytes == 1 && ins.prefixes.bytes[0] == 0xf2) { /* ENQCMD cannot be used in the kernel. */ - WARN("ENQCMD instruction at %s:%lx", sec->name, - offset); + WARN("ENQCMD instruction at %s:%lx", sec->name, offset); } } else if (op2 == 0xa0 || op2 == 0xa8) { @@ -646,7 +645,7 @@ int arch_decode_instruction(struct objtool_file *file, const struct section *sec if (disp->sym->type == STT_SECTION) func = find_symbol_by_offset(disp->sym->sec, reloc_addend(disp)); if (!func) { - WARN("no func for pv_ops[]"); + ERROR("no func for pv_ops[]"); return -1; } @@ -776,7 +775,7 @@ const char *arch_nop_insn(int len) }; if (len < 1 || len > 5) { - WARN("invalid NOP size: %d\n", len); + ERROR("invalid NOP size: %d\n", len); return NULL; } @@ -796,7 +795,7 @@ const char *arch_ret_insn(int len) }; if (len < 1 || len > 5) { - WARN("invalid RET size: %d\n", len); + ERROR("invalid RET size: %d\n", len); return NULL; } diff --git a/tools/objtool/arch/x86/orc.c b/tools/objtool/arch/x86/orc.c index b6cd943e87f9..7176b9ec5b05 100644 --- a/tools/objtool/arch/x86/orc.c +++ b/tools/objtool/arch/x86/orc.c @@ -40,7 +40,7 @@ int init_orc_entry(struct orc_entry *orc, struct cfi_state *cfi, struct instruct orc->type = ORC_TYPE_REGS_PARTIAL; break; default: - WARN_INSN(insn, "unknown unwind hint type %d", cfi->type); + ERROR_INSN(insn, "unknown unwind hint type %d", cfi->type); return -1; } @@ -72,7 +72,7 @@ int init_orc_entry(struct orc_entry *orc, struct cfi_state *cfi, struct instruct orc->sp_reg = ORC_REG_DX; break; default: - WARN_INSN(insn, "unknown CFA base reg %d", cfi->cfa.base); + ERROR_INSN(insn, "unknown CFA base reg %d", cfi->cfa.base); return -1; } @@ -87,7 +87,7 @@ int init_orc_entry(struct orc_entry *orc, struct cfi_state *cfi, struct instruct orc->bp_reg = ORC_REG_BP; break; default: - WARN_INSN(insn, "unknown BP base reg %d", bp->base); + ERROR_INSN(insn, "unknown BP base reg %d", bp->base); return -1; } diff --git a/tools/objtool/builtin-check.c b/tools/objtool/builtin-check.c index e364ab6345d3..80239843e9f0 100644 --- a/tools/objtool/builtin-check.c +++ b/tools/objtool/builtin-check.c @@ -139,22 +139,22 @@ int cmd_parse_options(int argc, const char **argv, const char * const usage[]) static bool opts_valid(void) { if (opts.mnop && !opts.mcount) { - WARN("--mnop requires --mcount"); + ERROR("--mnop requires --mcount"); return false; } if (opts.noinstr && !opts.link) { - WARN("--noinstr requires --link"); + ERROR("--noinstr requires --link"); return false; } if (opts.ibt && !opts.link) { - WARN("--ibt requires --link"); + ERROR("--ibt requires --link"); return false; } if (opts.unret && !opts.link) { - WARN("--unret requires --link"); + ERROR("--unret requires --link"); return false; } @@ -171,7 +171,7 @@ static bool opts_valid(void) opts.static_call || opts.uaccess) { if (opts.dump_orc) { - WARN("--dump can't be combined with other actions"); + ERROR("--dump can't be combined with other actions"); return false; } @@ -181,7 +181,7 @@ static bool opts_valid(void) if (opts.dump_orc) return true; - WARN("At least one action required"); + ERROR("At least one action required"); return false; } @@ -194,30 +194,30 @@ static int copy_file(const char *src, const char *dst) src_fd = open(src, O_RDONLY); if (src_fd == -1) { - WARN("can't open %s for reading: %s", src, strerror(errno)); + ERROR("can't open %s for reading: %s", src, strerror(errno)); return 1; } dst_fd = open(dst, O_WRONLY | O_CREAT | O_TRUNC, 0400); if (dst_fd == -1) { - WARN("can't open %s for writing: %s", dst, strerror(errno)); + ERROR("can't open %s for writing: %s", dst, strerror(errno)); return 1; } if (fstat(src_fd, &stat) == -1) { - WARN_GLIBC("fstat"); + ERROR_GLIBC("fstat"); return 1; } if (fchmod(dst_fd, stat.st_mode) == -1) { - WARN_GLIBC("fchmod"); + ERROR_GLIBC("fchmod"); return 1; } for (to_copy = stat.st_size; to_copy > 0; to_copy -= copied) { copied = sendfile(dst_fd, src_fd, &offset, to_copy); if (copied == -1) { - WARN_GLIBC("sendfile"); + ERROR_GLIBC("sendfile"); return 1; } } @@ -231,14 +231,14 @@ static void save_argv(int argc, const char **argv) { orig_argv = calloc(argc, sizeof(char *)); if (!orig_argv) { - WARN_GLIBC("calloc"); + ERROR_GLIBC("calloc"); exit(1); } for (int i = 0; i < argc; i++) { orig_argv[i] = strdup(argv[i]); if (!orig_argv[i]) { - WARN_GLIBC("strdup(%s)", argv[i]); + ERROR_GLIBC("strdup(%s)", argv[i]); exit(1); } }; @@ -257,7 +257,7 @@ void print_args(void) */ backup = malloc(strlen(objname) + strlen(ORIG_SUFFIX) + 1); if (!backup) { - WARN_GLIBC("malloc"); + ERROR_GLIBC("malloc"); goto print; } @@ -319,7 +319,7 @@ int objtool_run(int argc, const char **argv) return 1; if (!opts.link && has_multiple_files(file->elf)) { - WARN("Linked object requires --link"); + ERROR("Linked object requires --link"); return 1; } 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); diff --git a/tools/objtool/elf.c b/tools/objtool/elf.c index b352a78b596c..727a3a4fd9d7 100644 --- a/tools/objtool/elf.c +++ b/tools/objtool/elf.c @@ -72,17 +72,17 @@ static inline void __elf_hash_del(struct elf_hash_node *node, obj; \ obj = elf_list_entry(obj->member.next, typeof(*(obj)), member)) -#define elf_alloc_hash(name, size) \ -({ \ - __elf_bits(name) = max(10, ilog2(size)); \ +#define elf_alloc_hash(name, size) \ +({ \ + __elf_bits(name) = max(10, ilog2(size)); \ __elf_table(name) = mmap(NULL, sizeof(struct elf_hash_node *) << __elf_bits(name), \ - PROT_READ|PROT_WRITE, \ - MAP_PRIVATE|MAP_ANON, -1, 0); \ - if (__elf_table(name) == (void *)-1L) { \ - WARN("mmap fail " #name); \ - __elf_table(name) = NULL; \ - } \ - __elf_table(name); \ + PROT_READ|PROT_WRITE, \ + MAP_PRIVATE|MAP_ANON, -1, 0); \ + if (__elf_table(name) == (void *)-1L) { \ + ERROR_GLIBC("mmap fail " #name); \ + __elf_table(name) = NULL; \ + } \ + __elf_table(name); \ }) static inline unsigned long __sym_start(struct symbol *s) @@ -316,12 +316,12 @@ static int read_sections(struct elf *elf) int i; if (elf_getshdrnum(elf->elf, §ions_nr)) { - WARN_ELF("elf_getshdrnum"); + ERROR_ELF("elf_getshdrnum"); return -1; } if (elf_getshdrstrndx(elf->elf, &shstrndx)) { - WARN_ELF("elf_getshdrstrndx"); + ERROR_ELF("elf_getshdrstrndx"); return -1; } @@ -331,7 +331,7 @@ static int read_sections(struct elf *elf) elf->section_data = calloc(sections_nr, sizeof(*sec)); if (!elf->section_data) { - WARN_GLIBC("calloc"); + ERROR_GLIBC("calloc"); return -1; } for (i = 0; i < sections_nr; i++) { @@ -341,33 +341,32 @@ static int read_sections(struct elf *elf) s = elf_getscn(elf->elf, i); if (!s) { - WARN_ELF("elf_getscn"); + ERROR_ELF("elf_getscn"); return -1; } sec->idx = elf_ndxscn(s); if (!gelf_getshdr(s, &sec->sh)) { - WARN_ELF("gelf_getshdr"); + ERROR_ELF("gelf_getshdr"); return -1; } sec->name = elf_strptr(elf->elf, shstrndx, sec->sh.sh_name); if (!sec->name) { - WARN_ELF("elf_strptr"); + ERROR_ELF("elf_strptr"); return -1; } if (sec->sh.sh_size != 0 && !is_dwarf_section(sec)) { sec->data = elf_getdata(s, NULL); if (!sec->data) { - WARN_ELF("elf_getdata"); + ERROR_ELF("elf_getdata"); return -1; } if (sec->data->d_off != 0 || sec->data->d_size != sec->sh.sh_size) { - WARN("unexpected data attributes for %s", - sec->name); + ERROR("unexpected data attributes for %s", sec->name); return -1; } } @@ -387,7 +386,7 @@ static int read_sections(struct elf *elf) /* sanity check, one more call to elf_nextscn() should return NULL */ if (elf_nextscn(elf->elf, s)) { - WARN("section entry mismatch"); + ERROR("section entry mismatch"); return -1; } @@ -467,7 +466,7 @@ static int read_symbols(struct elf *elf) elf->symbol_data = calloc(symbols_nr, sizeof(*sym)); if (!elf->symbol_data) { - WARN_GLIBC("calloc"); + ERROR_GLIBC("calloc"); return -1; } for (i = 0; i < symbols_nr; i++) { @@ -477,14 +476,14 @@ static int read_symbols(struct elf *elf) if (!gelf_getsymshndx(symtab->data, shndx_data, i, &sym->sym, &shndx)) { - WARN_ELF("gelf_getsymshndx"); + ERROR_ELF("gelf_getsymshndx"); goto err; } sym->name = elf_strptr(elf->elf, symtab->sh.sh_link, sym->sym.st_name); if (!sym->name) { - WARN_ELF("elf_strptr"); + ERROR_ELF("elf_strptr"); goto err; } @@ -496,8 +495,7 @@ static int read_symbols(struct elf *elf) sym->sec = find_section_by_index(elf, shndx); if (!sym->sec) { - WARN("couldn't find section for symbol %s", - sym->name); + ERROR("couldn't find section for symbol %s", sym->name); goto err; } if (GELF_ST_TYPE(sym->sym.st_info) == STT_SECTION) { @@ -536,8 +534,7 @@ static int read_symbols(struct elf *elf) pnamelen = coldstr - sym->name; pname = strndup(sym->name, pnamelen); if (!pname) { - WARN("%s(): failed to allocate memory", - sym->name); + ERROR("%s(): failed to allocate memory", sym->name); return -1; } @@ -545,8 +542,7 @@ static int read_symbols(struct elf *elf) free(pname); if (!pfunc) { - WARN("%s(): can't find parent function", - sym->name); + ERROR("%s(): can't find parent function", sym->name); return -1; } @@ -613,14 +609,14 @@ static int elf_update_symbol(struct elf *elf, struct section *symtab, s = elf_getscn(elf->elf, symtab->idx); if (!s) { - WARN_ELF("elf_getscn"); + ERROR_ELF("elf_getscn"); return -1; } if (symtab_shndx) { t = elf_getscn(elf->elf, symtab_shndx->idx); if (!t) { - WARN_ELF("elf_getscn"); + ERROR_ELF("elf_getscn"); return -1; } } @@ -643,7 +639,7 @@ static int elf_update_symbol(struct elf *elf, struct section *symtab, if (idx) { /* we don't do holes in symbol tables */ - WARN("index out of range"); + ERROR("index out of range"); return -1; } @@ -654,7 +650,7 @@ static int elf_update_symbol(struct elf *elf, struct section *symtab, buf = calloc(num, entsize); if (!buf) { - WARN("malloc"); + ERROR_GLIBC("calloc"); return -1; } @@ -669,7 +665,7 @@ static int elf_update_symbol(struct elf *elf, struct section *symtab, if (t) { buf = calloc(num, sizeof(Elf32_Word)); if (!buf) { - WARN("malloc"); + ERROR_GLIBC("calloc"); return -1; } @@ -687,7 +683,7 @@ static int elf_update_symbol(struct elf *elf, struct section *symtab, /* empty blocks should not happen */ if (!symtab_data->d_size) { - WARN("zero size data"); + ERROR("zero size data"); return -1; } @@ -702,7 +698,7 @@ static int elf_update_symbol(struct elf *elf, struct section *symtab, /* something went side-ways */ if (idx < 0) { - WARN("negative index"); + ERROR("negative index"); return -1; } @@ -714,13 +710,13 @@ static int elf_update_symbol(struct elf *elf, struct section *symtab, } else { sym->sym.st_shndx = SHN_XINDEX; if (!shndx_data) { - WARN("no .symtab_shndx"); + ERROR("no .symtab_shndx"); return -1; } } if (!gelf_update_symshndx(symtab_data, shndx_data, idx, &sym->sym, shndx)) { - WARN_ELF("gelf_update_symshndx"); + ERROR_ELF("gelf_update_symshndx"); return -1; } @@ -738,7 +734,7 @@ __elf_create_symbol(struct elf *elf, struct symbol *sym) if (symtab) { symtab_shndx = find_section_by_name(elf, ".symtab_shndx"); } else { - WARN("no .symtab"); + ERROR("no .symtab"); return NULL; } @@ -760,7 +756,7 @@ __elf_create_symbol(struct elf *elf, struct symbol *sym) old->idx = new_idx; if (elf_update_symbol(elf, symtab, symtab_shndx, old)) { - WARN("elf_update_symbol move"); + ERROR("elf_update_symbol move"); return NULL; } @@ -778,7 +774,7 @@ __elf_create_symbol(struct elf *elf, struct symbol *sym) non_local: sym->idx = new_idx; if (elf_update_symbol(elf, symtab, symtab_shndx, sym)) { - WARN("elf_update_symbol"); + ERROR("elf_update_symbol"); return NULL; } @@ -799,7 +795,7 @@ elf_create_section_symbol(struct elf *elf, struct section *sec) struct symbol *sym = calloc(1, sizeof(*sym)); if (!sym) { - WARN_GLIBC("malloc"); + ERROR_GLIBC("malloc"); return NULL; } @@ -829,7 +825,7 @@ elf_create_prefix_symbol(struct elf *elf, struct symbol *orig, long size) char *name = malloc(namelen); if (!sym || !name) { - WARN_GLIBC("malloc"); + ERROR_GLIBC("malloc"); return NULL; } @@ -858,16 +854,16 @@ static struct reloc *elf_init_reloc(struct elf *elf, struct section *rsec, struct reloc *reloc, empty = { 0 }; if (reloc_idx >= sec_num_entries(rsec)) { - WARN("%s: bad reloc_idx %u for %s with %d relocs", - __func__, reloc_idx, rsec->name, sec_num_entries(rsec)); + ERROR("%s: bad reloc_idx %u for %s with %d relocs", + __func__, reloc_idx, rsec->name, sec_num_entries(rsec)); return NULL; } reloc = &rsec->relocs[reloc_idx]; if (memcmp(reloc, &empty, sizeof(empty))) { - WARN("%s: %s: reloc %d already initialized!", - __func__, rsec->name, reloc_idx); + ERROR("%s: %s: reloc %d already initialized!", + __func__, rsec->name, reloc_idx); return NULL; } @@ -896,8 +892,7 @@ struct reloc *elf_init_reloc_text_sym(struct elf *elf, struct section *sec, int addend = insn_off; if (!(insn_sec->sh.sh_flags & SHF_EXECINSTR)) { - WARN("bad call to %s() for data symbol %s", - __func__, sym->name); + ERROR("bad call to %s() for data symbol %s", __func__, sym->name); return NULL; } @@ -926,8 +921,7 @@ struct reloc *elf_init_reloc_data_sym(struct elf *elf, struct section *sec, s64 addend) { if (sym->sec && (sec->sh.sh_flags & SHF_EXECINSTR)) { - WARN("bad call to %s() for text symbol %s", - __func__, sym->name); + ERROR("bad call to %s() for text symbol %s", __func__, sym->name); return NULL; } @@ -953,8 +947,7 @@ static int read_relocs(struct elf *elf) rsec->base = find_section_by_index(elf, rsec->sh.sh_info); if (!rsec->base) { - WARN("can't find base section for reloc section %s", - rsec->name); + ERROR("can't find base section for reloc section %s", rsec->name); return -1; } @@ -963,7 +956,7 @@ static int read_relocs(struct elf *elf) nr_reloc = 0; rsec->relocs = calloc(sec_num_entries(rsec), sizeof(*reloc)); if (!rsec->relocs) { - WARN_GLIBC("calloc"); + ERROR_GLIBC("calloc"); return -1; } for (i = 0; i < sec_num_entries(rsec); i++) { @@ -973,8 +966,7 @@ static int read_relocs(struct elf *elf) symndx = reloc_sym(reloc); reloc->sym = sym = find_symbol_by_index(elf, symndx); if (!reloc->sym) { - WARN("can't find reloc entry symbol %d for %s", - symndx, rsec->name); + ERROR("can't find reloc entry symbol %d for %s", symndx, rsec->name); return -1; } @@ -1005,7 +997,7 @@ struct elf *elf_open_read(const char *name, int flags) elf = malloc(sizeof(*elf)); if (!elf) { - WARN_GLIBC("malloc"); + ERROR_GLIBC("malloc"); return NULL; } memset(elf, 0, sizeof(*elf)); @@ -1028,12 +1020,12 @@ struct elf *elf_open_read(const char *name, int flags) elf->elf = elf_begin(elf->fd, cmd, NULL); if (!elf->elf) { - WARN_ELF("elf_begin"); + ERROR_ELF("elf_begin"); goto err; } if (!gelf_getehdr(elf->elf, &elf->ehdr)) { - WARN_ELF("gelf_getehdr"); + ERROR_ELF("gelf_getehdr"); goto err; } @@ -1062,19 +1054,19 @@ static int elf_add_string(struct elf *elf, struct section *strtab, char *str) if (!strtab) strtab = find_section_by_name(elf, ".strtab"); if (!strtab) { - WARN("can't find .strtab section"); + ERROR("can't find .strtab section"); return -1; } s = elf_getscn(elf->elf, strtab->idx); if (!s) { - WARN_ELF("elf_getscn"); + ERROR_ELF("elf_getscn"); return -1; } data = elf_newdata(s); if (!data) { - WARN_ELF("elf_newdata"); + ERROR_ELF("elf_newdata"); return -1; } @@ -1099,7 +1091,7 @@ struct section *elf_create_section(struct elf *elf, const char *name, sec = malloc(sizeof(*sec)); if (!sec) { - WARN_GLIBC("malloc"); + ERROR_GLIBC("malloc"); return NULL; } memset(sec, 0, sizeof(*sec)); @@ -1108,13 +1100,13 @@ struct section *elf_create_section(struct elf *elf, const char *name, s = elf_newscn(elf->elf); if (!s) { - WARN_ELF("elf_newscn"); + ERROR_ELF("elf_newscn"); return NULL; } sec->name = strdup(name); if (!sec->name) { - WARN_GLIBC("strdup"); + ERROR_GLIBC("strdup"); return NULL; } @@ -1122,7 +1114,7 @@ struct section *elf_create_section(struct elf *elf, const char *name, sec->data = elf_newdata(s); if (!sec->data) { - WARN_ELF("elf_newdata"); + ERROR_ELF("elf_newdata"); return NULL; } @@ -1132,14 +1124,14 @@ struct section *elf_create_section(struct elf *elf, const char *name, if (size) { sec->data->d_buf = malloc(size); if (!sec->data->d_buf) { - WARN_GLIBC("malloc"); + ERROR_GLIBC("malloc"); return NULL; } memset(sec->data->d_buf, 0, size); } if (!gelf_getshdr(s, &sec->sh)) { - WARN_ELF("gelf_getshdr"); + ERROR_ELF("gelf_getshdr"); return NULL; } @@ -1154,7 +1146,7 @@ struct section *elf_create_section(struct elf *elf, const char *name, if (!shstrtab) shstrtab = find_section_by_name(elf, ".strtab"); if (!shstrtab) { - WARN("can't find .shstrtab or .strtab section"); + ERROR("can't find .shstrtab or .strtab section"); return NULL; } sec->sh.sh_name = elf_add_string(elf, shstrtab, sec->name); @@ -1179,7 +1171,7 @@ static struct section *elf_create_rela_section(struct elf *elf, rsec_name = malloc(strlen(sec->name) + strlen(".rela") + 1); if (!rsec_name) { - WARN_GLIBC("malloc"); + ERROR_GLIBC("malloc"); return NULL; } strcpy(rsec_name, ".rela"); @@ -1199,7 +1191,7 @@ static struct section *elf_create_rela_section(struct elf *elf, rsec->relocs = calloc(sec_num_entries(rsec), sizeof(struct reloc)); if (!rsec->relocs) { - WARN_GLIBC("calloc"); + ERROR_GLIBC("calloc"); return NULL; } @@ -1232,7 +1224,7 @@ int elf_write_insn(struct elf *elf, struct section *sec, Elf_Data *data = sec->data; if (data->d_type != ELF_T_BYTE || data->d_off) { - WARN("write to unexpected data for section: %s", sec->name); + ERROR("write to unexpected data for section: %s", sec->name); return -1; } @@ -1261,7 +1253,7 @@ static int elf_truncate_section(struct elf *elf, struct section *sec) s = elf_getscn(elf->elf, sec->idx); if (!s) { - WARN_ELF("elf_getscn"); + ERROR_ELF("elf_getscn"); return -1; } @@ -1271,7 +1263,7 @@ static int elf_truncate_section(struct elf *elf, struct section *sec) if (!data) { if (size) { - WARN("end of section data but non-zero size left\n"); + ERROR("end of section data but non-zero size left\n"); return -1; } return 0; @@ -1279,12 +1271,12 @@ static int elf_truncate_section(struct elf *elf, struct section *sec) if (truncated) { /* when we remove symbols */ - WARN("truncated; but more data\n"); + ERROR("truncated; but more data\n"); return -1; } if (!data->d_size) { - WARN("zero size data"); + ERROR("zero size data"); return -1; } @@ -1310,13 +1302,13 @@ int elf_write(struct elf *elf) if (sec_changed(sec)) { s = elf_getscn(elf->elf, sec->idx); if (!s) { - WARN_ELF("elf_getscn"); + ERROR_ELF("elf_getscn"); return -1; } /* Note this also flags the section dirty */ if (!gelf_update_shdr(s, &sec->sh)) { - WARN_ELF("gelf_update_shdr"); + ERROR_ELF("gelf_update_shdr"); return -1; } @@ -1329,7 +1321,7 @@ int elf_write(struct elf *elf) /* Write all changes to the file. */ if (elf_update(elf->elf, ELF_C_WRITE) < 0) { - WARN_ELF("elf_update"); + ERROR_ELF("elf_update"); return -1; } diff --git a/tools/objtool/include/objtool/warn.h b/tools/objtool/include/objtool/warn.h index e3ad9b2caf87..cb8fe846d9dd 100644 --- a/tools/objtool/include/objtool/warn.h +++ b/tools/objtool/include/objtool/warn.h @@ -42,26 +42,43 @@ static inline char *offstr(struct section *sec, unsigned long offset) return str; } -#define WARN(format, ...) \ - fprintf(stderr, \ - "%s%s%s: objtool: " format "\n", \ - objname ?: "", \ - objname ? ": " : "", \ - opts.werror ? "error" : "warning", \ +#define ___WARN(severity, extra, format, ...) \ + fprintf(stderr, \ + "%s%s%s: objtool" extra ": " format "\n", \ + objname ?: "", \ + objname ? ": " : "", \ + severity, \ ##__VA_ARGS__) -#define WARN_FUNC(format, sec, offset, ...) \ -({ \ - char *_str = offstr(sec, offset); \ - WARN("%s: " format, _str, ##__VA_ARGS__); \ - free(_str); \ +#define __WARN(severity, format, ...) \ + ___WARN(severity, "", format, ##__VA_ARGS__) + +#define __WARN_LINE(severity, format, ...) \ + ___WARN(severity, " [%s:%d]", format, __FILE__, __LINE__, ##__VA_ARGS__) + +#define __WARN_ELF(severity, format, ...) \ + __WARN_LINE(severity, "%s: " format " failed: %s", __func__, ##__VA_ARGS__, elf_errmsg(-1)) + +#define __WARN_GLIBC(severity, format, ...) \ + __WARN_LINE(severity, "%s: " format " failed: %s", __func__, ##__VA_ARGS__, strerror(errno)) + +#define __WARN_FUNC(severity, sec, offset, format, ...) \ +({ \ + char *_str = offstr(sec, offset); \ + __WARN(severity, "%s: " format, _str, ##__VA_ARGS__); \ + free(_str); \ }) +#define WARN_STR (opts.werror ? "error" : "warning") + +#define WARN(format, ...) __WARN(WARN_STR, format, ##__VA_ARGS__) +#define WARN_FUNC(sec, offset, format, ...) __WARN_FUNC(WARN_STR, sec, offset, format, ##__VA_ARGS__) + #define WARN_INSN(insn, format, ...) \ ({ \ struct instruction *_insn = (insn); \ if (!_insn->sym || !_insn->sym->warned) \ - WARN_FUNC(format, _insn->sec, _insn->offset, \ + WARN_FUNC(_insn->sec, _insn->offset, format, \ ##__VA_ARGS__); \ if (_insn->sym) \ _insn->sym->warned = 1; \ @@ -77,10 +94,12 @@ static inline char *offstr(struct section *sec, unsigned long offset) } \ }) -#define WARN_ELF(format, ...) \ - WARN("%s: " format " failed: %s", __func__, ##__VA_ARGS__, elf_errmsg(-1)) +#define ERROR_STR "error" -#define WARN_GLIBC(format, ...) \ - WARN("%s: " format " failed: %s", __func__, ##__VA_ARGS__, strerror(errno)) +#define ERROR(format, ...) __WARN(ERROR_STR, format, ##__VA_ARGS__) +#define ERROR_ELF(format, ...) __WARN_ELF(ERROR_STR, format, ##__VA_ARGS__) +#define ERROR_GLIBC(format, ...) __WARN_GLIBC(ERROR_STR, format, ##__VA_ARGS__) +#define ERROR_FUNC(sec, offset, format, ...) __WARN_FUNC(ERROR_STR, sec, offset, format, ##__VA_ARGS__) +#define ERROR_INSN(insn, format, ...) WARN_FUNC(insn->sec, insn->offset, format, ##__VA_ARGS__) #endif /* _WARN_H */ diff --git a/tools/objtool/objtool.c b/tools/objtool/objtool.c index e4b49c534e4d..5c8b974ad0f9 100644 --- a/tools/objtool/objtool.c +++ b/tools/objtool/objtool.c @@ -23,7 +23,7 @@ static struct objtool_file file; struct objtool_file *objtool_open_read(const char *filename) { if (file.elf) { - WARN("won't handle more than one file at a time"); + ERROR("won't handle more than one file at a time"); return NULL; } @@ -50,7 +50,7 @@ int objtool_pv_add(struct objtool_file *f, int idx, struct symbol *func) return 0; if (!f->pv_ops) { - WARN("paravirt confusion"); + ERROR("paravirt confusion"); return -1; } diff --git a/tools/objtool/orc_dump.c b/tools/objtool/orc_dump.c index 05ef0e297837..1dd9fc18fe62 100644 --- a/tools/objtool/orc_dump.c +++ b/tools/objtool/orc_dump.c @@ -36,47 +36,47 @@ int orc_dump(const char *filename) elf = elf_begin(fd, ELF_C_READ_MMAP, NULL); if (!elf) { - WARN_ELF("elf_begin"); + ERROR_ELF("elf_begin"); return -1; } if (!elf64_getehdr(elf)) { - WARN_ELF("elf64_getehdr"); + ERROR_ELF("elf64_getehdr"); return -1; } memcpy(&dummy_elf.ehdr, elf64_getehdr(elf), sizeof(dummy_elf.ehdr)); if (elf_getshdrnum(elf, &nr_sections)) { - WARN_ELF("elf_getshdrnum"); + ERROR_ELF("elf_getshdrnum"); return -1; } if (elf_getshdrstrndx(elf, &shstrtab_idx)) { - WARN_ELF("elf_getshdrstrndx"); + ERROR_ELF("elf_getshdrstrndx"); return -1; } for (i = 0; i < nr_sections; i++) { scn = elf_getscn(elf, i); if (!scn) { - WARN_ELF("elf_getscn"); + ERROR_ELF("elf_getscn"); return -1; } if (!gelf_getshdr(scn, &sh)) { - WARN_ELF("gelf_getshdr"); + ERROR_ELF("gelf_getshdr"); return -1; } name = elf_strptr(elf, shstrtab_idx, sh.sh_name); if (!name) { - WARN_ELF("elf_strptr"); + ERROR_ELF("elf_strptr"); return -1; } data = elf_getdata(scn, NULL); if (!data) { - WARN_ELF("elf_getdata"); + ERROR_ELF("elf_getdata"); return -1; } @@ -99,7 +99,7 @@ int orc_dump(const char *filename) return 0; if (orc_size % sizeof(*orc) != 0) { - WARN("bad .orc_unwind section size"); + ERROR("bad .orc_unwind section size"); return -1; } @@ -107,36 +107,36 @@ int orc_dump(const char *filename) for (i = 0; i < nr_entries; i++) { if (rela_orc_ip) { if (!gelf_getrela(rela_orc_ip, i, &rela)) { - WARN_ELF("gelf_getrela"); + ERROR_ELF("gelf_getrela"); return -1; } if (!gelf_getsym(symtab, GELF_R_SYM(rela.r_info), &sym)) { - WARN_ELF("gelf_getsym"); + ERROR_ELF("gelf_getsym"); return -1; } if (GELF_ST_TYPE(sym.st_info) == STT_SECTION) { scn = elf_getscn(elf, sym.st_shndx); if (!scn) { - WARN_ELF("elf_getscn"); + ERROR_ELF("elf_getscn"); return -1; } if (!gelf_getshdr(scn, &sh)) { - WARN_ELF("gelf_getshdr"); + ERROR_ELF("gelf_getshdr"); return -1; } name = elf_strptr(elf, shstrtab_idx, sh.sh_name); if (!name) { - WARN_ELF("elf_strptr"); + ERROR_ELF("elf_strptr"); return -1; } } else { name = elf_strptr(elf, strtab_idx, sym.st_name); if (!name) { - WARN_ELF("elf_strptr"); + ERROR_ELF("elf_strptr"); return -1; } } diff --git a/tools/objtool/special.c b/tools/objtool/special.c index 6cd7b1be5331..c80fed8a840e 100644 --- a/tools/objtool/special.c +++ b/tools/objtool/special.c @@ -86,7 +86,7 @@ static int get_alt_entry(struct elf *elf, const struct special_entry *entry, orig_reloc = find_reloc_by_dest(elf, sec, offset + entry->orig); if (!orig_reloc) { - WARN_FUNC("can't find orig reloc", sec, offset + entry->orig); + ERROR_FUNC(sec, offset + entry->orig, "can't find orig reloc"); return -1; } @@ -97,8 +97,7 @@ static int get_alt_entry(struct elf *elf, const struct special_entry *entry, if (!entry->group || alt->new_len) { new_reloc = find_reloc_by_dest(elf, sec, offset + entry->new); if (!new_reloc) { - WARN_FUNC("can't find new reloc", - sec, offset + entry->new); + ERROR_FUNC(sec, offset + entry->new, "can't find new reloc"); return -1; } @@ -114,8 +113,7 @@ static int get_alt_entry(struct elf *elf, const struct special_entry *entry, key_reloc = find_reloc_by_dest(elf, sec, offset + entry->key); if (!key_reloc) { - WARN_FUNC("can't find key reloc", - sec, offset + entry->key); + ERROR_FUNC(sec, offset + entry->key, "can't find key reloc"); return -1; } alt->key_addend = reloc_addend(key_reloc); @@ -145,8 +143,7 @@ int special_get_alts(struct elf *elf, struct list_head *alts) continue; if (sec->sh.sh_size % entry->size != 0) { - WARN("%s size not a multiple of %d", - sec->name, entry->size); + ERROR("%s size not a multiple of %d", sec->name, entry->size); return -1; } @@ -155,7 +152,7 @@ int special_get_alts(struct elf *elf, struct list_head *alts) for (idx = 0; idx < nr_entries; idx++) { alt = malloc(sizeof(*alt)); if (!alt) { - WARN("malloc failed"); + ERROR_GLIBC("malloc failed"); return -1; } memset(alt, 0, sizeof(*alt)); From e77956e4e5c11218e60a1fe8cdbccd02476f2e56 Mon Sep 17 00:00:00 2001 From: David Laight Date: Mon, 31 Mar 2025 21:26:42 -0700 Subject: [PATCH 32/36] 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(+) 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); }" From 09f37f2d7b21ff35b8b533f9ab8cfad2fe8f72f6 Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Mon, 31 Mar 2025 21:26:44 -0700 Subject: [PATCH 33/36] sched/smt: Always inline sched_smt_active() sched_smt_active() can be called from noinstr code, so it should always be inlined. The CONFIG_SCHED_SMT version already has __always_inline. Do the same for its !CONFIG_SCHED_SMT counterpart. Fixes the following warning: vmlinux.o: error: objtool: intel_idle_ibrs+0x13: call to sched_smt_active() leaves .noinstr.text section Fixes: 321a874a7ef8 ("sched/smt: Expose sched_smt_present static key") Reported-by: kernel test robot Signed-off-by: Josh Poimboeuf Signed-off-by: Ingo Molnar Cc: Linus Torvalds Link: https://lore.kernel.org/r/1d03907b0a247cf7fb5c1d518de378864f603060.1743481539.git.jpoimboe@kernel.org Closes: https://lore.kernel.org/r/202503311434.lyw2Tveh-lkp@intel.com/ --- include/linux/sched/smt.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/linux/sched/smt.h b/include/linux/sched/smt.h index fb1e295e7e63..166b19af956f 100644 --- a/include/linux/sched/smt.h +++ b/include/linux/sched/smt.h @@ -12,7 +12,7 @@ static __always_inline bool sched_smt_active(void) return static_branch_likely(&sched_smt_present); } #else -static inline bool sched_smt_active(void) { return false; } +static __always_inline bool sched_smt_active(void) { return false; } #endif void arch_smt_update(void); From 9ac50f7311dc8b39e355582f14c1e82da47a8196 Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Mon, 31 Mar 2025 21:26:45 -0700 Subject: [PATCH 34/36] context_tracking: Always inline ct_{nmi,irq}_{enter,exit}() Thanks to CONFIG_DEBUG_SECTION_MISMATCH, empty functions can be generated out of line. These can be called from noinstr code, so make sure they're always inlined. Fixes the following warnings: vmlinux.o: warning: objtool: irqentry_nmi_enter+0xa2: call to ct_nmi_enter() leaves .noinstr.text section vmlinux.o: warning: objtool: irqentry_nmi_exit+0x16: call to ct_nmi_exit() leaves .noinstr.text section vmlinux.o: warning: objtool: irqentry_exit+0x78: call to ct_irq_exit() leaves .noinstr.text section Fixes: 6f0e6c1598b1 ("context_tracking: Take IRQ eqs entrypoints over RCU") Reported-by: Randy Dunlap Signed-off-by: Josh Poimboeuf Signed-off-by: Ingo Molnar Cc: Frederic Weisbecker Cc: Paul E. McKenney Cc: Linus Torvalds Link: https://lore.kernel.org/r/8509bce3f536bcd4ae7af3a2cf6930d48c5e631a.1743481539.git.jpoimboe@kernel.org Closes: https://lore.kernel.org/d1eca076-fdde-484a-b33e-70e0d167c36d@infradead.org --- include/linux/context_tracking_irq.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/include/linux/context_tracking_irq.h b/include/linux/context_tracking_irq.h index c50b5670c4a5..197916ee91a4 100644 --- a/include/linux/context_tracking_irq.h +++ b/include/linux/context_tracking_irq.h @@ -10,12 +10,12 @@ void ct_irq_exit_irqson(void); void ct_nmi_enter(void); void ct_nmi_exit(void); #else -static inline void ct_irq_enter(void) { } -static inline void ct_irq_exit(void) { } +static __always_inline void ct_irq_enter(void) { } +static __always_inline void ct_irq_exit(void) { } static inline void ct_irq_enter_irqson(void) { } static inline void ct_irq_exit_irqson(void) { } -static inline void ct_nmi_enter(void) { } -static inline void ct_nmi_exit(void) { } +static __always_inline void ct_nmi_enter(void) { } +static __always_inline void ct_nmi_exit(void) { } #endif #endif From 6309a5c43b0dc629851f25b2e5ef8beff61d08e5 Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Mon, 31 Mar 2025 21:26:46 -0700 Subject: [PATCH 35/36] rcu-tasks: Always inline rcu_irq_work_resched() Thanks to CONFIG_DEBUG_SECTION_MISMATCH, empty functions can be generated out of line. rcu_irq_work_resched() can be called from noinstr code, so make sure it's always inlined. Fixes: 564506495ca9 ("rcu/context-tracking: Move deferred nocb resched to context tracking") Reported-by: Randy Dunlap Signed-off-by: Josh Poimboeuf Signed-off-by: Ingo Molnar Cc: Frederic Weisbecker Cc: Paul E. McKenney Cc: Linus Torvalds Link: https://lore.kernel.org/r/e84f15f013c07e4c410d972e75620c53b62c1b3e.1743481539.git.jpoimboe@kernel.org Closes: https://lore.kernel.org/d1eca076-fdde-484a-b33e-70e0d167c36d@infradead.org --- include/linux/rcupdate.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/linux/rcupdate.h b/include/linux/rcupdate.h index f8159f8a7d73..120536f4c6eb 100644 --- a/include/linux/rcupdate.h +++ b/include/linux/rcupdate.h @@ -132,7 +132,7 @@ static inline void rcu_sysrq_end(void) { } #if defined(CONFIG_NO_HZ_FULL) && (!defined(CONFIG_GENERIC_ENTRY) || !defined(CONFIG_KVM_XFER_TO_GUEST_WORK)) void rcu_irq_work_resched(void); #else -static inline void rcu_irq_work_resched(void) { } +static __always_inline void rcu_irq_work_resched(void) { } #endif #ifdef CONFIG_RCU_NOCB_CPU From 7c977393b8277ed319e92e4b598b26598c9d30c0 Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Mon, 31 Mar 2025 21:26:43 -0700 Subject: [PATCH 36/36] objtool/loongarch: Add unwind hints in prepare_frametrace() If 'regs' points to a local stack variable, prepare_frametrace() stores all registers to the stack. This confuses objtool as it expects them to be restored from the stack later. The stores don't affect stack tracing, so use unwind hints to hide them from objtool. Fixes the following warnings: arch/loongarch/kernel/traps.o: warning: objtool: show_stack+0xe0: stack state mismatch: reg1[22]=-1+0 reg2[22]=-2-160 arch/loongarch/kernel/traps.o: warning: objtool: show_stack+0xe0: stack state mismatch: reg1[23]=-1+0 reg2[23]=-2-152 Fixes: cb8a2ef0848c ("LoongArch: Add ORC stack unwinder support") Reported-by: kernel test robot Tested-by: Tiezhu Yang Signed-off-by: Josh Poimboeuf Signed-off-by: Ingo Molnar Cc: Linus Torvalds Link: https://lore.kernel.org/r/270cadd8040dda74db2307f23497bb68e65db98d.1743481539.git.jpoimboe@kernel.org Closes: https://lore.kernel.org/oe-kbuild-all/202503280703.OARM8SrY-lkp@intel.com/ --- arch/loongarch/include/asm/stacktrace.h | 3 +++ arch/loongarch/include/asm/unwind_hints.h | 10 +++++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/arch/loongarch/include/asm/stacktrace.h b/arch/loongarch/include/asm/stacktrace.h index f23adb15f418..fc8b64773794 100644 --- a/arch/loongarch/include/asm/stacktrace.h +++ b/arch/loongarch/include/asm/stacktrace.h @@ -8,6 +8,7 @@ #include #include #include +#include #include enum stack_type { @@ -43,6 +44,7 @@ int get_stack_info(unsigned long stack, struct task_struct *task, struct stack_i static __always_inline void prepare_frametrace(struct pt_regs *regs) { __asm__ __volatile__( + UNWIND_HINT_SAVE /* Save $ra */ STORE_ONE_REG(1) /* Use $ra to save PC */ @@ -80,6 +82,7 @@ static __always_inline void prepare_frametrace(struct pt_regs *regs) STORE_ONE_REG(29) STORE_ONE_REG(30) STORE_ONE_REG(31) + UNWIND_HINT_RESTORE : "=m" (regs->csr_era) : "r" (regs->regs) : "memory"); diff --git a/arch/loongarch/include/asm/unwind_hints.h b/arch/loongarch/include/asm/unwind_hints.h index a01086ad9dde..2c68bc72736c 100644 --- a/arch/loongarch/include/asm/unwind_hints.h +++ b/arch/loongarch/include/asm/unwind_hints.h @@ -23,6 +23,14 @@ UNWIND_HINT sp_reg=ORC_REG_SP type=UNWIND_HINT_TYPE_CALL .endm -#endif /* __ASSEMBLY__ */ +#else /* !__ASSEMBLY__ */ + +#define UNWIND_HINT_SAVE \ + UNWIND_HINT(UNWIND_HINT_TYPE_SAVE, 0, 0, 0) + +#define UNWIND_HINT_RESTORE \ + UNWIND_HINT(UNWIND_HINT_TYPE_RESTORE, 0, 0, 0) + +#endif /* !__ASSEMBLY__ */ #endif /* _ASM_LOONGARCH_UNWIND_HINTS_H */