[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