[PATCH 27/27] objtool: Warn about missing/stale ANNOTATE_EXPORTED_NORETURN() usage

Josh Poimboeuf <[email protected]>
Newsgroups gmane.linux.kbuild.devel,gmane.linux.kernel,gmane.linux.kernel.rust
Message-ID <4db947677ac3e655a094335c8a23fc0dfe5b2e56.1787890035.git.jpoimboe@kernel.org>
Add some warnings to keep ANNOTATE_EXPORTED_NORETURN() annotations
correct.

Signed-off-by: Josh Poimboeuf <[email protected]>
---
 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(-)

diff --git a/tools/objtool/Documentation/objtool.txt b/tools/objtool/Documentation/objtool.txt
index d5ac48bc203ec..950e88c713d1d 100644
--- a/tools/objtool/Documentation/objtool.txt
+++ b/tools/objtool/Documentation/objtool.txt
@@ -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.
 
diff --git a/tools/objtool/check.c b/tools/objtool/check.c
index aced52ad6fc81..ac6ae823a2d45 100644
--- a/tools/objtool/check.c
+++ b/tools/objtool/check.c
@@ -224,6 +224,22 @@ static bool might_return(struct objtool_file *file, struct symbol *func)
 	return false;
 }
 
+static bool is_noreturn_candidate(struct objtool_file *file, struct symbol *func)
+{
+	struct instruction *insn;
+
+	if (!is_func_sym(func) || is_undef_sym(func) ||
+	    is_prefix_func(func) || func->embedded_insn ||
+	    func != func->alias->pfunc)
+		return false;
+
+	insn = find_insn(file, func->sec, func->offset);
+	if (!insn || insn_func(insn) != func)
+		return false;
+
+	return true;
+}
+
 static void detect_noreturns(struct objtool_file *file)
 {
 	struct symbol *func, *dest;
@@ -232,15 +248,7 @@ static void detect_noreturns(struct objtool_file *file)
 
 	/* 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)
+		if (!is_noreturn_candidate(file, func))
 			continue;
 
 		func->_noreturn = 1;
@@ -410,9 +418,38 @@ static void read_annotate_noreturn(struct objtool_file *file)
 
 		if (is_undef_sym(func))
 			func->_noreturn = 1;
+
+		func->annotate_noreturn = 1;
 	}
 }
 
+static int validate_noreturns(struct objtool_file *file)
+{
+	struct symbol *func;
+	int warnings = 0;
+
+	for_each_sym(file->elf, func) {
+		if (!is_noreturn_candidate(file, func))
+			continue;
+
+		if (opts.module && is_noreturn(func) && func->exported &&
+		    !func->annotate_noreturn) {
+			WARN("%s() is exported and noreturn, its declaration needs __noreturn and ANNOTATE_EXPORTED_NORETURN()",
+			     func->name);
+			warnings++;
+		}
+
+		if (func->annotate_noreturn && !func->ignore_noreturn &&
+		    !is_noreturn(func)) {
+			WARN("%s() has ANNOTATE_EXPORTED_NORETURN() but returns",
+			     func->name);
+			warnings++;
+		}
+	}
+
+	return warnings;
+}
+
 static void init_cfi_state(struct cfi_state *cfi)
 {
 	int i;
@@ -5005,8 +5042,10 @@ int check(struct objtool_file *file)
 
 		w += validate_functions(file);
 		w += validate_unwind_hints(file, NULL);
-		if (!w)
+		if (!w) {
 			w += validate_reachable_instructions(file);
+			w += validate_noreturns(file);
+		}
 
 		warnings += w;
 
diff --git a/tools/objtool/include/objtool/elf.h b/tools/objtool/include/objtool/elf.h
index adf6e1322c6df..e79cdabf09c26 100644
--- a/tools/objtool/include/objtool/elf.h
+++ b/tools/objtool/include/objtool/elf.h
@@ -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;
-- 
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.