objtool: dynamically detect noreturns
Subsystems (17)
Josh Poimboeuf
maintainerv2 cover objtool: dynamically detect noreturns
changes since v1 (https://lore.kernel.org/[email protected]):
- improve commit log for "kbuild: Defer running objtool to link time for all CFG features"
- rebase on v7.3-rc2 to get a proper Sashiko review
The C and rust manual noreturn lists are a maintenance nightmare. Get rid of them by taking advantage of the fact that objtool can run on linked objects (vmlinux.o and modules) and can thus detect noreturns dynamically:
1) When features are enabled which require the noreturn knowledge (i.e., control flow analysis) always defer objtool to run on linked objects. That obsoletes most of noreturns.h. (patch 12)
2) For noreturn functions which are exported by vmlinux, dynamically write their names to a file which is then read by objtool running on modules. That obsoletes most of the remaining noreturns.h entries and the entirety of is_rust_noreturn(). (patch 24)
3) For the (four) noreturn functions which are exported by modules, manually annotate them at their declaration sites with a new ANNOTATE_EXPORTED_NORETURN(). That removes the last few stragglers in noreturns.h. (patch 26)
Josh Poimboeuf (27):
objtool: Remove obsolete noreturns.h entries
kbuild: Add CONFIG_OBJTOOL_DEFERRED
kbuild: Add CONFIG_OBJTOOL_CONTROL_FLOW
objtool: Fix dead end detection for sibling calls
objtool: Refactor the noreturn/dead-end detection
objtool: Ignore traps after noreturn calls in STT_CODE
objtool: Make .discard.stack_frame_non_standard non-allocatable efi/libstub: Drop .discard.addressable from the stub objects efi/loongarch: Mark loongarch efi_boot_kernel() non-standard for
objtool
objtool: Add ANNOTATE_IGNORE_NORETURN()
LoongArch: Annotate reboot and kexec paths as returnable
kbuild: Defer running objtool to link time for all CFG features
rust: Annotate the intrinsic stubs as returnable
panic: Mark abort() __noreturn
x86/xen: Ignore noreturn status of weak mem_map_via_hcall()
objtool: Detect noreturns in weak functions
x86/entry: Make rewind_stack_and_make_dead() a real function x86/xen: Make xen_cpu_bringup_again() a real function x86/xen: Make xen_start_kernel() noreturn
x86/boot: Rework how pi startup symbols get exposed to vmlinux
objtool: Fix noreturn detection for non-sibling jumps to SYM_CODE
objtool: Add options to write/read exported noreturns to/from a file
kbuild: Do the per-module objtool pass right before linking
kbuild: Generate the noreturn list and validate modules against it
objtool: Add ANNOTATE_EXPORTED_NORETURN()
objtool: Annotate all module-exported noreturns and remove noreturns.h
objtool: Warn about missing/stale ANNOTATE_EXPORTED_NORETURN() usage
Makefile | 8 + arch/arc/kernel/traps.c | 4 +- arch/arm/kernel/traps.c | 2 +- arch/loongarch/kernel/machine_kexec.c | 2 + arch/loongarch/kernel/reset.c | 4 + arch/x86/boot/startup/Makefile | 16 +- arch/x86/boot/startup/exports.h | 14 - arch/x86/boot/startup/unprefix.syms | 13 + arch/x86/entry/entry_64.S | 7 +- arch/x86/kernel/vmlinux.lds.S | 2 - arch/x86/platform/pvh/enlighten.c | 1 + arch/x86/xen/enlighten_pv.c | 5 +- arch/x86/xen/xen-head.S | 5 +- arch/x86/xen/xen-ops.h | 2 +- drivers/firmware/efi/libstub/Makefile | 4 + drivers/firmware/efi/libstub/loongarch.c | 8 + drivers/message/fusion/mptbase.h | 2 + include/kunit/test.h | 1 + include/kunit/try-catch.h | 2 + include/linux/annotate.h | 24 ++ include/linux/objtool.h | 13 +- include/linux/objtool_types.h | 1 + include/linux/panic.h | 2 +- include/linux/rtc/ds1685.h | 2 + kernel/exit.c | 2 +- lib/Kconfig.debug | 14 + lib/Kconfig.ubsan | 2 +- rust/.gitignore | 1 + rust/Makefile | 17 + scripts/.gitignore | 1 + scripts/Makefile.build | 31 +- scripts/Makefile.lib | 4 +- scripts/Makefile.modfinal | 35 +- scripts/Makefile.vmlinux_o | 13 +- scripts/link-vmlinux.sh | 3 +- scripts/livepatch/klp-build | 4 +- tools/include/linux/objtool_types.h | 1 + tools/objtool/Documentation/objtool.txt | 30 +- tools/objtool/builtin-check.c | 12 + tools/objtool/check.c | 437 +++++++++++++++++------ tools/objtool/include/objtool/builtin.h | 2 + tools/objtool/include/objtool/elf.h | 4 + tools/objtool/klp-diff.c | 32 ++ tools/objtool/noreturns.h | 56 --- 44 files changed, 584 insertions(+), 261 deletions(-) delete mode 100644 arch/x86/boot/startup/exports.h create mode 100644 arch/x86/boot/startup/unprefix.syms delete mode 100644 tools/objtool/noreturns.h
Signature · 1 line
2.55.0
Josh Poimboeuf
maintainerPATCH v2 1/27 objtool: Remove obsolete noreturns.h entries
Remove some obsolete noreturns.h entries: bcachefs is no longer in-tree, kthread_exit() is a macro, and rust_begin_unwind() is only called by its mangled name.
--- tools/objtool/noreturns.h | 5 ----- 1 file changed, 5 deletions(-)
tools/objtool/noreturns.h +0 -5
@@ -18,9 +18,6 @@ NORETURN(__x64_sys_exit) NORETURN(__x64_sys_exit_group) NORETURN(acpi_processor_ffh_play_dead) NORETURN(arch_cpu_idle_dead) -NORETURN(bch2_trans_in_restart_error) -NORETURN(bch2_trans_restart_error) -NORETURN(bch2_trans_unlocked_or_in_restart_error) NORETURN(cpu_bringup_and_idle) NORETURN(cpu_startup_entry) NORETURN(do_exit)
@@ -31,7 +28,6 @@ NORETURN(ex_handler_msr_mce) NORETURN(hlt_play_dead) NORETURN(hv_ghcb_terminate) NORETURN(kthread_complete_and_exit) -NORETURN(kthread_exit) NORETURN(kunit_try_catch_throw) NORETURN(machine_real_restart) NORETURN(make_task_dead)
@@ -44,7 +40,6 @@ NORETURN(vpanic) NORETURN(panic_smp_self_stop) NORETURN(rest_init) NORETURN(rewind_stack_and_make_dead) -NORETURN(rust_begin_unwind) NORETURN(rust_helper_BUG) NORETURN(sev_es_terminate) NORETURN(start_kernel)
Signature · 1 line
2.55.0
Josh Poimboeuf
maintainerPATCH v2 2/27 kbuild: Add CONFIG_OBJTOOL_DEFERRED
In reply to Josh PoimboeufFor LTO, IBT, and KLP builds, the running of objtool is deferred to vmlinux.o rather than individual translation units. In preparation for deferring it in more cases, add a new config option.
No functional change.
--- arch/x86/boot/startup/Makefile | 2 +- lib/Kconfig.debug | 7 +++++++ scripts/Makefile.build | 4 ++-- scripts/Makefile.lib | 4 +--- scripts/Makefile.vmlinux_o | 13 +++++++------ scripts/link-vmlinux.sh | 3 +-- 6 files changed, 19 insertions(+), 14 deletions(-)
arch/x86/boot/startup/Makefile +1 -1
@@ -36,7 +36,7 @@ $(patsubst %.o,$(obj)/%.o,$(lib-y)): OBJECT_FILES_NON_STANDARD := y # relocations, even if other objtool actions are being deferred. # $(pi-objs): objtool-enabled = 1 -$(pi-objs): objtool-args = $(if $(delay-objtool),--dry-run,$(objtool-args-y)) --noabs +$(pi-objs): objtool-args = $(if $(CONFIG_OBJTOOL_DEFERRED),--dry-run,$(objtool-args-y)) --noabs # # Confine the startup code by prefixing all symbols with __pi_ (for position
lib/Kconfig.debug +7 -0
@@ -588,6 +588,13 @@ config NOINSTR_VALIDATION select OBJTOOL default y +# For when objtool defers its work to the linked image (vmlinux.o or module.o) +# rather than running on individual translation units. +config OBJTOOL_DEFERRED + def_bool y + depends on OBJTOOL + depends on LTO_CLANG || X86_KERNEL_IBT || KLP_BUILD + config VMLINUX_MAP bool "Generate vmlinux.map file when linking" depends on EXPERT
scripts/Makefile.build +2 -2
@@ -278,7 +278,7 @@ endif # CONFIG_FTRACE_MCOUNT_USE_RECORDMCOUNT is-standard-object = $(if $(filter-out y%, $(OBJECT_FILES_NON_STANDARD_$(target-stem).o)$(OBJECT_FILES_NON_STANDARD)n),$(is-kernel-object)) ifdef CONFIG_OBJTOOL -$(obj)/%.o: private objtool-enabled = $(if $(is-standard-object),$(if $(delay-objtool),$(is-single-obj-m),y)) +$(obj)/%.o: private objtool-enabled = $(if $(is-standard-object),$(if $(CONFIG_OBJTOOL_DEFERRED),$(is-single-obj-m),y)) endif ifneq ($(findstring 1, $(KBUILD_EXTRA_WARN)),)
@@ -504,7 +504,7 @@ define rule_ld_multi_m $(call cmd,gen_objtooldep) endef -$(multi-obj-m): private objtool-enabled := $(delay-objtool) +$(multi-obj-m): private objtool-enabled := $(CONFIG_OBJTOOL_DEFERRED) $(multi-obj-m): private part-of-module := y $(multi-obj-m): %.o: %.mod FORCE $(call if_changed_rule,ld_multi_m)
scripts/Makefile.lib +1 -3
@@ -210,11 +210,9 @@ objtool-args-$(or $(CONFIG_GCOV_KERNEL),$(CONFIG_KCOV)) += --no-unreachable objtool-args-$(CONFIG_OBJTOOL_WERROR) += --werror objtool-args = $(objtool-args-y) \ - $(if $(delay-objtool), --link) \ + $(if $(CONFIG_OBJTOOL_DEFERRED), --link) \ $(if $(part-of-module), --module) -delay-objtool := $(or $(CONFIG_LTO_CLANG),$(CONFIG_X86_KERNEL_IBT),$(CONFIG_KLP_BUILD)) - cmd_objtool = $(if $(objtool-enabled), ; $(objtool) $(objtool-args) $@) cmd_gen_objtooldep = $(if $(objtool-enabled), { echo ; echo '$@: $$(wildcard $(objtool))' ; } >> $(dot-target).cmd)
scripts/Makefile.vmlinux_o +7 -6
@@ -30,15 +30,16 @@ endif # objtool for vmlinux.o # --------------------------------------------------------------------------- # -# For delay-objtool (IBT or LTO), objtool doesn't run on individual translation -# units. Instead it runs on vmlinux.o. +# For CONFIG_OBJTOOL_DEFERRED (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. +# For !CONFIG_OBJTOOL_DEFERRED + 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)) +objtool-enabled := $(or $(CONFIG_OBJTOOL_DEFERRED),$(CONFIG_NOINSTR_VALIDATION)) -ifeq ($(delay-objtool),y) +ifeq ($(CONFIG_OBJTOOL_DEFERRED),y) vmlinux-objtool-args-y += $(objtool-args-y) else vmlinux-objtool-args-$(CONFIG_OBJTOOL_WERROR) += --werror
scripts/link-vmlinux.sh +1 -2
@@ -60,8 +60,7 @@ vmlinux_link() # skip output file argument shift - if is_enabled CONFIG_LTO_CLANG || is_enabled CONFIG_X86_KERNEL_IBT || - is_enabled CONFIG_KLP_BUILD; then + if is_enabled CONFIG_OBJTOOL_DEFERRED || is_enabled CONFIG_LTO_CLANG; then # Use vmlinux.o instead of performing the slow LTO link again. objs=vmlinux.o libs=
Signature · 1 line
2.55.0
Re: [PATCH v2 02/27] kbuild: Add CONFIG_OBJTOOL_DEFERRED
Hi Josh,
Quoted 34 lines · Josh Poimboeuf
For LTO, IBT, and KLP builds, the running of objtool is deferred to vmlinux.o rather than individual translation units. In preparation for deferring it in more cases, add a new config option.
No functional change.
Signed-off-by Josh Poimboeuf <[email protected]>--- arch/x86/boot/startup/Makefile | 2 +- lib/Kconfig.debug | 7 +++++++ scripts/Makefile.build | 4 ++-- scripts/Makefile.lib | 4 +--- scripts/Makefile.vmlinux_o | 13 +++++++------ scripts/link-vmlinux.sh | 3 +-- 6 files changed, 19 insertions(+), 14 deletions(-)arch/x86/boot/startup/Makefile +1
-1@@ -36,7 +36,7 @@ $(patsubst %.o,$(obj)/%.o,$(lib-y)): OBJECT_FILES_NON_STANDARD := y # relocations, even if other objtool actions are being deferred. # $(pi-objs): objtool-enabled = 1 -$(pi-objs): objtool-args = $(if $(delay-objtool),--dry-run,$(objtool-args-y)) --noabs +$(pi-objs): objtool-args = $(if $(CONFIG_OBJTOOL_DEFERRED),--dry-run,$(objtool-args-y)) --noabs # # Confine the startup code by prefixing all symbols with __pi_ (for positionlib/Kconfig.debug +0
-0
lib/Kconfig.debug config NOINSTR_VALIDATION · line 597
Quoted 10 lines
lib/Kconfig.debug +7
-0@@ -588,6 +588,13 @@ config NOINSTR_VALIDATION select OBJTOOL default y +# For when objtool defers its work to the linked image (vmlinux.o or module.o) +# rather than running on individual translation units. +config OBJTOOL_DEFERRED + def_bool y + depends on OBJTOOL + depends on LTO_CLANG || X86_KERNEL_IBT || KLP_BUILD +
Just commenting: if you move this option OBJTOOL_DEFERRED (and OBJTOOL_CONTROL_FLOW in the next patch) a few entries up in this Kconfig file, menuconfig will render them as part of the OBJTOOL submenu because of the dependency on OBJTOOL.
E.g. this ordering:
OBJTOOLOBJTOOL_WERROROBJTOOL_DEFERREDOBJTOOL_CONTROL_FLOWSTACK_VALIDATIONNOINSTR_VALIDATION
would render better.
But this is minor, I don't think it necessitates a v3 on its own.
- Julian Braha
Josh Poimboeuf
maintainerPATCH v2 3/27 kbuild: Add CONFIG_OBJTOOL_CONTROL_FLOW
In reply to Josh PoimboeufAdd a config option for when objtool reverse engineers the control flow graph of every function.
No functional change.
--- lib/Kconfig.debug | 10 ++++++++-- lib/Kconfig.ubsan | 2 +- 2 files changed, 9 insertions(+), 3 deletions(-)
lib/Kconfig.debug +8 -2
@@ -588,8 +588,14 @@ config NOINSTR_VALIDATION select OBJTOOL default y -# For when objtool defers its work to the linked image (vmlinux.o or module.o) -# rather than running on individual translation units. +# Objtool reverse-engineers the control flow graph +config OBJTOOL_CONTROL_FLOW + def_bool y + depends on OBJTOOL + depends on UNWINDER_ORC || STACK_VALIDATION || HAVE_UACCESS_VALIDATION + +# Objtool defers its work to the linked image (vmlinux.o or module.o) rather +# than running on individual translation units. config OBJTOOL_DEFERRED def_bool y depends on OBJTOOL
lib/Kconfig.ubsan +1 -1
@@ -110,7 +110,7 @@ config UBSAN_UNREACHABLE bool "Perform checking for unreachable code" # objtool already handles unreachable checking and gets angry about # seeing UBSan instrumentation located in unreachable places. - depends on !(OBJTOOL && (STACK_VALIDATION || UNWINDER_ORC || HAVE_UACCESS_VALIDATION)) + depends on !OBJTOOL_CONTROL_FLOW depends on $(cc-option,-fsanitize=unreachable) help This option enables -fsanitize=unreachable which checks for control
Signature · 1 line
2.55.0
Josh Poimboeuf
maintainerPATCH v2 4/27 objtool: Fix dead end detection for sibling calls
In reply to Josh PoimboeufThe dead end detection for sibling calls has apparently always been broken. There are two issues: it checks jump_dest instead of call_dest, and it only checks the first sibling call instead of all of them. Fix both issues.
No warnings were seen, this was only found by code inspection.
--- tools/objtool/check.c | 37 +++++++++++++++++++------------------ 1 file changed, 19 insertions(+), 18 deletions(-)
tools/objtool/check.c +19 -18
@@ -274,30 +274,31 @@ static bool __dead_end_function(struct objtool_file *file, struct symbol *func,return false;/* -* A function can have a sibling call instead of a return. In that -* case, the function's dead-end status depends on whether the target -* of the sibling call returns. +* A function can have sibling calls instead of a return. It's only a +* dead end if *all* the sibling call targets are dead ends. */ func_for_each_insn (file, func, insn) { -if (is_sibling_call (insn)) { -struct instruction *dest = insn->jump_dest; +struct symbol *dest; -if (!dest) -/* sibling call to another file */ -return false; +if (!is_sibling_call (insn)) +continue ; -/* local sibling call */ -if (recursion ==5 ) { -/* -* Infinite recursion: two functions have -* sibling calls to each other. This is a very -* rare case. It means they aren't dead ends. -*/ -return false; - } + dest =insn_call_dest (insn); +if (!dest) +/* call to another file */ +return false; -return __dead_end_function (file,insn_func (dest), recursion+1 ); +if (recursion ==5 ) { +/* +* Infinite recursion: two functions have sibling +* calls to each other. This is a very rare case. +* It means they aren't dead ends. +*/ +return false; } + +if (!__dead_end_function (file, dest, recursion+1 )) +return false; }return true;
Signature · 1 line
2.55.0
Josh Poimboeuf
maintainerPATCH v2 5/27 objtool: Refactor the noreturn/dead-end detection
In reply to Josh PoimboeufThe dead end detection (basically "is this a call to a noreturn?") is currently derived on demand, at each call site. Also, the answer isn't saved anywhere, so it's redetermined each time. It also has an ugly recursion check.
Clean that up by just detecting all the noreturns at the beginning.
--- tools/objtool/check.c | 159 ++++++++++++++++------------ tools/objtool/include/objtool/elf.h | 1 + 2 files changed, 93 insertions(+), 67 deletions(-)
tools/objtool/check.c +92 -67
@@ -219,94 +219,112 @@ static bool is_rust_noreturn(const struct symbol *func) str_ends_with(func->name, "_fail")); } -/* -* This checks to see if the given function is a "noreturn" function. -* -* For global functions which are outside the scope of this object file, we -* have to keep a manual list of them. -* -* For local functions, we have to detect them manually by simply looking for -* the lack of a return instruction. -*/ -static bool __dead_end_function (struct objtool_file *file,struct symbol *func, -int recursion) +static bool is_listed_noreturn (struct symbol *func) { -int i; -struct instruction *insn; -bool empty = true; -#define NORETURN (func) __stringify(func),static const char *const global_noreturns[] = { #include "noreturns.h" };#undef NORETURN -if (!func) +if (is_local_sym (func))return false; -if (!is_local_sym (func)) { -if (is_rust_noreturn (func)) +if (is_rust_noreturn (func)) +return true; + +for (int i =0 ; i <ARRAY_SIZE (global_noreturns); i++) +if (!strcmp (func->name, global_noreturns[i]))return true; -for (i =0 ; i <ARRAY_SIZE (global_noreturns); i++) -if (!strcmp (func->name, global_noreturns[i])) -return true; - } +return false; +} + +/* +* Use this rather than reading sym->_noreturn directly: the noreturn status +* lives on the primary alias, and ANNOTATE_IGNORE_NORETURN() overrides it. +*/ +static bool is_noreturn (struct symbol *func) +{ + func = func->alias->pfunc; + +if (is_listed_noreturn (func)) +return true; + +return func->_noreturn; +} + +static bool might_return (struct objtool_file *file,struct symbol *func) +{ +struct instruction *insn; +struct symbol *dest;if (is_weak_sym (func)) -return false; - -if (!func->len) -return false; - - insn =find_insn (file, func->sec, func->offset); -if (!insn || !insn_func (insn)) -return false; +return true;func_for_each_insn (file, func, insn) { - empty = false; -if (insn->type ==INSN_RETURN ) -return false; - } - -if (empty) -return false; - -/* -* A function can have sibling calls instead of a return. It's only a -* dead end if *all* the sibling call targets are dead ends. -*/ -func_for_each_insn (file, func, insn) { -struct symbol *dest; +return true;if (!is_sibling_call (insn))continue ; dest =insn_call_dest (insn); -if (!dest) -/* call to another file */ -return false; - -if (recursion ==5 ) { -/* -* Infinite recursion: two functions have sibling -* calls to each other. This is a very rare case. -* It means they aren't dead ends. -*/ -return false; - } - -if (!__dead_end_function (file, dest, recursion+1 )) -return false; +if (!dest || !is_noreturn (dest)) +return true; } -return true; +return false; } -static bool dead_end_function (struct objtool_file *file,struct symbol *func) +static void detect_noreturns (struct objtool_file *file) { -return __dead_end_function (file, func,0 ); +struct symbol *func, *dest; +struct instruction *insn; +bool changed; + +/* Mark all functions guilty until proven innocent */ +for_each_sym (file->elf, func) { + +/* Aliases and cold subfunctions inherit the parent's verdict */ +if (!is_func_sym (func) ||is_undef_sym (func) || +is_prefix_func (func) || func->embedded_insn || + func != func->alias->pfunc) +continue ; + + insn =find_insn (file, func->sec, func->offset); +if (!insn ||insn_func (insn) != func) +continue ; + + func->_noreturn =1 ; + } + +/* +* A function's noreturn status depends on those of its sibling call +* destinations, which may not be settled yet. Keep clearing the +* noreturn bit for known cases until it stops spreading. +*/ +do { + changed = false; + +for_each_sym (file->elf, func) { +if (!func->_noreturn || !might_return (file, func)) +continue ; + + func->_noreturn =0 ; + changed = true; + } + }while (changed); + +/* Now mark the dead end call sites */ +for_each_insn (file, insn) { +if (insn->type !=INSN_CALL ) +continue ; + + dest =insn_call_dest (insn); +if (dest &&is_noreturn (dest)) + insn->dead_end = true; + } }static void init_cfi_state (struct cfi_state *cfi)
@@ -1422,9 +1440,6 @@ static int annotate_call_site(struct objtool_file *file, !insn->_call_dest->embedded_insn)list_add_tail (&insn->call_node, &file->call_list); -if (!sibling &&dead_end_function (file, sym)) - insn->dead_end = true; -return 0 ; }
@@ -2669,6 +2684,17 @@ int decode_file(struct objtool_file *file)if (add_jump_table_alts (file))return -1 ; +/* +* Must be after add_jump_table_alts(), which affects sibling call +* detection (jump table branches vs indirect sibling calls). +* +* The dead end marks are read by validate_branch() -- reached from +* both validate_functions() and validate_noinstr_sections() -- and by +* validate_unret(). +*/ +if (validate_branch_enabled () || opts.noinstr || opts.unret) +detect_noreturns (file); +if (read_unwind_hints (file))return -1 ;
@@ -2676,8 +2702,7 @@ int decode_file(struct objtool_file *file)mark_holes (file);/* -* Must be after add_call_destinations() such that it can override -* dead_end_function() marks. +* Must be after detect_noreturns() so it can override dead_end marks. */ if (read_annotate (file, __annotate_late))return -1 ;
tools/objtool/include/objtool/elf.h +1 -0
@@ -98,6 +98,7 @@ struct symbol { u8 klp : 1; u8 dont_correlate : 1; u8 fake : 1; + u8 _noreturn : 1;struct list_head pv_target;struct reloc *relocs;struct section *group_sec;
Signature · 1 line
2.55.0
Josh Poimboeuf
maintainerPATCH v2 6/27 objtool: Ignore traps after noreturn calls in STT_CODE
In reply to Josh PoimboeufObjtool doesn't do unreachable warnings for traps after noreturn calls, but only in functions. Loongarch assembly STT_CODE can have the same pattern:
bl start_kernel
ASM_BUG()
Ignore those as well.
--- tools/objtool/check.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-)
tools/objtool/check.c +6 -6
@@ -4271,12 +4271,6 @@ static bool ignore_unreachable_insn(struct objtool_file *file, struct instructio !strcmp(insn->sec->name, ".altinstr_aux"))return true; -if (!func) -return false; - -if (func->static_call_tramp) -return true; -/* * CONFIG_UBSAN_TRAP inserts a UD2 when it sees * __builtin_unreachable(). The BUG() macro has an unreachable() after
@@ -4292,6 +4286,12 @@ static bool ignore_unreachable_insn(struct objtool_file *file, struct instructio insn->jump_dest && insn->jump_dest->type == INSN_BUG)))return true; +if (!func) +return false; + +if (func->static_call_tramp) +return true; +/* * Check if this (or a subsequent) instruction is related to * CONFIG_UBSAN or CONFIG_KASAN.
Signature · 1 line
2.55.0
Josh Poimboeuf
maintainerPATCH v2 7/27 objtool: Make .discard.stack_frame_non_standard non-allocatable
In reply to Josh PoimboeufConvert STACK_FRAME_NON_STANDARD() to an asm macro to prevent it section from being allocatable.
This will help it survive being used in libstub, which renames all allocatable sections.
--- include/linux/objtool.h | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-)
include/linux/objtool.h +8 -5
@@ -4,6 +4,7 @@#include <linux/objtool_types.h> #include <linux/annotate.h> +#include <linux/compiler.h> #ifdef CONFIG_OBJTOOL
@@ -30,9 +31,11 @@* * For more information, see tools/objtool/Documentation/objtool.txt. */ -#define STACK_FRAME_NON_STANDARD (func) \ - static void __used __section(".discard.func_stack_frame_non_standard") \ - *__func_stack_frame_non_standard_##func = func +#define STACK_FRAME_NON_STANDARD (func) \ + __ADDRESSABLE(func); \ + asm(".pushsection .discard.func_stack_frame_non_standard\n\t" \ + ".long " #func " - .\n\t" \ + ".popsection")/* * STACK_FRAME_NON_STANDARD_FP() is a frame-pointer-specific function ignore
@@ -91,8 +94,8 @@ .endm .macro STACK_FRAME_NON_STANDARD func:req - .pushsection .discard.func_stack_frame_non_standard,"aw" - .quad \func + .pushsection .discard.func_stack_frame_non_standard,"" + .long \func - . .popsection .endm
Signature · 1 line
2.55.0
Josh Poimboeuf
maintainerPATCH v2 8/27 efi/libstub: Drop .discard.addressable from the stub objects
In reply to Josh PoimboeufOn arm64, riscv and loongarch, the EFI stub objects are objcopy'd with --prefix-alloc-sections=.init, which renames every allocatable section.
The .discard.addressable section is allocatable, so if STACK_FRAME_NON_STANDARD() and __ADDRESSABLE() are used, the section gets renamed and never discarded:
ld: warning: orphan section .init.discard.addressable' from vmlinux.o' being placed in section `.init.discard.addressable'
In preparation for using STACK_FRAME_NON_STANDARD() in libstub code, remove the section during objcopy.
--- drivers/firmware/efi/libstub/Makefile | 4 ++++ 1 file changed, 4 insertions(+)
drivers/firmware/efi/libstub/Makefile +4 -0
@@ -155,6 +155,10 @@ STUBCOPY_FLAGS-$(CONFIG_LOONGARCH) += --prefix-alloc-sections=.init \ --prefix-symbols=__efistub_ STUBCOPY_RELOC-$(CONFIG_LOONGARCH) := R_LARCH_MARK_LA +# Drop the section created by __ADDRESSABLE() so it doesn't get prefixed. +# Its job is already done. +STUBCOPY_FLAGS-y += --remove-section=.discard.addressable + $(obj)/%.stub.o: $(obj)/%.o FORCE $(call if_changed,stubcopy)
Signature · 1 line
2.55.0
Josh Poimboeuf
maintainerPATCH v2 9/27 efi/loongarch: Mark loongarch efi_boot_kernel() non-standard for objtool
In reply to Josh Poimboeufefi_boot_kernel() ends with a tail call through a __noreturn function pointer, so the compiler emits nothing after it. Objtool can't tell that in indirect call doesn't return, so it sees execution fall off the end of the function:
vmlinux.o: warning: objtool: __efistub_efi_boot_kernel() falls through to next function __efistub_efi_get_memory_map()
This is boot code for which objtool/ORC coverage doesn't matter, so tell objtool to ignore it.
--- drivers/firmware/efi/libstub/loongarch.c | 8 ++++++++ 1 file changed, 8 insertions(+)
drivers/firmware/efi/libstub/loongarch.c +8 -0
@@ -5,6 +5,7 @@* Copyright (C) 2020-2022 Loongson Technology Corporation Limited */ +#include <linux/objtool.h> #include <asm/efi.h> #include <asm/addrspace.h> #include "efistub.h"
@@ -103,3 +104,10 @@ efi_status_t efi_boot_kernel(void *handle, efi_loaded_image_t *image,real_kernel_entry (true, (unsigned long )cmdline_ptr, (unsigned long )efi_system_table); } + +/* +* Objtool has no way of knowing the real_kernel_entry() indirect call is +* noreturn. Just tell it to ignore this function: it's boot code anyway, so +* ORC coverage isn't needed. +*/ +STACK_FRAME_NON_STANDARD (efi_boot_kernel);
Signature · 1 line
2.55.0
Josh Poimboeuf
maintainerPATCH v2 10/27 objtool: Add ANNOTATE_IGNORE_NORETURN()
In reply to Josh PoimboeufA generic interface may be noreturn in one arch and returnable in another, in which case its common declaration can't say __noreturn.
Rust has a similar issue where a few core library calls to returnable functions are replaced after compilation with calls to a noreturn panic function.
Add an annotation which tells objtool to ignore the noreturn status of a given function.
--- include/linux/annotate.h | 7 +++++++ include/linux/objtool_types.h | 1 + tools/include/linux/objtool_types.h | 1 + tools/objtool/check.c | 19 +++++++++++++++++++ tools/objtool/include/objtool/elf.h | 1 + 5 files changed, 29 insertions(+)
include/linux/annotate.h +7 -0
@@ -3,6 +3,7 @@#define _LINUX_ANNOTATE_H#include <linux/objtool_types.h> +#include <linux/stringify.h> #ifdef CONFIG_OBJTOOL
@@ -105,6 +106,12 @@*/ #define ANNOTATE_NOCFI_SYM (sym) asm(ASM_ANNOTATE_LABEL(sym, ANNOTYPE_NOCFI)) +/* +* Treat a function as returnable by its callers despite objtool classifying it +* as noreturn. +*/ +#define ANNOTATE_IGNORE_NORETURN (sym) asm(ASM_ANNOTATE_LABEL(sym, ANNOTYPE_IGNORE_NORETURN)) +/* * Annotate a special section entry. This emables livepatch module generation * to find and extract individual special section entries as needed.
include/linux/objtool_types.h +1 -0
@@ -66,6 +66,7 @@ struct unwind_hint {#define ANNOTYPE_INTRA_FUNCTION_CALL 7#define ANNOTYPE_REACHABLE 8#define ANNOTYPE_NOCFI 9 +#define ANNOTYPE_IGNORE_NORETURN 10#define ANNOTYPE_DATA_SPECIAL 1
tools/include/linux/objtool_types.h +1 -0
@@ -66,6 +66,7 @@ struct unwind_hint {#define ANNOTYPE_INTRA_FUNCTION_CALL 7#define ANNOTYPE_REACHABLE 8#define ANNOTYPE_NOCFI 9 +#define ANNOTYPE_IGNORE_NORETURN 10#define ANNOTYPE_DATA_SPECIAL 1
tools/objtool/check.c +19 -0
@@ -248,6 +248,9 @@ static bool is_noreturn(struct symbol *func) { func = func->alias->pfunc; +if (func->ignore_noreturn) +return false; +if (is_listed_noreturn (func))return true;
@@ -2391,6 +2394,18 @@ static int __annotate_early(struct objtool_file *file, int type, struct instruct insn->noendbr =1 ;break ; +/* Must be before detect_noreturns() */ +case ANNOTYPE_IGNORE_NORETURN : { +struct symbol *sym =insn_sym (insn); + +if (!sym) { +ERROR_INSN (insn,"dodgy IGNORE_NORETURN annotation" ); +return -1 ; + } + sym->ignore_noreturn =1 ; +break ; + } +default :break ; }
@@ -2437,6 +2452,10 @@ static int __annotate_late(struct objtool_file *file, int type, struct instructi/* early */ break ; +case ANNOTYPE_IGNORE_NORETURN : +/* early */ +break ; +case ANNOTYPE_RETPOLINE_SAFE : if (insn->type != INSN_JUMP_DYNAMIC && insn->type != INSN_CALL_DYNAMIC &&
tools/objtool/include/objtool/elf.h +1 -0
@@ -99,6 +99,7 @@ struct symbol { u8 dont_correlate : 1; u8 fake : 1; u8 _noreturn : 1; + u8 ignore_noreturn : 1;struct list_head pv_target;struct reloc *relocs;struct section *group_sec;
Signature · 1 line
2.55.0
Josh Poimboeuf
maintainerPATCH v2 11/27 LoongArch: Annotate reboot and kexec paths as returnable
In reply to Josh PoimboeufRunning objtool on LoongArch vmlinux.o shows the following warnings:
vmlinux.o: warning: objtool: __do_sys_reboot+0x188: kernel_restart() is missing __noreturn in .c/.h vmlinux.o: warning: objtool: hw_failure_emergency_action_func+0x74: kernel_restart() is missing __noreturn in .c/.h vmlinux.o: warning: objtool: hibernate+0x3cc: kernel_halt() is missing __noreturn in .c/.h vmlinux.o: warning: objtool: kernel_kexec+0xbc: machine_kexec() is missing __noreturn in .c/.h vmlinux.o: warning: objtool: restart_poweroff_do_poweroff+0x1c: machine_restart() is missing __noreturn in .c/.h
The problem is that the generic declarations of the above functions are not declared noreturn.
But marking them __noreturn wouldn't be straightforward given the inconsistent behaviors they have across the arches:
- In many cases they don't return, but the compiler doesn't have visibility to that for various reasons including inline asm and indirect calls to noreturn functions (which objtool is currently not equipped to deal with).
- In some cases they even *do* return, e.g. x86 machine_exec(), arm64 machine_power_off(), and hexagon machine_restart().
In lieu of undertaking such a large cleanup, fix the above warnings with a few annotations so objtool doesn't consider them noreturn, consistent with their call sites.
--- arch/loongarch/kernel/machine_kexec.c | 2 ++ arch/loongarch/kernel/reset.c | 4 ++++ 2 files changed, 6 insertions(+)
arch/loongarch/kernel/machine_kexec.c +2 -0
@@ -16,6 +16,7 @@#include <linux/reboot.h> #include <linux/sched.h> #include <linux/sched/task_stack.h> +#include <linux/annotate.h> #include <asm/bootinfo.h> #include <asm/cacheflush.h>
@@ -296,3 +297,4 @@ void machine_kexec(struct kimage *image)kexec_reboot (); } +ANNOTATE_IGNORE_NORETURN (machine_kexec);
arch/loongarch/kernel/reset.c +4 -0
@@ -11,6 +11,7 @@#include <linux/reboot.h> #include <linux/delay.h> #include <linux/console.h> +#include <linux/annotate.h> #include <acpi/reboot.h> #include <asm/idle.h>
@@ -36,6 +37,7 @@ void machine_halt(void) __asm__ __volatile__("idle 0" : : :"memory" ); } } +ANNOTATE_IGNORE_NORETURN (machine_halt);void machine_power_off (void ) {
@@ -56,6 +58,7 @@ void machine_power_off(void) __asm__ __volatile__("idle 0" : : :"memory" ); } } +ANNOTATE_IGNORE_NORETURN (machine_power_off);void machine_restart (char *command) {
@@ -77,3 +80,4 @@ void machine_restart(char *command) __asm__ __volatile__("idle 0" : : :"memory" ); } } +ANNOTATE_IGNORE_NORETURN (machine_restart);
Signature · 1 line
2.55.0
Josh Poimboeuf
maintainerPATCH v2 12/27 kbuild: Defer running objtool to link time for all CFG features
In reply to Josh Poimboeufnoreturns.h file is hand-maintained file which hard codes all the known __noreturn functions in the kernel. It's fragile, often missing entries, and keeping it up to date is a maintenance burden.
For LTO, IBT, and klp-build configs, the majority of those entries aren't needed, because objtool runs on vmlinux.o, so it already has visibility into whether a given function returns (with its dead end detection).
Remove the need for many of the noreturns.h entries by just always deferring objtool for all features which rely on having the noreturn
information: namely the ones which rely on the reverse-engineered control flow graph (CFG).
Note that NOINSTR_VALIDATION is a special case, where objtool runs on individual TUs (with all the objtool-args-y), and then again on vmlinux.o (without objtool-args-y except for --werror).
This change makes NOINSTR_VALIDATION just another OBJTOOL_DEFERRED feature which runs deferred, and *not* on TUs, so now it gets all the args like the others, and the special case for adding --werror on !OBJTOOL_DEFERRED is no longer needed.
--- lib/Kconfig.debug | 3 ++- scripts/Makefile.vmlinux_o | 14 +++----------- tools/objtool/noreturns.h | 24 ------------------------ 3 files changed, 5 insertions(+), 36 deletions(-)
lib/Kconfig.debug +2 -1
@@ -599,7 +599,8 @@ config OBJTOOL_CONTROL_FLOW config OBJTOOL_DEFERRED def_bool y depends on OBJTOOL - depends on LTO_CLANG || X86_KERNEL_IBT || KLP_BUILD + depends on OBJTOOL_CONTROL_FLOW || NOINSTR_VALIDATION || LTO_CLANG || \ + X86_KERNEL_IBT || KLP_BUILD config VMLINUX_MAP bool "Generate vmlinux.map file when linking"
scripts/Makefile.vmlinux_o +3 -11
@@ -30,20 +30,12 @@ endif # objtool for vmlinux.o # --------------------------------------------------------------------------- # -# For CONFIG_OBJTOOL_DEFERRED (IBT or LTO), objtool doesn't run on individual -# translation units. Instead it runs on vmlinux.o. -# -# For !CONFIG_OBJTOOL_DEFERRED + CONFIG_NOINSTR_VALIDATION, it runs on both -# translation units and vmlinux.o, with the latter only used for noinstr/unret -# validation. +# For CONFIG_OBJTOOL_DEFERRED, objtool doesn't run on individual translation +# units. Instead it runs on vmlinux.o. -objtool-enabled := $(or $(CONFIG_OBJTOOL_DEFERRED),$(CONFIG_NOINSTR_VALIDATION)) +objtool-enabled := $(CONFIG_OBJTOOL_DEFERRED) -ifeq ($(CONFIG_OBJTOOL_DEFERRED),y) vmlinux-objtool-args-y += $(objtool-args-y) -else -vmlinux-objtool-args-$(CONFIG_OBJTOOL_WERROR) += --werror -endif vmlinux-objtool-args-$(CONFIG_NOINSTR_VALIDATION) += --noinstr \ $(if $(or $(CONFIG_MITIGATION_UNRET_ENTRY),$(CONFIG_MITIGATION_SRSO)), --unret)
tools/objtool/noreturns.h +0 -24
@@ -7,45 +7,21 @@* Yes, this is unfortunate. A better solution is in the works. */ NORETURN(__fortify_panic) -NORETURN(__ia32_sys_exit) -NORETURN(__ia32_sys_exit_group) NORETURN(__kunit_abort) NORETURN(__module_put_and_kthread_exit) NORETURN(__stack_chk_fail) -NORETURN(__tdx_hypercall_failed) NORETURN(__ubsan_handle_builtin_unreachable) -NORETURN(__x64_sys_exit) -NORETURN(__x64_sys_exit_group) NORETURN(acpi_processor_ffh_play_dead) -NORETURN(arch_cpu_idle_dead) -NORETURN(cpu_bringup_and_idle) -NORETURN(cpu_startup_entry) NORETURN(do_exit) -NORETURN(do_group_exit) -NORETURN(do_task_dead) -NORETURN(efi_rts_park_worker) -NORETURN(ex_handler_msr_mce) -NORETURN(hlt_play_dead) -NORETURN(hv_ghcb_terminate) NORETURN(kthread_complete_and_exit) NORETURN(kunit_try_catch_throw) -NORETURN(machine_real_restart) -NORETURN(make_task_dead) NORETURN(mpt_halt_firmware) -NORETURN(mwait_play_dead) -NORETURN(native_play_dead) NORETURN(nmi_panic_self_stop) NORETURN(panic) NORETURN(vpanic) NORETURN(panic_smp_self_stop) -NORETURN(rest_init) NORETURN(rewind_stack_and_make_dead) NORETURN(rust_helper_BUG) NORETURN(sev_es_terminate) -NORETURN(start_kernel) -NORETURN(stop_this_cpu) -NORETURN(usercopy_abort) -NORETURN(x86_64_start_kernel) -NORETURN(x86_64_start_reservations) NORETURN(xen_cpu_bringup_again) NORETURN(xen_start_kernel)
Signature · 1 line
2.55.0
Josh Poimboeuf
maintainerPATCH v2 13/27 rust: Annotate the intrinsic stubs as returnable
In reply to Josh PoimboeufWhen building the Rust core library, the compiler emits calls to compiler-rt builtins like __udivti3() for 128-bit arithmetic, which the kernel doesn't want, so objcopy is used to redirect those calls to stubs which just panic.
Problem is, their call sites still expect them to return, which confuses objtool.
Objtool doesn't report any warnings today because the stubs are weak, which noreturn detection currently ignores. In preparation for removing that restriction, annotate them as returnable.
--- rust/.gitignore | 1 + rust/Makefile | 17 +++++++++++++++++ 2 files changed, 18 insertions(+)
rust/.gitignore +1 -0
@@ -6,5 +6,6 @@ doctests_kernel_generated.rs doctests_kernel_generated_kunit.c uapi_generated.rs exports_*_generated.h +intrinsics_annotate_generated.c doc/ test/
rust/Makefile +17 -0
@@ -36,6 +36,9 @@ endif obj-$(CONFIG_RUST) += exports.o +always-$(CONFIG_RUST) += intrinsics_annotate_generated.c +obj-$(CONFIG_RUST) += intrinsics_annotate_generated.o + always-$(CONFIG_RUST) += host/libproc_macro2.rlib host/libquote.rlib host/libsyn.rlib always-$(CONFIG_RUST_KERNEL_DOCTESTS) += doctests_kernel_generated.rs
@@ -702,6 +705,20 @@ ifdef CONFIG_PPC32 __udivdi3 __umoddi3 endif +# For objtool, annotate the redirect-intrinsics as returnable. Their call +# sites were compiled against the returnable intrinsics, but the redirected +# versions are noreturn. +quiet_cmd_intrinsics_annotate = GEN $@ + cmd_intrinsics_annotate = { \ + echo '/* Generated by rust/Makefile - do not edit */'; \ + echo '\#include <linux/annotate.h>'; \ + for s in $(redirect-intrinsics); do \ + echo "ANNOTATE_IGNORE_NORETURN(__rust$$s);"; \ + done; } > $@ + +$(obj)/intrinsics_annotate_generated.c: $(srctree)/rust/Makefile FORCE + $(call if_changed,intrinsics_annotate) + ifdef CONFIG_MODVERSIONS cmd_gendwarfksyms = $(if $(skip_gendwarfksyms),, \ $(call rust_exports,$@,"%s\n") | \
Signature · 1 line
2.55.0
Josh Poimboeuf
maintainerPATCH v2 14/27 panic: Mark abort() __noreturn
In reply to Josh Poimboeufabort() is a C compiler builtin interface, so compilers already know it doesn't return, but mark it as such explicitly for documentation purposes.
--- arch/arc/kernel/traps.c | 4 +++- arch/arm/kernel/traps.c | 2 +- include/linux/panic.h | 2 +- kernel/exit.c | 2 +- 4 files changed, 6 insertions(+), 4 deletions(-)
arch/arc/kernel/traps.c +3 -1
@@ -153,7 +153,9 @@ void do_insterror_or_kprobe(unsigned long address, struct pt_regs *regs)/* * abort() call generated by older gcc for __builtin_trap() */ -void abort (void ) +void __noreturn abort (void ) { __asm__ __volatile__("trap_s 5\n" ); + +unreachable (); }
arch/arm/kernel/traps.c +1 -1
@@ -777,7 +777,7 @@ asmlinkage void __div0(void) }EXPORT_SYMBOL (__div0); -void abort (void ) +void __noreturn abort (void ) {BUG ();
include/linux/panic.h +1 -1
@@ -33,7 +33,7 @@ extern int sysctl_panic_on_stackoverflow;extern bool crash_kexec_post_notifiers;extern void __stack_chk_fail (void ); -void abort (void ); +void abort (void )__noreturn ;/* * panic_cpu is used for synchronizing panic() and crash_kexec() execution. It
kernel/exit.c +1 -1
@@ -2027,7 +2027,7 @@ COMPAT_SYSCALL_DEFINE5(waitid,* * See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=88345#c11 */ -__weak __function_aligned void abort(void) +__weak __function_aligned void __noreturn abort(void) {BUG ();
Signature · 1 line
2.55.0
Josh Poimboeuf
maintainerPATCH v2 15/27 x86/xen: Ignore noreturn status of weak mem_map_via_hcall()
In reply to Josh PoimboeufUnlike the non-weak version, the weak version of mem_map_via_hcall() doesn't return. Tell objtool to ignore that, as the protoype (and the non-weak version) aren't noreturn so its callers can't assume it.
--- arch/x86/platform/pvh/enlighten.c | 1 + 1 file changed, 1 insertion(+)
arch/x86/platform/pvh/enlighten.c +1 -0
@@ -37,6 +37,7 @@ void __init __weak mem_map_via_hcall(struct boot_params *ptr __maybe_unused)xen_raw_printk ("Error: Could not find memory map\n" );BUG (); } +ANNOTATE_IGNORE_NORETURN (mem_map_via_hcall);static void __init init_pvh_bootparams (bool xen_guest) {
Signature · 1 line
2.55.0
Josh Poimboeuf
maintainerPATCH v2 16/27 objtool: Detect noreturns in weak functions
In reply to Josh PoimboeufThe skipping of weak functions in the noreturn detection logic is a bit weird, as a function's return/noreturn ABI shouldn't change regardless of which version gets linked.
Fix that, and update the global noreturns list as needed.
Note that abort() now needs to be added to the list: while objtool on vmlinux.o can detect that its noreturn, modules have no way of knowing.
--- tools/objtool/check.c | 3 --- tools/objtool/noreturns.h | 3 +-- 2 files changed, 1 insertion(+), 5 deletions(-)
tools/objtool/check.c +0 -3
@@ -262,9 +262,6 @@ static bool might_return(struct objtool_file *file, struct symbol *func)struct instruction *insn;struct symbol *dest; -if (is_weak_sym (func)) -return true; -func_for_each_insn (file, func, insn) {if (insn->type ==INSN_RETURN )return true;
tools/objtool/noreturns.h +1 -2
@@ -11,15 +11,14 @@ NORETURN(__kunit_abort) NORETURN(__module_put_and_kthread_exit) NORETURN(__stack_chk_fail) NORETURN(__ubsan_handle_builtin_unreachable) +NORETURN(abort) NORETURN(acpi_processor_ffh_play_dead) NORETURN(do_exit) NORETURN(kthread_complete_and_exit) NORETURN(kunit_try_catch_throw) NORETURN(mpt_halt_firmware) -NORETURN(nmi_panic_self_stop) NORETURN(panic) NORETURN(vpanic) -NORETURN(panic_smp_self_stop) NORETURN(rewind_stack_and_make_dead) NORETURN(rust_helper_BUG) NORETURN(sev_es_terminate)
Signature · 1 line
2.55.0
Josh Poimboeuf
maintainerPATCH v2 17/27 x86/entry: Make rewind_stack_and_make_dead() a real function
In reply to Josh Poimboeufrewind_stack_and_make_dead() is SYM_CODE because it switches stacks, but it's otherwise a callable function from C.
Convert it to SYM_FUNC so objtool can detect that it's noreturn and its entry can be removed from the hard-coded global noreturns list.
--- arch/x86/entry/entry_64.S | 7 +++---- tools/objtool/noreturns.h | 1 - 2 files changed, 3 insertions(+), 5 deletions(-)
arch/x86/entry/entry_64.S +3 -4
@@ -1486,9 +1486,7 @@ SYM_CODE_START(entry_SYSCALL32_ignore) SYM_CODE_END(entry_SYSCALL32_ignore) .pushsection .text, "ax" - __FUNC_ALIGN -SYM_CODE_START_NOALIGN(rewind_stack_and_make_dead) - UNWIND_HINT_FUNC +SYM_FUNC_START(rewind_stack_and_make_dead) /* Prevent any naive code from trying to unwind to our caller. */ xorl %ebp, %ebp
@@ -1497,7 +1495,8 @@ SYM_CODE_START_NOALIGN(rewind_stack_and_make_dead) UNWIND_HINT_REGS call make_task_dead -SYM_CODE_END(rewind_stack_and_make_dead) +SYM_FUNC_END(rewind_stack_and_make_dead) +STACK_FRAME_NON_STANDARD_FP(rewind_stack_and_make_dead) .popsection /*
tools/objtool/noreturns.h +0 -1
@@ -19,7 +19,6 @@ NORETURN(kunit_try_catch_throw) NORETURN(mpt_halt_firmware) NORETURN(panic) NORETURN(vpanic) -NORETURN(rewind_stack_and_make_dead) NORETURN(rust_helper_BUG) NORETURN(sev_es_terminate) NORETURN(xen_cpu_bringup_again)
Signature · 1 line
2.55.0
Josh Poimboeuf
maintainerPATCH v2 18/27 x86/xen: Make xen_cpu_bringup_again() a real function
In reply to Josh Poimboeufxen_cpu_bringup_again() is SYM_CODE because it switches stacks, but it's otherwise a callable function from C.
Convert it to STT_FUNC so objtool can detect that it's noreturn and its entry can be removed from the hard-coded global noreturns list.
--- arch/x86/xen/xen-head.S | 5 +++-- tools/objtool/noreturns.h | 1 - 2 files changed, 3 insertions(+), 3 deletions(-)
arch/x86/xen/xen-head.S +3 -2
@@ -55,12 +55,13 @@ SYM_CODE_START(asm_cpu_bringup_and_idle) call cpu_bringup_and_idle SYM_CODE_END(asm_cpu_bringup_and_idle) -SYM_CODE_START(xen_cpu_bringup_again) +SYM_FUNC_START(xen_cpu_bringup_again) UNWIND_HINT_FUNC mov %rdi, %rsp UNWIND_HINT_REGS call cpu_bringup_and_idle -SYM_CODE_END(xen_cpu_bringup_again) +SYM_FUNC_END(xen_cpu_bringup_again) +STACK_FRAME_NON_STANDARD_FP(xen_cpu_bringup_again) .popsection #endif #endif
tools/objtool/noreturns.h +0 -1
@@ -21,5 +21,4 @@ NORETURN(panic) NORETURN(vpanic) NORETURN(rust_helper_BUG) NORETURN(sev_es_terminate) -NORETURN(xen_cpu_bringup_again) NORETURN(xen_start_kernel)
Signature · 1 line
2.55.0
Josh Poimboeuf
maintainerPATCH v2 19/27 x86/xen: Make xen_start_kernel() noreturn
In reply to Josh Poimboeufstartup_xen() calls xen_start_kernel() with the assumption that it never returns, otherwise it would fall off the end of SYM_CODE_END(), triggering undefined behavior.
But startup_xen() actually can return if 'si' is NULL. That doesn't seem to be possible, so drop the check and mark xen_start_kernel() __noreturn.
A NULL 'si' would now fault on the first xen_start_info dereference, which is at least a more deterministic way to crash.
Now that the function can no longer return, objtool can derive its noreturn attribute directly and the noreturns.h entry can be removed.
--- arch/x86/xen/enlighten_pv.c | 5 +---- arch/x86/xen/xen-ops.h | 2 +- tools/objtool/noreturns.h | 1 - 3 files changed, 2 insertions(+), 6 deletions(-)
arch/x86/xen/enlighten_pv.c +1 -4
@@ -1325,15 +1325,12 @@ static void __init xen_domu_set_legacy_features(void)extern void early_xen_iret_patch (void );/* First C function to be called on Xen boot */ -asmlinkage __visible void __init xen_start_kernel(struct start_info *si) +asmlinkage __visible void __init __noreturn xen_start_kernel(struct start_info *si) {struct physdev_set_iopl set_iopl;unsigned long initrd_start =0 ;int rc; -if (!si) -return ; -clear_bss (); xen_start_info = si;
arch/x86/xen/xen-ops.h +1 -1
@@ -73,7 +73,7 @@ void xen_force_evtchn_callback(void);void xen_pv_pre_suspend (void );void xen_pv_post_suspend (int suspend_cancelled); -void xen_start_kernel (struct start_info *si); +void __noreturn xen_start_kernel (struct start_info *si);void set_pte_mfn (unsigned long vaddr,unsigned long pfn,pgprot_t flags);void xen_init_mmu_ops (void );
tools/objtool/noreturns.h +0 -1
@@ -21,4 +21,3 @@ NORETURN(panic) NORETURN(vpanic) NORETURN(rust_helper_BUG) NORETURN(sev_es_terminate) -NORETURN(xen_start_kernel)
Signature · 1 line
2.55.0
Josh Poimboeuf
maintainerPATCH v2 20/27 x86/boot: Rework how pi startup symbols get exposed to vmlinux
In reply to Josh Poimboeufobjcopy prefixes every symbol in arch/x86/boot/startup/ with __pi_, which confines the startup code so that it can only reach other startup code or symbols deliberately exposed to it with SYM_PIC_ALIAS().
Nine of its functions are called by the core kernel at runtime. They are made accessible to the kernel by exports.h, which is included by vmlinux.lds.S to create aliases for each one.
That's problematic for objtool running on vmlinux.o, which is built before the linker script runs, so the call sites to the startup symbols are undefined, so objtool can't see their actual destinations functions.
Instead of making aliases at link time, rename the needed symbols using a second objcopy pass so they're visible in vmlinux.o. That also means objtool can now detect that sev_es_terminate() is noreturn, so it can be removed from noreturns.h.
--- arch/x86/boot/startup/Makefile | 14 ++++++++++---- arch/x86/boot/startup/exports.h | 14 -------------- arch/x86/boot/startup/unprefix.syms | 13 +++++++++++++ arch/x86/kernel/vmlinux.lds.S | 2 -- tools/objtool/noreturns.h | 1 - 5 files changed, 23 insertions(+), 21 deletions(-) delete mode 100644 arch/x86/boot/startup/exports.h create mode 100644 arch/x86/boot/startup/unprefix.syms
arch/x86/boot/startup/Makefile +10 -4
@@ -41,12 +41,18 @@ $(pi-objs): objtool-args = $(if $(CONFIG_OBJTOOL_DEFERRED),--dry-run,$(objtool-a # # Confine the startup code by prefixing all symbols with __pi_ (for position # independent). This ensures that startup code can only call other startup -# code, or code that has explicitly been made accessible to it via a symbol -# alias. +# code. # +# The few pi symbols which are allowed to be called by the core kernel are then +# un-prefixed back to their original name using a second objcopy. +# +quiet_cmd_pi_objcopy = $(quiet_cmd_objcopy) + cmd_pi_objcopy = $(cmd_objcopy) && \ + $(OBJCOPY) --redefine-syms=$(src)/unprefix.syms $@ + $(obj)/%.pi.o: OBJCOPYFLAGS := --prefix-symbols=__pi_ -$(obj)/%.pi.o: $(obj)/%.o FORCE - $(call if_changed,objcopy) +$(obj)/%.pi.o: $(obj)/%.o $(src)/unprefix.syms FORCE + $(call if_changed,pi_objcopy) targets += $(obj-y) obj-y := $(patsubst %.o,%.pi.o,$(obj-y))
arch/x86/boot/startup/exports.h deleted +0 -14
@@ -1,14 +0,0 @@ - -/* -* The symbols below are functions that are implemented by the startup code, -* but called at runtime by the SEV code residing in the core kernel. -*/ -PROVIDE (early_set_pages_state = __pi_early_set_pages_state); -PROVIDE (early_snp_set_memory_private = __pi_early_snp_set_memory_private); -PROVIDE (early_snp_set_memory_shared = __pi_early_snp_set_memory_shared); -PROVIDE (get_hv_features = __pi_get_hv_features); -PROVIDE (sev_es_terminate = __pi_sev_es_terminate); -PROVIDE (snp_cpuid = __pi_snp_cpuid); -PROVIDE (snp_cpuid_get_table = __pi_snp_cpuid_get_table); -PROVIDE (svsm_issue_call = __pi_svsm_issue_call); -PROVIDE (svsm_process_result_codes = __pi_svsm_process_result_codes);
arch/x86/boot/startup/unprefix.syms new file +13 -0
@@ -0,0 +1,13 @@ +# SPDX-License-Identifier: GPL-2.0 +# The symbols below are functions that are implemented by the startup code, +# but called at runtime by the SEV code residing in the core kernel. Undo the +# __pi_ prefixing so the core kernel can call them. +__pi_early_set_pages_state early_set_pages_state +__pi_early_snp_set_memory_private early_snp_set_memory_private +__pi_early_snp_set_memory_shared early_snp_set_memory_shared +__pi_get_hv_features get_hv_features +__pi_sev_es_terminate sev_es_terminate +__pi_snp_cpuid snp_cpuid +__pi_snp_cpuid_get_table snp_cpuid_get_table +__pi_svsm_issue_call svsm_issue_call +__pi_svsm_process_result_codes svsm_process_result_codes
arch/x86/kernel/vmlinux.lds.S +0 -2
@@ -527,5 +527,3 @@ xen_elfnote_entry_value = xen_elfnote_phys32_entry_value = ABSOLUTE(xen_elfnote_phys32_entry) + ABSOLUTE(pvh_start_xen - LOAD_OFFSET); #endif - -#include "../boot/startup/exports.h"
tools/objtool/noreturns.h +0 -1
@@ -20,4 +20,3 @@ NORETURN(mpt_halt_firmware) NORETURN(panic) NORETURN(vpanic) NORETURN(rust_helper_BUG) -NORETURN(sev_es_terminate)
Signature · 1 line
2.55.0
Josh Poimboeuf
maintainerPATCH v2 21/27 objtool: Fix noreturn detection for non-sibling jumps to SYM_CODE
In reply to Josh PoimboeufObjtool doesn't consider a jump to be a sibling call if the destination isn't a function, so srso_alias_untrain_ret()'s jump to srso_alias_return_thunk() is ignored by objtool and the former is falsely classified as noreturn.
Change the noreturn detection to consider such cases as effectively sibling calls, except for one known exception: jumping to .altinstr_aux.
Currently the misclassification is harmless: dead ends aren't marked when calling a noreturn from an alternative replacement (CALL_UNTRAIN_RET), they're ignored by rethunk validation, and ORC generation relies on unwind hints in the SRSO code regardless.
Fix it in the name of robustness and in preparation for a subsequent patch which generates a list of exported noreturns.
--- tools/objtool/check.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-)
tools/objtool/check.c +15 -1
@@ -266,8 +266,22 @@ static bool might_return(struct objtool_file *file, struct symbol *func)if (insn->type ==INSN_RETURN )return true; -if (!is_sibling_call (insn)) +if (!is_sibling_call (insn)) { +/* +* Assume a jump into a non-function eventually returns +* to the original caller one way or another, e.g., the +* jump in srso_alias_untrain_ret(). +* +* .altinstr_aux is an exception, cpu_feature_enabled() +* jumps there and then right back. +*/ +if (is_static_jump (insn) && insn->jump_dest && + !insn_func (insn->jump_dest) && +strcmp (insn->jump_dest->sec->name,".altinstr_aux" )) +return true; +continue ; + } dest =insn_call_dest (insn);if (!dest || !is_noreturn (dest))
Signature · 1 line
2.55.0
Josh Poimboeuf
maintainerPATCH v2 22/27 objtool: Add options to write/read exported noreturns to/from a file
In reply to Josh PoimboeufAdd --noreturns-write which can be used to write vmlinux's exported noreturns to a file, and --noreturn-read which can be used to read them when running objtool on a module.
--- tools/objtool/builtin-check.c | 12 ++ tools/objtool/check.c | 148 +++++++++++++++++++++++- tools/objtool/include/objtool/builtin.h | 2 + tools/objtool/include/objtool/elf.h | 1 + 4 files changed, 162 insertions(+), 1 deletion(-)
tools/objtool/builtin-check.c +12 -0
@@ -101,6 +101,8 @@ static const struct option check_options[] = {OPT_BOOLEAN (0 ,"module" , &opts.module,"object is part of a kernel module" ),OPT_BOOLEAN (0 ,"mnop" , &opts.mnop,"nop out mcount call sites" ),OPT_BOOLEAN (0 ,"no-unreachable" , &opts.no_unreachable,"skip 'unreachable instruction' warnings" ), +OPT_STRING (0 ,"noreturns-read" , &opts.noreturns_read,"file" ,"read exported noreturns from a file" ), +OPT_STRING (0 ,"noreturns-write" , &opts.noreturns_write,"file" ,"write exported noreturns to a file" ),OPT_STRING ('o' ,"output" , &opts.output,"file" ,"output file name" ),OPT_BOOLEAN (0 ,"sec-address" , &opts.sec_address,"print section addresses in warnings" ), OPT_BOOLEAN(0, "stats", &opts.stats, "print statistics"),
@@ -180,6 +182,16 @@ static bool opts_valid(void)return false; } +if (opts.noreturns_write && !opts.link) { +ERROR ("--noreturns-write requires --link" ); +return false; + } + +if (opts.noreturns_write && !opts.stackval && !opts.orc && !opts.uaccess) { +ERROR ("--noreturns-write requires --stackval, --orc, or --uaccess" ); +return false; + } + if (opts.disas || opts.hack_jump_label || opts.hack_noinstr ||
tools/objtool/check.c +147 -1
179 lines not on this page · raw message
tools/objtool/include/objtool/builtin.h +2 -0
@@ -41,6 +41,8 @@ struct opts {bool mnop;bool module;bool no_unreachable; +const char *noreturns_read; +const char *noreturns_write;const char *output;bool sec_address;bool stats;
tools/objtool/include/objtool/elf.h +1 -0
@@ -100,6 +100,7 @@ struct symbol { u8 fake : 1; u8 _noreturn : 1; u8 ignore_noreturn : 1; + u8 exported : 1;struct list_head pv_target;struct reloc *relocs;struct section *group_sec;
Signature · 1 line
2.55.0
Josh Poimboeuf
maintainerPATCH v2 23/27 kbuild: Do the per-module objtool pass right before linking
In reply to Josh PoimboeufWith CONFIG_OBJTOOL_CONTROL_FLOW, the objtool pass on vmlinux.o is going to be generating a list of its exported noreturns which will then be read by the modules' objtool pass, so there will need to be a build dependency between them.
In preparation for that, run objtool right before the module link so its no longer done on the descend in the deferred case.
--- scripts/Makefile.build | 31 +++++++++++++++---------------- scripts/Makefile.modfinal | 20 +++++++++++++++----- scripts/livepatch/klp-build | 4 ++-- 3 files changed, 32 insertions(+), 23 deletions(-)
scripts/Makefile.build +15 -16
@@ -271,15 +271,21 @@ cmd_record_mcount = $(if $(findstring $(strip $(CC_FLAGS_FTRACE)),$(_c_flags)), $(sub_cmd_record_mcount)) endif # CONFIG_FTRACE_MCOUNT_USE_RECORDMCOUNT -# 'OBJECT_FILES_NON_STANDARD := y': skip objtool checking for a directory -# 'OBJECT_FILES_NON_STANDARD_foo.o := 'y': skip objtool checking for a file -# 'OBJECT_FILES_NON_STANDARD_foo.o := 'n': override directory skip for a file - +ifdef CONFIG_OBJTOOL +# +# NOTE: these variables are deprecated, and have no effect when +# CONFIG_OBJTOOL_DEFERRED is enabled. Objtool-allergic code can instead be +# marked with STACK_FRAME_NON_STANDARD() or ANNOTATE_*(). +# +# 'OBJECT_FILES_NON_STANDARD := y': skip objtool checking for a directory +# 'OBJECT_FILES_NON_STANDARD_foo.o := 'y': skip objtool checking for a file +# 'OBJECT_FILES_NON_STANDARD_foo.o := 'n': override directory skip for a file +# is-standard-object = $(if $(filter-out y%, $(OBJECT_FILES_NON_STANDARD_$(target-stem).o)$(OBJECT_FILES_NON_STANDARD)n),$(is-kernel-object)) -ifdef CONFIG_OBJTOOL -$(obj)/%.o: private objtool-enabled = $(if $(is-standard-object),$(if $(CONFIG_OBJTOOL_DEFERRED),$(is-single-obj-m),y)) -endif +# Enable per-TU objtool for !CONFIG_OBJTOOL_DEFERRED +$(obj)/%.o: private objtool-enabled = $(if $(CONFIG_OBJTOOL_DEFERRED),,$(if $(is-standard-object),y)) +endif # CONFIG_OBJTOOL ifneq ($(findstring 1, $(KBUILD_EXTRA_WARN)),) cmd_warn_shared_object = $(if $(word 2, $(modname-multi)),$(warning $(kbuild-file): $*.o is added to multiple modules: $(modname-multi)))
@@ -497,17 +503,10 @@ $(obj)/lib.a: $(lib-y) FORCE $(call if_changed,ar) quiet_cmd_ld_multi_m = LD [M] $@ - cmd_ld_multi_m = $(LD) $(ld_flags) -r -o $@ @$< $(cmd_objtool) + cmd_ld_multi_m = $(LD) $(ld_flags) -r -o $@ @$< -define rule_ld_multi_m - $(call cmd_and_savecmd,ld_multi_m) - $(call cmd,gen_objtooldep) -endef - -$(multi-obj-m): private objtool-enabled := $(CONFIG_OBJTOOL_DEFERRED) -$(multi-obj-m): private part-of-module := y $(multi-obj-m): %.o: %.mod FORCE - $(call if_changed_rule,ld_multi_m) + $(call if_changed,ld_multi_m) $(call multi_depend, $(multi-obj-m), .o, -objs -y -m) # Add intermediate targets:
scripts/Makefile.modfinal +15 -5
@@ -32,11 +32,18 @@ ifneq ($(WARN_ON_UNUSED_TRACEPOINTS),) cmd_check_tracepoint = $(objtree)/scripts/tracepoint-update --module $<; endif +$(modules:%.o=%.ko): private objtool-enabled = $(CONFIG_OBJTOOL_DEFERRED) + +mod-objtool = $(if $(objtool-enabled),$(objtool) $(objtool-args) -o [email protected] $< ;) +mod-obj = $(if $(objtool-enabled),[email protected],$<) + quiet_cmd_ld_ko_o = LD [M] $@ cmd_ld_ko_o = \ + $(mod-objtool) \ $(LD) -r $(KBUILD_LDFLAGS) \ $(KBUILD_LDFLAGS_MODULE) $(LDFLAGS_MODULE) \ - -T $(objtree)/scripts/module.lds -o $@ $(filter %.o, $^) + -T $(objtree)/scripts/module.lds -o $@ \ + $(mod-obj) $(filter-out $<, $(filter %.o, $^)) quiet_cmd_btf_ko = BTF [M] $@ cmd_btf_ko = \
@@ -46,12 +53,15 @@ quiet_cmd_btf_ko = BTF [M] $@ $(CONFIG_SHELL) $(srctree)/scripts/gen-btf.sh --btf_base $(objtree)/vmlinux $@; \ fi; +define rule_ld_ko_o + +$(call cmd_and_savecmd,ld_ko_o) + +$(call cmd,gen_objtooldep) + +$(if $(CONFIG_DEBUG_INFO_BTF_MODULES),$(if $(newer-prereqs),$(call cmd,btf_ko))) +endef + # Re-generate module BTFs if either module's .ko or vmlinux changed %.ko: %.o %.mod.o .module-common.o $(objtree)/scripts/module.lds $(and $(CONFIG_DEBUG_INFO_BTF_MODULES),$(KBUILD_BUILTIN),$(objtree)/vmlinux) FORCE - +$(call if_changed,ld_ko_o) -ifdef CONFIG_DEBUG_INFO_BTF_MODULES - +$(if $(newer-prereqs),$(call cmd,btf_ko)) -endif + $(call if_changed_rule,ld_ko_o) +$(call cmd,check_tracepoint) targets += $(modules:%.o=%.ko) $(modules:%.o=%.mod.o) .module-common.o
scripts/livepatch/klp-build +2 -2
@@ -600,7 +600,7 @@ copy_orig_objects() { xtrace_save "copying original objects" for _file in "${files[@]}"; do local rel_file="${_file/.ko/.o}" - local file="$PWD/$rel_file" + local file="$PWD/${_file/%.ko/.ko.o}" local orig_file="$ORIG_DIR/$rel_file" local orig_dir="$(dirname "$orig_file")"
@@ -636,7 +636,7 @@ copy_patched_objects() { xtrace_save "copying changed objects" for _file in "${files[@]}"; do local rel_file="${_file/.ko/.o}" - local file="$PWD/$rel_file" + local file="$PWD/${_file/%.ko/.ko.o}" local orig_file="$ORIG_DIR/$rel_file" local patched_file="$PATCHED_DIR/$rel_file" local patched_dir="$(dirname "$patched_file")"
Signature · 1 line
2.55.0
Josh Poimboeuf
maintainerPATCH v2 24/27 kbuild: Generate the noreturn list and validate modules against it
In reply to Josh PoimboeufMost of the remaining hard-coded noreturn functions are exported symbols which live in vmlinux.o. The objtool vmlinux.o pass can detect them automatically, but modules can't.
Instead of hard-coding them in noreturns.h, dynamically generate the list in a new scripts/noreturns.builtin file, which objtool on modules can then read. That allows most of the remaining hard-coded noreturn entries to go away, including the Rust ones. Note this means that modules need to be linked after vmlinux.o has been built and analyzed by objtool.
Objtool running on OOT modules will also need to read the generated file. Its location in scripts/ should ensure it gets installed with kernel build files automatically.
--- Makefile | 8 +++++ scripts/.gitignore | 1 + scripts/Makefile.modfinal | 15 ++++++-- scripts/Makefile.vmlinux_o | 2 ++ tools/objtool/Documentation/objtool.txt | 8 ++--- tools/objtool/check.c | 46 ------------------------- tools/objtool/noreturns.h | 17 ++------- 7 files changed, 29 insertions(+), 68 deletions(-)
Makefile +8 -0
@@ -1750,6 +1750,13 @@ KBUILD_BUILTIN := y modules: vmlinux endif +# Objtool for modules (run before module link) needs to come after objtool for +# vmlinux.o so it can read the generated exported noreturns file. +ifdef CONFIG_OBJTOOL_CONTROL_FLOW +KBUILD_BUILTIN := y +modules: vmlinux.o +endif + modules: modules_prepare # Target to prepare building external modules
@@ -1780,6 +1787,7 @@ MRPROPER_FILES += include/config include/generated \ debian snap tar-install PKGBUILD pacman \ .config .config.old .version \ Module.symvers \ + scripts/noreturns.builtin \ certs/signing_key.pem \ certs/x509.genkey \ vmlinux-gdb.py \
scripts/.gitignore +1 -0
@@ -5,6 +5,7 @@ /insert-sys-cert /kallsyms /module.lds +/noreturns.builtin /recordmcount /rustdoc_test_builder /rustdoc_test_gen
scripts/Makefile.modfinal +13 -2
@@ -32,6 +32,15 @@ ifneq ($(WARN_ON_UNUSED_TRACEPOINTS),) cmd_check_tracepoint = $(objtree)/scripts/tracepoint-update --module $<; endif +ifdef CONFIG_OBJTOOL_CONTROL_FLOW +noreturns-builtin := $(wildcard $(objtree)/scripts/noreturns.builtin) +ifeq ($(noreturns-builtin),) +$(warning scripts/noreturns.builtin is missing) +else +objtool-args += --noreturns-read=$(noreturns-builtin) +endif +endif + $(modules:%.o=%.ko): private objtool-enabled = $(CONFIG_OBJTOOL_DEFERRED) mod-objtool = $(if $(objtool-enabled),$(objtool) $(objtool-args) -o [email protected] $< ;)
@@ -59,8 +68,10 @@ define rule_ld_ko_o +$(if $(CONFIG_DEBUG_INFO_BTF_MODULES),$(if $(newer-prereqs),$(call cmd,btf_ko))) endef -# Re-generate module BTFs if either module's .ko or vmlinux changed -%.ko: %.o %.mod.o .module-common.o $(objtree)/scripts/module.lds $(and $(CONFIG_DEBUG_INFO_BTF_MODULES),$(KBUILD_BUILTIN),$(objtree)/vmlinux) FORCE +# Re-generate module BTFs if either module's .ko or vmlinux changed. +# Re-run module objtool if the generated noreturn list changed. +%.ko: %.o %.mod.o .module-common.o $(objtree)/scripts/module.lds $(noreturns-builtin) \ + $(and $(CONFIG_DEBUG_INFO_BTF_MODULES),$(KBUILD_BUILTIN),$(objtree)/vmlinux) FORCE $(call if_changed_rule,ld_ko_o) +$(call cmd,check_tracepoint)
scripts/Makefile.vmlinux_o +2 -0
@@ -37,6 +37,8 @@ objtool-enabled := $(CONFIG_OBJTOOL_DEFERRED) vmlinux-objtool-args-y += $(objtool-args-y) +vmlinux-objtool-args-$(CONFIG_OBJTOOL_CONTROL_FLOW) += --noreturns-write=scripts/noreturns.builtin + vmlinux-objtool-args-$(CONFIG_NOINSTR_VALIDATION) += --noinstr \ $(if $(or $(CONFIG_MITIGATION_UNRET_ENTRY),$(CONFIG_MITIGATION_SRSO)), --unret)
tools/objtool/Documentation/objtool.txt +3 -5
@@ -334,8 +334,7 @@ the objtool maintainers. The call from foo() to bar() doesn't return, but bar() is incorrectly annotated. A noreturn function must be marked __noreturn in both its - declaration and its definition, and must have a NORETURN() annotation - in tools/objtool/noreturns.h. + declaration and its definition. 4. file.o: warning: objtool: func(): can't find starting instruction
@@ -393,9 +392,8 @@ the objtool maintainers. reasons for this: a) funcA()'s last instruction is a call to a "noreturn" function like - panic(). In this case the noreturn function needs to be added to - objtool's hard-coded global_noreturns array. Feel free to bug the - objtool maintainer, or you can submit a patch. + panic(), and objtool doesn't know that function never returns. See + warning 3 above. b) funcA() uses the unreachable() annotation in a section of code that is actually reachable.
tools/objtool/check.c +0 -46
58 lines not on this page · raw message
tools/objtool/noreturns.h +2 -15
@@ -1,22 +1,9 @@/* SPDX-License-Identifier: GPL-2.0 */ /* -* This is a (sorted!) list of all known __noreturn functions in the kernel. -* It's needed for objtool to properly reverse-engineer the control flow graph. -* -* Yes, this is unfortunate. A better solution is in the works. +* This is a list of noreturn functions which are exported *by modules*. +* No other noreturns need to be listed here. */ -NORETURN(__fortify_panic) NORETURN(__kunit_abort) -NORETURN(__module_put_and_kthread_exit) -NORETURN(__stack_chk_fail) -NORETURN(__ubsan_handle_builtin_unreachable) -NORETURN(abort) -NORETURN(acpi_processor_ffh_play_dead) -NORETURN(do_exit) -NORETURN(kthread_complete_and_exit) NORETURN(kunit_try_catch_throw) NORETURN(mpt_halt_firmware) -NORETURN(panic) -NORETURN(vpanic) -NORETURN(rust_helper_BUG)
Signature · 1 line
2.55.0
Josh Poimboeuf
maintainerPATCH v2 25/27 objtool: Add ANNOTATE_EXPORTED_NORETURN()
In reply to Josh PoimboeufAdd ANNOTATE_EXPORTED_NORETURN() for the rare case where a module exports a noreturn function.
--- include/linux/annotate.h | 17 +++++++++++++++++ tools/objtool/check.c | 27 +++++++++++++++++++++++++++ tools/objtool/klp-diff.c | 32 ++++++++++++++++++++++++++++++++ 3 files changed, 76 insertions(+)
include/linux/annotate.h +17 -0
@@ -25,6 +25,15 @@"912: " \ __stringify(__ASM_ANNOTATE(.discard.annotate_data, 912b, type)) +/* +* Annotate a symbol by name rather than by relocation, so it can optionally be +* used in a header file. + */ +#define ASM_ANNOTATE_NAME (section, sym) \ + ".pushsection " section ", \"MS\", @progbits, 1\n\t" \ + ".asciz \"" __stringify(sym) "\"\n\t" \ + ".popsection" +#else /* __ASSEMBLY__ */ .macro ANNOTATE type
@@ -44,6 +53,7 @@#define ASM_ANNOTATE_LABEL (label, type) ""#define ASM_ANNOTATE (type)#define ASM_ANNOTATE_DATA (type) +#define ASM_ANNOTATE_NAME (section, sym) ""#else /* __ASSEMBLY__ */ .macro ANNOTATE type .endm
@@ -112,6 +122,13 @@*/ #define ANNOTATE_IGNORE_NORETURN (sym) asm(ASM_ANNOTATE_LABEL(sym, ANNOTYPE_IGNORE_NORETURN)) +/* +* Tell objtool running on a module that a function exported by another module +* is __noreturn. Objtool has no way of communicating that between modules due +* to the parallel nature of module linking in kbuild. +*/ +#define ANNOTATE_EXPORTED_NORETURN (sym) asm(ASM_ANNOTATE_NAME(".discard.annotate_noreturn", sym)) +/* * Annotate a special section entry. This emables livepatch module generation * to find and extract individual special section entries as needed.
tools/objtool/check.c +27 -0
@@ -409,6 +409,31 @@ static int read_noreturns(struct objtool_file *file)return 0 ; } +static void read_annotate_noreturn (struct objtool_file *file) +{ +struct section *sec; +struct symbol *func; +unsigned long off; +const char *name; + + sec =find_section_by_name (file->elf,".discard.annotate_noreturn" ); +if (!sec || !sec->data) +return ; + +for (off =0 ; off <sec_size (sec); off +=strlen (name) +1 ) { + name = sec->data->d_buf + off; +if (!*name) +continue ; + + func =find_global_symbol_by_name (file->elf, name); +if (!func) +continue ; + +if (is_undef_sym (func)) + func->_noreturn =1 ; + } +} +static void init_cfi_state (struct cfi_state *cfi) {int i;
@@ -4983,6 +5008,8 @@ int check(struct objtool_file *file) goto out; } +read_annotate_noreturn (file); + ret =decode_file (file);if (ret) goto out;
tools/objtool/klp-diff.c +32 -0
@@ -360,6 +360,7 @@ static bool is_special_section(struct section *sec)static const char *const non_special_discards[] = {".discard.addressable" , +".discard.annotate_noreturn" ,".discard.sym_checksum" , };
@@ -2216,6 +2217,34 @@ static int copy_import_ns(struct elfs *e)return 0 ; } +/* +* ANNOTATE_EXPORTED_NORETURN() annotations reference their functions by name +* rather than by relocation, so individual entries can't be extracted. Copy +* the (tiny) section as-is. +*/ +static int copy_annotate_noreturn (struct elfs *e) +{ +struct section *patched_sec, *out_sec; + + patched_sec =find_section_by_name (e->patched,".discard.annotate_noreturn" ); +if (!patched_sec || !patched_sec->data || !sec_size (patched_sec)) +return 0 ; + + out_sec =elf_create_section (e->out, patched_sec->name,0 , + patched_sec->sh.sh_entsize, + patched_sec->sh.sh_type, + patched_sec->sh.sh_addralign, + patched_sec->sh.sh_flags); +if (!out_sec) +return -1 ; + +if (!elf_add_data (e->out, out_sec, patched_sec->data->d_buf, +sec_size (patched_sec))) +return -1 ; + +return 0 ; +} +int cmd_klp_diff (int argc,const char **argv) {struct elfs e = {0 };
@@ -2287,6 +2316,9 @@ int cmd_klp_diff(int argc, const char **argv)if (copy_import_ns (&e))return -1 ; +if (copy_annotate_noreturn (&e)) +return -1 ; +if (elf_write (e.out))return -1 ;
Signature · 1 line
2.55.0
Josh Poimboeuf
maintainerPATCH v2 26/27 objtool: Annotate all module-exported noreturns and remove noreturns.h
In reply to Josh PoimboeufThe vmlinux.o objtool pass detects its exported noreturns and writes them to scripts/noreturns.builtins to avoid needing to keep them hard-coded in noreturns.h.
However, due to the parallel nature of module linking, module-exported noreturns are detected too late to be included in the generated file, so they still need noreturns.h entries.
Instead of hard-coding them in noreturns.h, annotate the functions next to their declaration sites with ANNOTATE_EXPORTED_NORETURN(), which is a more robust annotation, and allows us to finally be rid of noreturns.h.
--- drivers/message/fusion/mptbase.h | 2 ++ include/kunit/test.h | 1 + include/kunit/try-catch.h | 2 ++ include/linux/rtc/ds1685.h | 2 ++ tools/objtool/Documentation/objtool.txt | 2 +- tools/objtool/check.c | 23 +---------------------- tools/objtool/noreturns.h | 9 --------- 7 files changed, 9 insertions(+), 32 deletions(-) delete mode 100644 tools/objtool/noreturns.h
drivers/message/fusion/mptbase.h +2 -0
@@ -49,6 +49,7 @@#define MPTBASE_H_INCLUDED /*{-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +#include <linux/annotate.h> #include <linux/kernel.h> #include <linux/pci.h> #include <linux/mutex.h>
@@ -942,6 +943,7 @@ extern int mpt_raid_phys_disk_get_num_paths(MPT_ADAPTER *ioc,extern int mpt_set_taskmgmt_in_progress_flag (MPT_ADAPTER *ioc);extern void mpt_clear_taskmgmt_in_progress_flag (MPT_ADAPTER *ioc);extern void __noreturn mpt_halt_firmware (MPT_ADAPTER *ioc); +ANNOTATE_EXPORTED_NORETURN (mpt_halt_firmware);/*
include/kunit/test.h +1 -0
@@ -723,6 +723,7 @@ void __printf(2, 3) kunit_log_append(struct string_stream *log, const char *fmt,#define KUNIT_SUCCEED (test) _KUNIT_SAVE_LOC(test)void __noreturn __kunit_abort (struct kunit *test); +ANNOTATE_EXPORTED_NORETURN (__kunit_abort); void __printf(6, 7) __kunit_do_failed_assertion(struct kunit *test, const struct kunit_loc *loc,
include/kunit/try-catch.h +2 -0
@@ -10,6 +10,7 @@#ifndef _KUNIT_TRY_CATCH_H#define _KUNIT_TRY_CATCH_H +#include <linux/annotate.h> #include <linux/types.h> typedef void (*kunit_try_catch_func_t )(void *);
@@ -54,6 +55,7 @@ struct kunit_try_catch {void kunit_try_catch_run (struct kunit_try_catch *try_catch,void *context);void __noreturn kunit_try_catch_throw (struct kunit_try_catch *try_catch); +ANNOTATE_EXPORTED_NORETURN (kunit_try_catch_throw);static inline int kunit_try_catch_get_result (struct kunit_try_catch *try_catch) {
include/linux/rtc/ds1685.h +2 -0
@@ -21,6 +21,7 @@#ifndef _LINUX_RTC_DS1685_H_#define _LINUX_RTC_DS1685_H_ +#include <linux/annotate.h> #include <linux/rtc.h> #include <linux/platform_device.h> #include <linux/workqueue.h>
@@ -362,5 +363,6 @@ struct ds1685_rtc_platform_data {*/ extern void __noreturn ds1685_rtc_poweroff (struct platform_device *pdev); +ANNOTATE_EXPORTED_NORETURN (ds1685_rtc_poweroff);#endif /* _LINUX_RTC_DS1685_H_ */
tools/objtool/Documentation/objtool.txt +1 -1
@@ -330,7 +330,7 @@ the objtool maintainers. traces and want objtool to ignore it, see "Adding exceptions" below. -3. file.o: warning: objtool: foo+0x48c: bar() missing __noreturn in .c/.h or NORETURN() in noreturns.h +3. file.o: warning: objtool: foo+0x48c: bar() is missing __noreturn in .c/.h The call from foo() to bar() doesn't return, but bar() is incorrectly annotated. A noreturn function must be marked __noreturn in both its
tools/objtool/check.c +1 -22
@@ -176,24 +176,6 @@ static bool is_sibling_call(struct instruction *insn)return (is_static_jump (insn) &&insn_call_dest (insn)); } -static bool is_listed_noreturn (struct symbol *func) -{ -#define NORETURN (func) __stringify(func), -static const char *const global_noreturns[] = { -#include "noreturns.h" - }; -#undef NORETURN - -if (is_local_sym (func)) -return false; - -for (int i =0 ; i <ARRAY_SIZE (global_noreturns); i++) -if (!strcmp (func->name, global_noreturns[i])) -return true; - -return false; -} -/* * Use this rather than reading sym->_noreturn directly: the noreturn status * lives on the primary alias, and ANNOTATE_IGNORE_NORETURN() overrides it.
@@ -205,9 +187,6 @@ static bool is_noreturn(struct symbol *func)if (func->ignore_noreturn)return false; -if (is_listed_noreturn (func)) -return true; -return func->_noreturn; }
@@ -4890,7 +4869,7 @@ static int validate_reachable_instructions(struct objtool_file *file)if (prev_insn && prev_insn->dead_end) { call_dest =insn_call_dest (prev_insn);if (call_dest) { -WARN_INSN (insn,"%s() missing __noreturn in .c/.h or NORETURN() in noreturns.h" , +WARN_INSN (insn,"%s() is missing __noreturn in .c/.h" , call_dest->name); warnings++;continue ;
tools/objtool/noreturns.h deleted +0 -9
@@ -1,9 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ - -/* -* This is a list of noreturn functions which are exported *by modules*. -* No other noreturns need to be listed here. -*/ -NORETURN(__kunit_abort) -NORETURN(kunit_try_catch_throw) -NORETURN(mpt_halt_firmware)
Signature · 1 line
2.55.0
Josh Poimboeuf
maintainerPATCH v2 27/27 objtool: Warn about missing/stale ANNOTATE_EXPORTED_NORETURN() usage
In reply to Josh PoimboeufAdd some warnings to keep ANNOTATE_EXPORTED_NORETURN() annotations correct.
--- tools/objtool/Documentation/objtool.txt | 22 ++++++++- tools/objtool/check.c | 59 ++++++++++++++++++++----- tools/objtool/include/objtool/elf.h | 1 + 3 files changed, 71 insertions(+), 11 deletions(-)
tools/objtool/Documentation/objtool.txt +21 -1
@@ -393,7 +393,7 @@ the objtool maintainers. a) funcA()'s last instruction is a call to a "noreturn" function like panic(), and objtool doesn't know that function never returns. See - warning 3 above. + warnings 3 and 13. b) funcA() uses the unreachable() annotation in a section of code that is actually reachable.
@@ -455,6 +455,26 @@ the objtool maintainers. macros to create them. +13. file.o: warning: objtool: func() is exported and noreturn, its declaration needs __noreturn and ANNOTATE_EXPORTED_NORETURN() + + func() is exported and never returns, but is defined in a module, so + its noreturn status needs to be manually annotated. Put an + ANNOTATE_EXPORTED_NORETURN() next to its declaration. + + Mark the declaration __noreturn while you're there, if it isn't + already. The two go together: __noreturn stops the compiler from + emitting code after the call, and the annotation tells objtool the + call doesn't return. + + +14. file.o: warning: objtool: func() has ANNOTATE_EXPORTED_NORETURN() but returns + + func() is annotated as never returning, but it does. Usually this + means the annotation outlived the fact: the function grew a return path + and the ANNOTATE_EXPORTED_NORETURN() next to its declaration was left + behind. Remove it, along with the __noreturn. + + If the error doesn't seem to make sense, it could be a bug in objtool. Feel free to ask objtool maintainers for help.
tools/objtool/check.c +49 -10
87 lines not on this page · raw message
tools/objtool/include/objtool/elf.h +1 -0
@@ -100,6 +100,7 @@ struct symbol { u8 fake : 1; u8 _noreturn : 1; u8 ignore_noreturn : 1; + u8 annotate_noreturn : 1; u8 exported : 1;struct list_head pv_target;struct reloc *relocs;
Signature · 1 line
2.55.0