[PATCH 24/27] kbuild: Generate the noreturn list and validate modules against it

Josh Poimboeuf <[email protected]>
Newsgroups gmane.linux.kernel.rust,gmane.linux.kernel,gmane.linux.kbuild.devel
Message-ID <48390d44cc3796932e1eef180b08259a33121f74.1787890035.git.jpoimboe@kernel.org>
Most 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.

Signed-off-by: Josh Poimboeuf <[email protected]>
---
 Makefile                                |  8 +++++
 scripts/.gitignore                      |  1 +
 scripts/Makefile.modfinal               | 15 +++++++--
 scripts/Makefile.vmlinux_o              |  2 ++
 tools/objtool/Documentation/objtool.txt |  8 ++---
 tools/objtool/check.c                   | 44 -------------------------
 tools/objtool/noreturns.h               | 17 ++--------
 7 files changed, 29 insertions(+), 66 deletions(-)

diff --git a/Makefile b/Makefile
index 902f3f3d54b71..1c443309dbc4d 100644
--- a/Makefile
+++ b/Makefile
@@ -1708,6 +1708,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
@@ -1738,6 +1745,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 \
diff --git a/scripts/.gitignore b/scripts/.gitignore
index 4215c2208f7e4..15d99dfd7c936 100644
--- a/scripts/.gitignore
+++ b/scripts/.gitignore
@@ -5,6 +5,7 @@
 /insert-sys-cert
 /kallsyms
 /module.lds
+/noreturns.builtin
 /recordmcount
 /rustdoc_test_builder
 /rustdoc_test_gen
diff --git a/scripts/Makefile.modfinal b/scripts/Makefile.modfinal
index 56b4227cc6863..23b07ad7eb9bd 100644
--- a/scripts/Makefile.modfinal
+++ b/scripts/Makefile.modfinal
@@ -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)
 
diff --git a/scripts/Makefile.vmlinux_o b/scripts/Makefile.vmlinux_o
index df1e3584883e6..9c60a4918a756 100644
--- a/scripts/Makefile.vmlinux_o
+++ b/scripts/Makefile.vmlinux_o
@@ -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)
 
diff --git a/tools/objtool/Documentation/objtool.txt b/tools/objtool/Documentation/objtool.txt
index 9e97fc25b2d8a..c7ebfb9a8ca6b 100644
--- a/tools/objtool/Documentation/objtool.txt
+++ b/tools/objtool/Documentation/objtool.txt
@@ -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.
diff --git a/tools/objtool/check.c b/tools/objtool/check.c
index 086e8f9eca867..583d86220e040 100644
--- a/tools/objtool/check.c
+++ b/tools/objtool/check.c
@@ -176,47 +176,6 @@ static bool is_sibling_call(struct instruction *insn)
 	return (is_static_jump(insn) && insn_call_dest(insn));
 }
 
-/*
- * Checks if a function is a Rust "noreturn" one.
- */
-static bool is_rust_noreturn(const struct symbol *func)
-{
-	/*
-	 * If it does not start with "_R", then it is not a Rust symbol.
-	 */
-	if (strncmp(func->name, "_R", 2))
-		return false;
-
-	/*
-	 * These are just heuristics -- we do not control the precise symbol
-	 * name, due to the crate disambiguators (which depend on the compiler)
-	 * as well as changes to the source code itself between versions (since
-	 * these come from the Rust standard library).
-	 */
-	return str_ends_with(func->name, "_4core3num20from_str_radix_panic")				||
-	       str_ends_with(func->name, "_4core3num22from_ascii_radix_panic")				||
-	       str_ends_with(func->name, "_4core5sliceSp15copy_from_slice17len_mismatch_fail")		||
-	       str_ends_with(func->name, "_4core6option13expect_failed")				||
-	       str_ends_with(func->name, "_4core6option13unwrap_failed")				||
-	       str_ends_with(func->name, "_4core6result13unwrap_failed")				||
-	       str_ends_with(func->name, "_4core9panicking5panic")					||
-	       str_ends_with(func->name, "_4core9panicking9panic_fmt")					||
-	       str_ends_with(func->name, "_4core9panicking14panic_explicit")				||
-	       str_ends_with(func->name, "_4core9panicking14panic_nounwind")				||
-	       str_ends_with(func->name, "_4core9panicking18panic_bounds_check")			||
-	       str_ends_with(func->name, "_4core9panicking18panic_nounwind_fmt")			||
-	       str_ends_with(func->name, "_4core9panicking19assert_failed_inner")			||
-	       str_ends_with(func->name, "_4core9panicking30panic_null_pointer_dereference")		||
-	       str_ends_with(func->name, "_4core9panicking32panic_null_reference_constructed")		||
-	       str_ends_with(func->name, "_4core9panicking36panic_misaligned_pointer_dereference")	||
-	       str_ends_with(func->name, "_7___rustc17rust_begin_unwind")				||
-	       strstr(func->name, "_4core9panicking13assert_failed")					||
-	       strstr(func->name, "_4core9panicking11panic_const24panic_const_")			||
-	       (strstr(func->name, "_4core5slice5index") &&
-		strstr(func->name, "slice_") &&
-		str_ends_with(func->name, "_fail"));
-}
-
 static bool is_listed_noreturn(struct symbol *func)
 {
 #define NORETURN(func) __stringify(func),
@@ -228,9 +187,6 @@ static bool is_listed_noreturn(struct symbol *func)
 	if (is_local_sym(func))
 		return false;
 
-	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;
diff --git a/tools/objtool/noreturns.h b/tools/objtool/noreturns.h
index 2dce56030b980..4e8007a8c2584 100644
--- a/tools/objtool/noreturns.h
+++ b/tools/objtool/noreturns.h
@@ -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)
-- 
2.55.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.