From 5e3992fe72748ed3892be876f09d4d990548b7af Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Tue, 18 Apr 2023 14:27:47 -0700 Subject: objtool: Limit unreachable warnings to once per function Unreachable instruction warnings are limited to once per object file. That no longer makes sense for vmlinux validation, which might have more unreachable instructions lurking in other places. Change it to once per function. Note this affects some other (much rarer) non-fatal warnings as well. In general I think one-warning-per-function makes sense, as related warnings can accumulate quickly and we want to eventually get back to failing the build with -Werror anyway. Reviewed-by: Miroslav Benes Link: https://lore.kernel.org/r/9d38f881bfc34e031c74e4e90064ccb3e49f599a.1681853186.git.jpoimboe@kernel.org Signed-off-by: Josh Poimboeuf --- tools/objtool/check.c | 5 +++-- tools/objtool/include/objtool/elf.h | 1 + tools/objtool/include/objtool/warn.h | 7 ++++++- 3 files changed, 10 insertions(+), 3 deletions(-) (limited to 'tools') diff --git a/tools/objtool/check.c b/tools/objtool/check.c index 0fcf99c91400..98e6c3b5fefc 100644 --- a/tools/objtool/check.c +++ b/tools/objtool/check.c @@ -4514,6 +4514,7 @@ static int validate_sls(struct objtool_file *file) static int validate_reachable_instructions(struct objtool_file *file) { struct instruction *insn; + int warnings = 0; if (file->ignore_unreachables) return 0; @@ -4523,10 +4524,10 @@ static int validate_reachable_instructions(struct objtool_file *file) continue; WARN_INSN(insn, "unreachable instruction"); - return 1; + warnings++; } - return 0; + return warnings; } int check(struct objtool_file *file) diff --git a/tools/objtool/include/objtool/elf.h b/tools/objtool/include/objtool/elf.h index e1ca588eb69d..78e2d0fc21ca 100644 --- a/tools/objtool/include/objtool/elf.h +++ b/tools/objtool/include/objtool/elf.h @@ -61,6 +61,7 @@ struct symbol { u8 return_thunk : 1; u8 fentry : 1; u8 profiling_func : 1; + u8 warned : 1; struct list_head pv_target; struct list_head reloc_list; }; diff --git a/tools/objtool/include/objtool/warn.h b/tools/objtool/include/objtool/warn.h index b1c920dc9516..f195deab456e 100644 --- a/tools/objtool/include/objtool/warn.h +++ b/tools/objtool/include/objtool/warn.h @@ -55,7 +55,12 @@ static inline char *offstr(struct section *sec, unsigned long offset) #define WARN_INSN(insn, format, ...) \ ({ \ - WARN_FUNC(format, insn->sec, insn->offset, ##__VA_ARGS__); \ + struct instruction *_insn = (insn); \ + if (!_insn->sym || !_insn->sym->warned) \ + WARN_FUNC(format, _insn->sec, _insn->offset, \ + ##__VA_ARGS__); \ + if (_insn->sym) \ + _insn->sym->warned = 1; \ }) #define BT_FUNC(format, insn, ...) \ -- cgit v1.2.3 From ca653464dd097fe64e69f1735e9f348b2a0f8037 Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Tue, 18 Apr 2023 14:27:48 -0700 Subject: objtool: Add verbose option for disassembling affected functions When a warning is associated with a function, add an option to disassemble that function. This makes it easier for reporters to submit the information needed to diagnose objtool warnings. Reviewed-by: Miroslav Benes Link: https://lore.kernel.org/r/dd0fe13428ede186f09c74059a8001f4adcea5fc.1681853186.git.jpoimboe@kernel.org Signed-off-by: Josh Poimboeuf --- tools/objtool/Documentation/objtool.txt | 5 +++ tools/objtool/builtin-check.c | 5 +++ tools/objtool/check.c | 77 +++++++++++++++++++++++++++++++++ tools/objtool/include/objtool/builtin.h | 1 + 4 files changed, 88 insertions(+) (limited to 'tools') diff --git a/tools/objtool/Documentation/objtool.txt b/tools/objtool/Documentation/objtool.txt index 744db4218e7a..8db1f29bf432 100644 --- a/tools/objtool/Documentation/objtool.txt +++ b/tools/objtool/Documentation/objtool.txt @@ -244,6 +244,11 @@ To achieve the validation, objtool enforces the following rules: Objtool warnings ---------------- +NOTE: When requesting help with an objtool warning, please recreate with +OBJTOOL_VERBOSE=1 (e.g., "make OBJTOOL_VERBOSE=1") and send the full +output, including any disassembly below the warning, to the objtool +maintainers. + For asm files, if you're getting an error which doesn't make sense, first make sure that the affected code follows the above rules. diff --git a/tools/objtool/builtin-check.c b/tools/objtool/builtin-check.c index 7c175198d09f..5e21cfb7661d 100644 --- a/tools/objtool/builtin-check.c +++ b/tools/objtool/builtin-check.c @@ -93,6 +93,7 @@ static const struct option check_options[] = { OPT_BOOLEAN(0, "no-unreachable", &opts.no_unreachable, "skip 'unreachable instruction' warnings"), OPT_BOOLEAN(0, "sec-address", &opts.sec_address, "print section addresses in warnings"), OPT_BOOLEAN(0, "stats", &opts.stats, "print statistics"), + OPT_BOOLEAN('v', "verbose", &opts.verbose, "verbose warnings"), OPT_END(), }; @@ -118,6 +119,10 @@ int cmd_parse_options(int argc, const char **argv, const char * const usage[]) parse_options(envc, envv, check_options, env_usage, 0); } + env = getenv("OBJTOOL_VERBOSE"); + if (env && !strcmp(env, "1")) + opts.verbose = true; + argc = parse_options(argc, argv, check_options, usage, 0); if (argc != 1) usage_with_options(usage, check_options); diff --git a/tools/objtool/check.c b/tools/objtool/check.c index 98e6c3b5fefc..0bd0ca4c767c 100644 --- a/tools/objtool/check.c +++ b/tools/objtool/check.c @@ -4530,6 +4530,81 @@ static int validate_reachable_instructions(struct objtool_file *file) return warnings; } +/* 'funcs' is a space-separated list of function names */ +static int disas_funcs(const char *funcs) +{ + const char *objdump_str, *cross_compile; + int size, ret; + char *cmd; + + cross_compile = getenv("CROSS_COMPILE"); + + objdump_str = "%sobjdump -wdr %s | gawk -M -v _funcs='%s' '" + "BEGIN { split(_funcs, funcs); }" + "/^$/ { func_match = 0; }" + "/<.*>:/ { " + "f = gensub(/.*<(.*)>:/, \"\\\\1\", 1);" + "for (i in funcs) {" + "if (funcs[i] == f) {" + "func_match = 1;" + "base = strtonum(\"0x\" $1);" + "break;" + "}" + "}" + "}" + "{" + "if (func_match) {" + "addr = strtonum(\"0x\" $1);" + "printf(\"%%04x \", addr - base);" + "print;" + "}" + "}' 1>&2"; + + /* fake snprintf() to calculate the size */ + size = snprintf(NULL, 0, objdump_str, cross_compile, objname, funcs) + 1; + if (size <= 0) { + WARN("objdump string size calculation failed"); + return -1; + } + + cmd = malloc(size); + + /* real snprintf() */ + snprintf(cmd, size, objdump_str, cross_compile, objname, funcs); + ret = system(cmd); + if (ret) { + WARN("disassembly failed: %d", ret); + return -1; + } + + return 0; +} + +static int disas_warned_funcs(struct objtool_file *file) +{ + struct symbol *sym; + char *funcs = NULL, *tmp; + + for_each_sym(file, sym) { + if (sym->warned) { + if (!funcs) { + funcs = malloc(strlen(sym->name) + 1); + strcpy(funcs, sym->name); + } else { + tmp = malloc(strlen(funcs) + strlen(sym->name) + 2); + sprintf(tmp, "%s %s", funcs, sym->name); + free(funcs); + funcs = tmp; + } + } + } + + if (funcs) + disas_funcs(funcs); + + return 0; +} + int check(struct objtool_file *file) { int ret, warnings = 0; @@ -4674,6 +4749,8 @@ int check(struct objtool_file *file) warnings += ret; } + if (opts.verbose) + disas_warned_funcs(file); if (opts.stats) { printf("nr_insns_visited: %ld\n", nr_insns_visited); diff --git a/tools/objtool/include/objtool/builtin.h b/tools/objtool/include/objtool/builtin.h index 2a108e648b7a..fcca6662c8b4 100644 --- a/tools/objtool/include/objtool/builtin.h +++ b/tools/objtool/include/objtool/builtin.h @@ -37,6 +37,7 @@ struct opts { bool no_unreachable; bool sec_address; bool stats; + bool verbose; }; extern struct opts opts; -- cgit v1.2.3 From ced23d2e3762ecfb859ae65d3a351218edff7205 Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Tue, 18 Apr 2023 14:27:49 -0700 Subject: objtool: Include backtrace in verbose mode Include backtrace in verbose mode. This makes it easy to gather all the information needed for diagnosing objtool warnings. Reviewed-by: Miroslav Benes Link: https://lore.kernel.org/r/c255224fabcf7e64bac232fec1c77c9fc2d7d7ab.1681853186.git.jpoimboe@kernel.org Signed-off-by: Josh Poimboeuf --- tools/objtool/Documentation/objtool.txt | 4 ++-- tools/objtool/check.c | 26 ++++++++++---------------- tools/objtool/include/objtool/warn.h | 14 ++++++++------ 3 files changed, 20 insertions(+), 24 deletions(-) (limited to 'tools') diff --git a/tools/objtool/Documentation/objtool.txt b/tools/objtool/Documentation/objtool.txt index 8db1f29bf432..9ec8cbf20668 100644 --- a/tools/objtool/Documentation/objtool.txt +++ b/tools/objtool/Documentation/objtool.txt @@ -246,8 +246,8 @@ Objtool warnings NOTE: When requesting help with an objtool warning, please recreate with OBJTOOL_VERBOSE=1 (e.g., "make OBJTOOL_VERBOSE=1") and send the full -output, including any disassembly below the warning, to the objtool -maintainers. +output, including any disassembly or backtrace below the warning, to the +objtool maintainers. For asm files, if you're getting an error which doesn't make sense, first make sure that the affected code follows the above rules. diff --git a/tools/objtool/check.c b/tools/objtool/check.c index 0bd0ca4c767c..71985f3a6fa6 100644 --- a/tools/objtool/check.c +++ b/tools/objtool/check.c @@ -3657,8 +3657,7 @@ static int validate_branch(struct objtool_file *file, struct symbol *func, ret = validate_branch(file, func, alt->insn, state); if (ret) { - if (opts.backtrace) - BT_FUNC("(alt)", insn); + BT_INSN(insn, "(alt)"); return ret; } } @@ -3703,8 +3702,7 @@ static int validate_branch(struct objtool_file *file, struct symbol *func, ret = validate_branch(file, func, insn->jump_dest, state); if (ret) { - if (opts.backtrace) - BT_FUNC("(branch)", insn); + BT_INSN(insn, "(branch)"); return ret; } } @@ -3802,8 +3800,8 @@ static int validate_unwind_hint(struct objtool_file *file, { if (insn->hint && !insn->visited && !insn->ignore) { int ret = validate_branch(file, insn_func(insn), insn, *state); - if (ret && opts.backtrace) - BT_FUNC("<=== (hint)", insn); + if (ret) + BT_INSN(insn, "<=== (hint)"); return ret; } @@ -3861,8 +3859,7 @@ static int validate_unret(struct objtool_file *file, struct instruction *insn) ret = validate_unret(file, alt->insn); if (ret) { - if (opts.backtrace) - BT_FUNC("(alt)", insn); + BT_INSN(insn, "(alt)"); return ret; } } @@ -3888,10 +3885,8 @@ static int validate_unret(struct objtool_file *file, struct instruction *insn) } ret = validate_unret(file, insn->jump_dest); if (ret) { - if (opts.backtrace) { - BT_FUNC("(branch%s)", insn, - insn->type == INSN_JUMP_CONDITIONAL ? "-cond" : ""); - } + BT_INSN(insn, "(branch%s)", + insn->type == INSN_JUMP_CONDITIONAL ? "-cond" : ""); return ret; } @@ -3913,8 +3908,7 @@ static int validate_unret(struct objtool_file *file, struct instruction *insn) ret = validate_unret(file, dest); if (ret) { - if (opts.backtrace) - BT_FUNC("(call)", insn); + BT_INSN(insn, "(call)"); return ret; } /* @@ -4216,8 +4210,8 @@ static int validate_symbol(struct objtool_file *file, struct section *sec, state->uaccess = sym->uaccess_safe; ret = validate_branch(file, insn_func(insn), insn, *state); - if (ret && opts.backtrace) - BT_FUNC("<=== (sym)", insn); + if (ret) + BT_INSN(insn, "<=== (sym)"); return ret; } diff --git a/tools/objtool/include/objtool/warn.h b/tools/objtool/include/objtool/warn.h index f195deab456e..ac04d3fe4dd9 100644 --- a/tools/objtool/include/objtool/warn.h +++ b/tools/objtool/include/objtool/warn.h @@ -63,12 +63,14 @@ static inline char *offstr(struct section *sec, unsigned long offset) _insn->sym->warned = 1; \ }) -#define BT_FUNC(format, insn, ...) \ -({ \ - struct instruction *_insn = (insn); \ - char *_str = offstr(_insn->sec, _insn->offset); \ - WARN(" %s: " format, _str, ##__VA_ARGS__); \ - free(_str); \ +#define BT_INSN(insn, format, ...) \ +({ \ + if (opts.verbose || opts.backtrace) { \ + struct instruction *_insn = (insn); \ + char *_str = offstr(_insn->sec, _insn->offset); \ + WARN(" %s: " format, _str, ##__VA_ARGS__); \ + free(_str); \ + } \ }) #define WARN_ELF(format, ...) \ -- cgit v1.2.3 From fedb724c3db5490234ddde0103811c28c2fedae0 Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Tue, 18 Apr 2023 14:27:50 -0700 Subject: objtool: Detect missing __noreturn annotations Most "unreachable instruction" warnings these days seem to actually be the result of a missing __noreturn annotation. Add an explicit check for that. Reviewed-by: Miroslav Benes Link: https://lore.kernel.org/r/6e2b93d8c65eaed6c4166a358269dc0ef01f890c.1681853186.git.jpoimboe@kernel.org Signed-off-by: Josh Poimboeuf --- tools/objtool/Documentation/objtool.txt | 6 ++++++ tools/objtool/check.c | 14 +++++++++++++- 2 files changed, 19 insertions(+), 1 deletion(-) (limited to 'tools') diff --git a/tools/objtool/Documentation/objtool.txt b/tools/objtool/Documentation/objtool.txt index 9ec8cbf20668..f9345e0ce740 100644 --- a/tools/objtool/Documentation/objtool.txt +++ b/tools/objtool/Documentation/objtool.txt @@ -303,6 +303,12 @@ the objtool maintainers. If it's not actually in a callable function (e.g. kernel entry code), change ENDPROC to END. +3. file.o: warning: objtool: foo+0x48c: bar() is missing a __noreturn annotation + + The call from foo() to bar() doesn't return, but bar() is missing the + __noreturn annotation. NOTE: In addition to adding the __noreturn + annotation, the function name also needs to be added to + 'global_noreturns' in tools/objtool/check.c. 4. file.o: warning: objtool: func(): can't find starting instruction or diff --git a/tools/objtool/check.c b/tools/objtool/check.c index 71985f3a6fa6..8d1b4226608c 100644 --- a/tools/objtool/check.c +++ b/tools/objtool/check.c @@ -4507,7 +4507,8 @@ static int validate_sls(struct objtool_file *file) static int validate_reachable_instructions(struct objtool_file *file) { - struct instruction *insn; + struct instruction *insn, *prev_insn; + struct symbol *call_dest; int warnings = 0; if (file->ignore_unreachables) @@ -4517,6 +4518,17 @@ static int validate_reachable_instructions(struct objtool_file *file) if (insn->visited || ignore_unreachable_insn(file, insn)) continue; + prev_insn = prev_insn_same_sec(file, insn); + if (prev_insn && prev_insn->dead_end) { + call_dest = insn_call_dest(prev_insn); + if (call_dest) { + WARN_INSN(insn, "%s() is missing a __noreturn annotation", + call_dest->name); + warnings++; + continue; + } + } + WARN_INSN(insn, "unreachable instruction"); warnings++; } -- cgit v1.2.3 From 55eeab2a8a11b71586ef0ad3adf532ca5f97d4be Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Tue, 18 Apr 2023 14:27:51 -0700 Subject: objtool: Ignore exc_double_fault() __noreturn warnings This is a hack, but it works for now. Problem is, exc_double_fault() may or may not return, depending on whether CONFIG_X86_ESPFIX64 is set. But objtool has no visibility to the kernel config. "Fix" it by silencing the exc_double_fault() __noreturn warning. This removes the following warning: vmlinux.o: warning: objtool: xenpv_exc_double_fault+0xd: exc_double_fault() is missing a __noreturn annotation Reviewed-by: Miroslav Benes Link: https://lore.kernel.org/r/a45b085071d3a7d049a20f9e78754452336ecbe8.1681853186.git.jpoimboe@kernel.org Signed-off-by: Josh Poimboeuf --- tools/objtool/check.c | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) (limited to 'tools') diff --git a/tools/objtool/check.c b/tools/objtool/check.c index 8d1b4226608c..8dac1e35264e 100644 --- a/tools/objtool/check.c +++ b/tools/objtool/check.c @@ -4505,6 +4505,35 @@ static int validate_sls(struct objtool_file *file) return warnings; } +static bool ignore_noreturn_call(struct instruction *insn) +{ + struct symbol *call_dest = insn_call_dest(insn); + + /* + * FIXME: hack, we need a real noreturn solution + * + * Problem is, exc_double_fault() may or may not return, depending on + * whether CONFIG_X86_ESPFIX64 is set. But objtool has no visibility + * to the kernel config. + * + * Other potential ways to fix it: + * + * - have compiler communicate __noreturn functions somehow + * - remove CONFIG_X86_ESPFIX64 + * - read the .config file + * - add a cmdline option + * - create a generic objtool annotation format (vs a bunch of custom + * formats) and annotate it + */ + if (!strcmp(call_dest->name, "exc_double_fault")) { + /* prevent further unreachable warnings for the caller */ + insn->sym->warned = 1; + return true; + } + + return false; +} + static int validate_reachable_instructions(struct objtool_file *file) { struct instruction *insn, *prev_insn; @@ -4521,7 +4550,7 @@ static int validate_reachable_instructions(struct objtool_file *file) prev_insn = prev_insn_same_sec(file, insn); if (prev_insn && prev_insn->dead_end) { call_dest = insn_call_dest(prev_insn); - if (call_dest) { + if (call_dest && !ignore_noreturn_call(prev_insn)) { WARN_INSN(insn, "%s() is missing a __noreturn annotation", call_dest->name); warnings++; -- cgit v1.2.3 From 34245659debd194cbd4148d2ee5176306bdf8899 Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Tue, 18 Apr 2023 14:27:52 -0700 Subject: objtool: Remove superfluous global_noreturns entries lbug_with_loc() no longer exists, and resume_play_dead() is static (objtool only checks globals and weaks). Reviewed-by: Miroslav Benes Link: https://lore.kernel.org/r/2725d7f2ccc2361c6903de9ebaa2b5bb304f7ac2.1681853186.git.jpoimboe@kernel.org Signed-off-by: Josh Poimboeuf --- tools/objtool/check.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'tools') diff --git a/tools/objtool/check.c b/tools/objtool/check.c index 8dac1e35264e..8c2762de7ae3 100644 --- a/tools/objtool/check.c +++ b/tools/objtool/check.c @@ -217,7 +217,6 @@ static bool __dead_end_function(struct objtool_file *file, struct symbol *func, "kthread_complete_and_exit", "kthread_exit", "kunit_try_catch_throw", - "lbug_with_loc", "machine_real_restart", "make_task_dead", "mpt_halt_firmware", @@ -225,7 +224,6 @@ static bool __dead_end_function(struct objtool_file *file, struct symbol *func, "panic", "panic_smp_self_stop", "rest_init", - "resume_play_dead", "rewind_stack_and_make_dead", "sev_es_terminate", "snp_abort", -- cgit v1.2.3 From d59fec29b131f30b27343d54bdf1071ee98eda8e Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Tue, 18 Apr 2023 14:27:53 -0700 Subject: tools/lib/subcmd: Replace NORETURN usage with __noreturn NORETURN is redundant with __noreturn, just use the latter. Reviewed-by: Miroslav Benes Link: https://lore.kernel.org/r/c7c83d1e6b3d2b0c3e65dd3790c22c772d3b2527.1681853186.git.jpoimboe@kernel.org Signed-off-by: Josh Poimboeuf --- tools/lib/subcmd/parse-options.h | 8 ++------ tools/lib/subcmd/subcmd-util.h | 5 ++--- 2 files changed, 4 insertions(+), 9 deletions(-) (limited to 'tools') diff --git a/tools/lib/subcmd/parse-options.h b/tools/lib/subcmd/parse-options.h index 41b9b942504d..8e9147358a28 100644 --- a/tools/lib/subcmd/parse-options.h +++ b/tools/lib/subcmd/parse-options.h @@ -6,10 +6,6 @@ #include #include -#ifndef NORETURN -#define NORETURN __attribute__((__noreturn__)) -#endif - enum parse_opt_type { /* special types */ OPTION_END, @@ -183,9 +179,9 @@ extern int parse_options_subcommand(int argc, const char **argv, const char *const subcommands[], const char *usagestr[], int flags); -extern NORETURN void usage_with_options(const char * const *usagestr, +extern __noreturn void usage_with_options(const char * const *usagestr, const struct option *options); -extern NORETURN __attribute__((format(printf,3,4))) +extern __noreturn __attribute__((format(printf,3,4))) void usage_with_options_msg(const char * const *usagestr, const struct option *options, const char *fmt, ...); diff --git a/tools/lib/subcmd/subcmd-util.h b/tools/lib/subcmd/subcmd-util.h index b2aec04fce8f..dfac76e35ac7 100644 --- a/tools/lib/subcmd/subcmd-util.h +++ b/tools/lib/subcmd/subcmd-util.h @@ -5,8 +5,7 @@ #include #include #include - -#define NORETURN __attribute__((__noreturn__)) +#include static inline void report(const char *prefix, const char *err, va_list params) { @@ -15,7 +14,7 @@ static inline void report(const char *prefix, const char *err, va_list params) fprintf(stderr, " %s%s\n", prefix, msg); } -static NORETURN inline void die(const char *err, ...) +static __noreturn inline void die(const char *err, ...) { va_list params; -- cgit v1.2.3 From 6245ce4ab670166efcdae843c35c14e4c0811aa3 Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Tue, 18 Apr 2023 14:27:54 -0700 Subject: objtool: Move noreturn function list to separate file This makes it a little cleaner and easier to maintain. Suggested-by: Peter Zijlstra Reviewed-by: Miroslav Benes Link: https://lore.kernel.org/r/cecacf07a69a244c74474c18b7652627de67a528.1681853186.git.jpoimboe@kernel.org Signed-off-by: Josh Poimboeuf --- tools/objtool/Documentation/objtool.txt | 5 ++-- tools/objtool/check.c | 44 +++----------------------------- tools/objtool/noreturns.h | 45 +++++++++++++++++++++++++++++++++ 3 files changed, 50 insertions(+), 44 deletions(-) create mode 100644 tools/objtool/noreturns.h (limited to 'tools') diff --git a/tools/objtool/Documentation/objtool.txt b/tools/objtool/Documentation/objtool.txt index f9345e0ce740..fe39c2a8ef0d 100644 --- a/tools/objtool/Documentation/objtool.txt +++ b/tools/objtool/Documentation/objtool.txt @@ -306,9 +306,8 @@ the objtool maintainers. 3. file.o: warning: objtool: foo+0x48c: bar() is missing a __noreturn annotation The call from foo() to bar() doesn't return, but bar() is missing the - __noreturn annotation. NOTE: In addition to adding the __noreturn - annotation, the function name also needs to be added to - 'global_noreturns' in tools/objtool/check.c. + __noreturn annotation. NOTE: In addition to annotating the function + with __noreturn, please also add it to tools/objtool/noreturns.h. 4. file.o: warning: objtool: func(): can't find starting instruction or diff --git a/tools/objtool/check.c b/tools/objtool/check.c index 8c2762de7ae3..a13c257f80dd 100644 --- a/tools/objtool/check.c +++ b/tools/objtool/check.c @@ -192,49 +192,11 @@ static bool __dead_end_function(struct objtool_file *file, struct symbol *func, struct instruction *insn; bool empty = true; - /* - * Unfortunately these have to be hard coded because the noreturn - * attribute isn't provided in ELF data. Keep 'em sorted. - */ +#define NORETURN(func) __stringify(func), static const char * const global_noreturns[] = { - "__invalid_creds", - "__module_put_and_kthread_exit", - "__reiserfs_panic", - "__stack_chk_fail", - "__ubsan_handle_builtin_unreachable", - "arch_call_rest_init", - "arch_cpu_idle_dead", - "btrfs_assertfail", - "cpu_bringup_and_idle", - "cpu_startup_entry", - "do_exit", - "do_group_exit", - "do_task_dead", - "ex_handler_msr_mce", - "fortify_panic", - "hlt_play_dead", - "hv_ghcb_terminate", - "kthread_complete_and_exit", - "kthread_exit", - "kunit_try_catch_throw", - "machine_real_restart", - "make_task_dead", - "mpt_halt_firmware", - "nmi_panic_self_stop", - "panic", - "panic_smp_self_stop", - "rest_init", - "rewind_stack_and_make_dead", - "sev_es_terminate", - "snp_abort", - "start_kernel", - "stop_this_cpu", - "usercopy_abort", - "x86_64_start_kernel", - "x86_64_start_reservations", - "xen_cpu_bringup_again", - "xen_start_kernel", +#include "noreturns.h" }; +#undef NORETURN if (!func) return false; diff --git a/tools/objtool/noreturns.h b/tools/objtool/noreturns.h new file mode 100644 index 000000000000..cede6068ddf6 --- /dev/null +++ b/tools/objtool/noreturns.h @@ -0,0 +1,45 @@ +/* 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. + */ +NORETURN(__invalid_creds) +NORETURN(__module_put_and_kthread_exit) +NORETURN(__reiserfs_panic) +NORETURN(__stack_chk_fail) +NORETURN(__ubsan_handle_builtin_unreachable) +NORETURN(arch_call_rest_init) +NORETURN(arch_cpu_idle_dead) +NORETURN(btrfs_assertfail) +NORETURN(cpu_bringup_and_idle) +NORETURN(cpu_startup_entry) +NORETURN(do_exit) +NORETURN(do_group_exit) +NORETURN(do_task_dead) +NORETURN(ex_handler_msr_mce) +NORETURN(fortify_panic) +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) +NORETURN(mpt_halt_firmware) +NORETURN(nmi_panic_self_stop) +NORETURN(panic) +NORETURN(panic_smp_self_stop) +NORETURN(rest_init) +NORETURN(rewind_stack_and_make_dead) +NORETURN(sev_es_terminate) +NORETURN(snp_abort) +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) -- cgit v1.2.3 From ff9a6459bbec06df7da2545020d7383aba13b3fb Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Fri, 2 Jun 2023 10:54:53 -0700 Subject: objtool: Add __kunit_abort() to noreturns Fixes a bunch of warnings like: drivers/input/tests/input_test.o: warning: objtool: input_test_init+0x1cb: stack state mismatch: cfa1=4+64 cfa2=4+56 lib/kunit/kunit-test.o: warning: objtool: kunit_log_newline_test+0xfb: return with modified stack frame ... Fixes: 260755184cbd ("kunit: Move kunit_abort() call out of kunit_do_failed_assertion()") Reported-by: Stephen Rothwell Signed-off-by: Josh Poimboeuf Signed-off-by: Peter Zijlstra (Intel) Link: https://lkml.kernel.org/r/20230602175453.swsn3ehyochtwkhy@treble --- tools/objtool/noreturns.h | 1 + 1 file changed, 1 insertion(+) (limited to 'tools') diff --git a/tools/objtool/noreturns.h b/tools/objtool/noreturns.h index cede6068ddf6..1514e84d5cc4 100644 --- a/tools/objtool/noreturns.h +++ b/tools/objtool/noreturns.h @@ -7,6 +7,7 @@ * Yes, this is unfortunate. A better solution is in the works. */ NORETURN(__invalid_creds) +NORETURN(__kunit_abort) NORETURN(__module_put_and_kthread_exit) NORETURN(__reiserfs_panic) NORETURN(__stack_chk_fail) -- cgit v1.2.3 From d49d1666aab51ad3caf79f414aff6b641837a6ea Mon Sep 17 00:00:00 2001 From: Lu Hongfei Date: Tue, 30 May 2023 15:56:49 +0800 Subject: tools: Remove unnecessary variables There are several places where warnings variables are not needed, remove them and directly return 0. Signed-off-by: Lu Hongfei Link: https://lore.kernel.org/r/20230530075649.21661-1-luhongfei@vivo.com Signed-off-by: Josh Poimboeuf --- tools/objtool/check.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'tools') diff --git a/tools/objtool/check.c b/tools/objtool/check.c index a13c257f80dd..4b869de7e827 100644 --- a/tools/objtool/check.c +++ b/tools/objtool/check.c @@ -3799,7 +3799,7 @@ static int validate_unwind_hints(struct objtool_file *file, struct section *sec) static int validate_unret(struct objtool_file *file, struct instruction *insn) { struct instruction *next, *dest; - int ret, warnings = 0; + int ret; for (;;) { next = next_insn_to_validate(file, insn); @@ -3897,7 +3897,7 @@ static int validate_unret(struct objtool_file *file, struct instruction *insn) insn = next; } - return warnings; + return 0; } /* @@ -4132,7 +4132,6 @@ static int add_prefix_symbols(struct objtool_file *file) { struct section *sec; struct symbol *func; - int warnings = 0; for_each_sec(file, sec) { if (!(sec->sh.sh_flags & SHF_EXECINSTR)) @@ -4146,7 +4145,7 @@ static int add_prefix_symbols(struct objtool_file *file) } } - return warnings; + return 0; } static int validate_symbol(struct objtool_file *file, struct section *sec, -- cgit v1.2.3 From 1e4b619185e83e54aca617cf5070c64a88fe936b Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Mon, 5 Jun 2023 09:12:21 -0700 Subject: objtool: Allow stack operations in UNWIND_HINT_UNDEFINED regions If the code specified UNWIND_HINT_UNDEFINED, skip the "undefined stack state" warning due to a stack operation. Just ignore the stack op and continue to propagate the undefined state. Link: https://lore.kernel.org/r/820c5b433f17c84e8761fb7465a8d319d706b1cf.1685981486.git.jpoimboe@kernel.org Signed-off-by: Josh Poimboeuf --- tools/objtool/check.c | 12 ++++++++++++ tools/objtool/include/objtool/cfi.h | 1 + 2 files changed, 13 insertions(+) (limited to 'tools') diff --git a/tools/objtool/check.c b/tools/objtool/check.c index 4b869de7e827..b11c25a715ac 100644 --- a/tools/objtool/check.c +++ b/tools/objtool/check.c @@ -33,6 +33,7 @@ static unsigned long nr_cfi, nr_cfi_reused, nr_cfi_cache; static struct cfi_init_state initial_func_cfi; static struct cfi_state init_cfi; static struct cfi_state func_cfi; +static struct cfi_state force_undefined_cfi; struct instruction *find_insn(struct objtool_file *file, struct section *sec, unsigned long offset) @@ -2240,6 +2241,11 @@ static int read_unwind_hints(struct objtool_file *file) insn->hint = true; + if (hint->type == UNWIND_HINT_TYPE_UNDEFINED) { + insn->cfi = &force_undefined_cfi; + continue; + } + if (hint->type == UNWIND_HINT_TYPE_SAVE) { insn->hint = false; insn->save = true; @@ -2793,6 +2799,10 @@ static int update_cfi_state(struct instruction *insn, struct cfi_reg *cfa = &cfi->cfa; struct cfi_reg *regs = cfi->regs; + /* ignore UNWIND_HINT_UNDEFINED regions */ + if (cfi->force_undefined) + return 0; + /* stack operations don't make sense with an undefined CFA */ if (cfa->base == CFI_UNDEFINED) { if (insn_func(insn)) { @@ -4607,6 +4617,8 @@ int check(struct objtool_file *file) init_cfi_state(&init_cfi); init_cfi_state(&func_cfi); set_func_state(&func_cfi); + init_cfi_state(&force_undefined_cfi); + force_undefined_cfi.force_undefined = true; if (!cfi_hash_alloc(1UL << (file->elf->symbol_bits - 3))) goto out; diff --git a/tools/objtool/include/objtool/cfi.h b/tools/objtool/include/objtool/cfi.h index b1258e79a1b7..c8a6bec4f6b9 100644 --- a/tools/objtool/include/objtool/cfi.h +++ b/tools/objtool/include/objtool/cfi.h @@ -36,6 +36,7 @@ struct cfi_state { bool drap; bool signal; bool end; + bool force_undefined; }; #endif /* _OBJTOOL_CFI_H */ -- cgit v1.2.3 From 809373e17b2649948cc681dd1962b2736b22c7a6 Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Tue, 30 May 2023 10:20:53 -0700 Subject: objtool: Tidy elf.h Reorganize elf.h a bit: - Move the prototypes higher up so they can be used by the inline functions. - Move hash-related code to the bottom. - Remove the unused ELF_HASH_BITS macro. No functional changes. Link: https://lore.kernel.org/r/b1490ed85951868219a6ece177a7cd30a6454d66.1685464332.git.jpoimboe@kernel.org Signed-off-by: Josh Poimboeuf --- tools/objtool/include/objtool/elf.h | 96 ++++++++++++++++++------------------- 1 file changed, 47 insertions(+), 49 deletions(-) (limited to 'tools') diff --git a/tools/objtool/include/objtool/elf.h b/tools/objtool/include/objtool/elf.h index 78e2d0fc21ca..b24f83e7ca34 100644 --- a/tools/objtool/include/objtool/elf.h +++ b/tools/objtool/include/objtool/elf.h @@ -83,8 +83,6 @@ struct reloc { bool jump_table_start; }; -#define ELF_HASH_BITS 20 - struct elf { Elf *elf; GElf_Ehdr ehdr; @@ -110,53 +108,6 @@ struct elf { struct symbol *symbol_data; }; -#define OFFSET_STRIDE_BITS 4 -#define OFFSET_STRIDE (1UL << OFFSET_STRIDE_BITS) -#define OFFSET_STRIDE_MASK (~(OFFSET_STRIDE - 1)) - -#define for_offset_range(_offset, _start, _end) \ - for (_offset = ((_start) & OFFSET_STRIDE_MASK); \ - _offset >= ((_start) & OFFSET_STRIDE_MASK) && \ - _offset <= ((_end) & OFFSET_STRIDE_MASK); \ - _offset += OFFSET_STRIDE) - -static inline u32 sec_offset_hash(struct section *sec, unsigned long offset) -{ - u32 ol, oh, idx = sec->idx; - - offset &= OFFSET_STRIDE_MASK; - - ol = offset; - oh = (offset >> 16) >> 16; - - __jhash_mix(ol, oh, idx); - - return ol; -} - -static inline u32 reloc_hash(struct reloc *reloc) -{ - return sec_offset_hash(reloc->sec, reloc->offset); -} - -/* - * Try to see if it's a whole archive (vmlinux.o or module). - * - * Note this will miss the case where a module only has one source file. - */ -static inline bool has_multiple_files(struct elf *elf) -{ - return elf->num_files > 1; -} - -static inline int elf_class_addrsize(struct elf *elf) -{ - if (elf->ehdr.e_ident[EI_CLASS] == ELFCLASS32) - return sizeof(u32); - else - return sizeof(u64); -} - struct elf *elf_open_read(const char *name, int flags); struct section *elf_create_section(struct elf *elf, const char *name, unsigned int sh_flags, size_t entsize, int nr); @@ -186,6 +137,24 @@ struct reloc *find_reloc_by_dest_range(const struct elf *elf, struct section *se unsigned long offset, unsigned int len); struct symbol *find_func_containing(struct section *sec, unsigned long offset); +/* + * Try to see if it's a whole archive (vmlinux.o or module). + * + * Note this will miss the case where a module only has one source file. + */ +static inline bool has_multiple_files(struct elf *elf) +{ + return elf->num_files > 1; +} + +static inline int elf_class_addrsize(struct elf *elf) +{ + if (elf->ehdr.e_ident[EI_CLASS] == ELFCLASS32) + return sizeof(u32); + else + return sizeof(u64); +} + #define for_each_sec(file, sec) \ list_for_each_entry(sec, &file->elf->sections, list) @@ -198,4 +167,33 @@ struct symbol *find_func_containing(struct section *sec, unsigned long offset); for_each_sec(file, __sec) \ sec_for_each_sym(__sec, sym) +#define OFFSET_STRIDE_BITS 4 +#define OFFSET_STRIDE (1UL << OFFSET_STRIDE_BITS) +#define OFFSET_STRIDE_MASK (~(OFFSET_STRIDE - 1)) + +#define for_offset_range(_offset, _start, _end) \ + for (_offset = ((_start) & OFFSET_STRIDE_MASK); \ + _offset >= ((_start) & OFFSET_STRIDE_MASK) && \ + _offset <= ((_end) & OFFSET_STRIDE_MASK); \ + _offset += OFFSET_STRIDE) + +static inline u32 sec_offset_hash(struct section *sec, unsigned long offset) +{ + u32 ol, oh, idx = sec->idx; + + offset &= OFFSET_STRIDE_MASK; + + ol = offset; + oh = (offset >> 16) >> 16; + + __jhash_mix(ol, oh, idx); + + return ol; +} + +static inline u32 reloc_hash(struct reloc *reloc) +{ + return sec_offset_hash(reloc->sec, reloc->offset); +} + #endif /* _OBJTOOL_ELF_H */ -- cgit v1.2.3 From 2707579dfa615a5dda4aabb92e433f03a87b5ec5 Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Tue, 30 May 2023 10:20:54 -0700 Subject: objtool: Remove flags argument from elf_create_section() Simplify the elf_create_section() interface a bit by removing the flags argument. Most callers don't care about changing the section header flags. If needed, they can be modified afterwards, just like any other section header field. Link: https://lore.kernel.org/r/515235d9cf62637a14bee37bfa9169ef20065471.1685464332.git.jpoimboe@kernel.org Signed-off-by: Josh Poimboeuf --- tools/objtool/check.c | 17 ++++++++++------- tools/objtool/elf.c | 10 +++++----- tools/objtool/include/objtool/elf.h | 2 +- tools/objtool/orc_gen.c | 4 ++-- 4 files changed, 18 insertions(+), 15 deletions(-) (limited to 'tools') diff --git a/tools/objtool/check.c b/tools/objtool/check.c index b11c25a715ac..eaf681557096 100644 --- a/tools/objtool/check.c +++ b/tools/objtool/check.c @@ -677,11 +677,14 @@ static int create_static_call_sections(struct objtool_file *file) list_for_each_entry(insn, &file->static_call_list, call_node) idx++; - sec = elf_create_section(file->elf, ".static_call_sites", SHF_WRITE, + sec = elf_create_section(file->elf, ".static_call_sites", sizeof(struct static_call_site), idx); if (!sec) return -1; + /* Allow modules to set the low bits of static_call_site::key */ + sec->sh.sh_flags |= SHF_WRITE; + idx = 0; list_for_each_entry(insn, &file->static_call_list, call_node) { @@ -763,7 +766,7 @@ static int create_retpoline_sites_sections(struct objtool_file *file) if (!idx) return 0; - sec = elf_create_section(file->elf, ".retpoline_sites", 0, + sec = elf_create_section(file->elf, ".retpoline_sites", sizeof(int), idx); if (!sec) { WARN("elf_create_section: .retpoline_sites"); @@ -809,7 +812,7 @@ static int create_return_sites_sections(struct objtool_file *file) if (!idx) return 0; - sec = elf_create_section(file->elf, ".return_sites", 0, + sec = elf_create_section(file->elf, ".return_sites", sizeof(int), idx); if (!sec) { WARN("elf_create_section: .return_sites"); @@ -861,7 +864,7 @@ static int create_ibt_endbr_seal_sections(struct objtool_file *file) if (!idx) return 0; - sec = elf_create_section(file->elf, ".ibt_endbr_seal", 0, + sec = elf_create_section(file->elf, ".ibt_endbr_seal", sizeof(int), idx); if (!sec) { WARN("elf_create_section: .ibt_endbr_seal"); @@ -920,7 +923,7 @@ static int create_cfi_sections(struct objtool_file *file) idx++; } - sec = elf_create_section(file->elf, ".cfi_sites", 0, sizeof(unsigned int), idx); + sec = elf_create_section(file->elf, ".cfi_sites", sizeof(unsigned int), idx); if (!sec) return -1; @@ -968,7 +971,7 @@ static int create_mcount_loc_sections(struct objtool_file *file) list_for_each_entry(insn, &file->mcount_loc_list, call_node) idx++; - sec = elf_create_section(file->elf, "__mcount_loc", 0, addrsize, idx); + sec = elf_create_section(file->elf, "__mcount_loc", addrsize, idx); if (!sec) return -1; @@ -1013,7 +1016,7 @@ static int create_direct_call_sections(struct objtool_file *file) list_for_each_entry(insn, &file->call_list, call_node) idx++; - sec = elf_create_section(file->elf, ".call_sites", 0, sizeof(unsigned int), idx); + sec = elf_create_section(file->elf, ".call_sites", sizeof(unsigned int), idx); if (!sec) return -1; diff --git a/tools/objtool/elf.c b/tools/objtool/elf.c index 500e92979a31..7598c0a2633d 100644 --- a/tools/objtool/elf.c +++ b/tools/objtool/elf.c @@ -1059,7 +1059,7 @@ static int elf_add_string(struct elf *elf, struct section *strtab, char *str) } struct section *elf_create_section(struct elf *elf, const char *name, - unsigned int sh_flags, size_t entsize, int nr) + size_t entsize, int nr) { struct section *sec, *shstrtab; size_t size = entsize * nr; @@ -1117,7 +1117,7 @@ struct section *elf_create_section(struct elf *elf, const char *name, sec->sh.sh_entsize = entsize; sec->sh.sh_type = SHT_PROGBITS; sec->sh.sh_addralign = 1; - sec->sh.sh_flags = SHF_ALLOC | sh_flags; + sec->sh.sh_flags = SHF_ALLOC; /* Add section name to .shstrtab (or .strtab for Clang) */ shstrtab = find_section_by_name(elf, ".shstrtab"); @@ -1153,7 +1153,7 @@ static struct section *elf_create_rel_reloc_section(struct elf *elf, struct sect strcpy(relocname, ".rel"); strcat(relocname, base->name); - sec = elf_create_section(elf, relocname, 0, sizeof(GElf_Rel), 0); + sec = elf_create_section(elf, relocname, sizeof(GElf_Rel), 0); free(relocname); if (!sec) return NULL; @@ -1185,9 +1185,9 @@ static struct section *elf_create_rela_reloc_section(struct elf *elf, struct sec strcat(relocname, base->name); if (addrsize == sizeof(u32)) - sec = elf_create_section(elf, relocname, 0, sizeof(Elf32_Rela), 0); + sec = elf_create_section(elf, relocname, sizeof(Elf32_Rela), 0); else - sec = elf_create_section(elf, relocname, 0, sizeof(GElf_Rela), 0); + sec = elf_create_section(elf, relocname, sizeof(GElf_Rela), 0); free(relocname); if (!sec) return NULL; diff --git a/tools/objtool/include/objtool/elf.h b/tools/objtool/include/objtool/elf.h index b24f83e7ca34..2c28aeeb3cb2 100644 --- a/tools/objtool/include/objtool/elf.h +++ b/tools/objtool/include/objtool/elf.h @@ -109,7 +109,7 @@ struct elf { }; struct elf *elf_open_read(const char *name, int flags); -struct section *elf_create_section(struct elf *elf, const char *name, unsigned int sh_flags, size_t entsize, int nr); +struct section *elf_create_section(struct elf *elf, const char *name, size_t entsize, int nr); struct symbol *elf_create_prefix_symbol(struct elf *elf, struct symbol *orig, long size); diff --git a/tools/objtool/orc_gen.c b/tools/objtool/orc_gen.c index 48efd1e2f00d..d5f750be7d7d 100644 --- a/tools/objtool/orc_gen.c +++ b/tools/objtool/orc_gen.c @@ -237,12 +237,12 @@ int orc_create(struct objtool_file *file) WARN("file already has .orc_unwind section, skipping"); return -1; } - orc_sec = elf_create_section(file->elf, ".orc_unwind", 0, + orc_sec = elf_create_section(file->elf, ".orc_unwind", sizeof(struct orc_entry), nr); if (!orc_sec) return -1; - sec = elf_create_section(file->elf, ".orc_unwind_ip", 0, sizeof(int), nr); + sec = elf_create_section(file->elf, ".orc_unwind_ip", sizeof(int), nr); if (!sec) return -1; -- cgit v1.2.3 From a5bd623653231bce8657978e9d2c2ebfaf19e297 Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Tue, 30 May 2023 10:20:55 -0700 Subject: objtool: Improve reloc naming - The term "reloc" is overloaded to mean both "an instance of struct reloc" and "a reloc section". Change the latter to "rsec". - For variable names, use "sec" for regular sections and "rsec" for rela sections to prevent them getting mixed up. - For struct reloc variables, use "reloc" instead of "rel" everywhere for consistency. Link: https://lore.kernel.org/r/8b790e403df46f445c21003e7893b8f53b99a6f3.1685464332.git.jpoimboe@kernel.org Signed-off-by: Josh Poimboeuf --- tools/objtool/check.c | 134 ++++++++++++++--------------- tools/objtool/elf.c | 164 ++++++++++++++++++------------------ tools/objtool/include/objtool/elf.h | 2 +- 3 files changed, 151 insertions(+), 149 deletions(-) (limited to 'tools') diff --git a/tools/objtool/check.c b/tools/objtool/check.c index eaf681557096..f4c52a2c8d5b 100644 --- a/tools/objtool/check.c +++ b/tools/objtool/check.c @@ -494,7 +494,7 @@ static int add_pv_ops(struct objtool_file *file, const char *symname) { struct symbol *sym, *func; unsigned long off, end; - struct reloc *rel; + struct reloc *reloc; int idx; sym = find_symbol_by_name(file->elf, symname); @@ -504,19 +504,19 @@ static int add_pv_ops(struct objtool_file *file, const char *symname) off = sym->offset; end = off + sym->len; for (;;) { - rel = find_reloc_by_dest_range(file->elf, sym->sec, off, end - off); - if (!rel) + reloc = find_reloc_by_dest_range(file->elf, sym->sec, off, end - off); + if (!reloc) break; - func = rel->sym; + func = reloc->sym; if (func->type == STT_SECTION) - func = find_symbol_by_offset(rel->sym->sec, rel->addend); + func = find_symbol_by_offset(reloc->sym->sec, reloc->addend); - idx = (rel->offset - sym->offset) / sizeof(unsigned long); + idx = (reloc->offset - sym->offset) / sizeof(unsigned long); objtool_pv_add(file, idx, func); - off = rel->offset + 1; + off = reloc->offset + 1; if (off > end) break; } @@ -581,20 +581,20 @@ static struct instruction *find_last_insn(struct objtool_file *file, */ static int add_dead_ends(struct objtool_file *file) { - struct section *sec; + struct section *rsec; struct reloc *reloc; struct instruction *insn; /* * Check for manually annotated dead ends. */ - sec = find_section_by_name(file->elf, ".rela.discard.unreachable"); - if (!sec) + rsec = find_section_by_name(file->elf, ".rela.discard.unreachable"); + if (!rsec) goto reachable; - list_for_each_entry(reloc, &sec->reloc_list, list) { + list_for_each_entry(reloc, &rsec->reloc_list, list) { if (reloc->sym->type != STT_SECTION) { - WARN("unexpected relocation symbol type in %s", sec->name); + WARN("unexpected relocation symbol type in %s", rsec->name); return -1; } insn = find_insn(file, reloc->sym->sec, reloc->addend); @@ -623,13 +623,13 @@ reachable: * GCC doesn't know the "ud2" is fatal, so it generates code as if it's * not a dead end. */ - sec = find_section_by_name(file->elf, ".rela.discard.reachable"); - if (!sec) + rsec = find_section_by_name(file->elf, ".rela.discard.reachable"); + if (!rsec) return 0; - list_for_each_entry(reloc, &sec->reloc_list, list) { + list_for_each_entry(reloc, &rsec->reloc_list, list) { if (reloc->sym->type != STT_SECTION) { - WARN("unexpected relocation symbol type in %s", sec->name); + WARN("unexpected relocation symbol type in %s", rsec->name); return -1; } insn = find_insn(file, reloc->sym->sec, reloc->addend); @@ -1044,15 +1044,15 @@ static int create_direct_call_sections(struct objtool_file *file) static void add_ignores(struct objtool_file *file) { struct instruction *insn; - struct section *sec; + struct section *rsec; struct symbol *func; struct reloc *reloc; - sec = find_section_by_name(file->elf, ".rela.discard.func_stack_frame_non_standard"); - if (!sec) + rsec = find_section_by_name(file->elf, ".rela.discard.func_stack_frame_non_standard"); + if (!rsec) return; - list_for_each_entry(reloc, &sec->reloc_list, list) { + list_for_each_entry(reloc, &rsec->reloc_list, list) { switch (reloc->sym->type) { case STT_FUNC: func = reloc->sym; @@ -1065,7 +1065,8 @@ static void add_ignores(struct objtool_file *file) break; default: - WARN("unexpected relocation symbol type in %s: %d", sec->name, reloc->sym->type); + WARN("unexpected relocation symbol type in %s: %d", + rsec->name, reloc->sym->type); continue; } @@ -1284,17 +1285,17 @@ static void add_uaccess_safe(struct objtool_file *file) */ static int add_ignore_alternatives(struct objtool_file *file) { - struct section *sec; + struct section *rsec; struct reloc *reloc; struct instruction *insn; - sec = find_section_by_name(file->elf, ".rela.discard.ignore_alts"); - if (!sec) + rsec = find_section_by_name(file->elf, ".rela.discard.ignore_alts"); + if (!rsec) return 0; - list_for_each_entry(reloc, &sec->reloc_list, list) { + list_for_each_entry(reloc, &rsec->reloc_list, list) { if (reloc->sym->type != STT_SECTION) { - WARN("unexpected relocation symbol type in %s", sec->name); + WARN("unexpected relocation symbol type in %s", rsec->name); return -1; } @@ -2204,7 +2205,7 @@ static void set_func_state(struct cfi_state *state) static int read_unwind_hints(struct objtool_file *file) { struct cfi_state cfi = init_cfi; - struct section *sec, *relocsec; + struct section *sec; struct unwind_hint *hint; struct instruction *insn; struct reloc *reloc; @@ -2214,8 +2215,7 @@ static int read_unwind_hints(struct objtool_file *file) if (!sec) return 0; - relocsec = sec->reloc; - if (!relocsec) { + if (!sec->rsec) { WARN("missing .rela.discard.unwind_hints section"); return -1; } @@ -2295,15 +2295,15 @@ static int read_unwind_hints(struct objtool_file *file) static int read_noendbr_hints(struct objtool_file *file) { - struct section *sec; struct instruction *insn; + struct section *rsec; struct reloc *reloc; - sec = find_section_by_name(file->elf, ".rela.discard.noendbr"); - if (!sec) + rsec = find_section_by_name(file->elf, ".rela.discard.noendbr"); + if (!rsec) return 0; - list_for_each_entry(reloc, &sec->reloc_list, list) { + list_for_each_entry(reloc, &rsec->reloc_list, list) { insn = find_insn(file, reloc->sym->sec, reloc->sym->offset + reloc->addend); if (!insn) { WARN("bad .discard.noendbr entry"); @@ -2318,17 +2318,17 @@ static int read_noendbr_hints(struct objtool_file *file) static int read_retpoline_hints(struct objtool_file *file) { - struct section *sec; + struct section *rsec; struct instruction *insn; struct reloc *reloc; - sec = find_section_by_name(file->elf, ".rela.discard.retpoline_safe"); - if (!sec) + rsec = find_section_by_name(file->elf, ".rela.discard.retpoline_safe"); + if (!rsec) return 0; - list_for_each_entry(reloc, &sec->reloc_list, list) { + list_for_each_entry(reloc, &rsec->reloc_list, list) { if (reloc->sym->type != STT_SECTION) { - WARN("unexpected relocation symbol type in %s", sec->name); + WARN("unexpected relocation symbol type in %s", rsec->name); return -1; } @@ -2354,17 +2354,17 @@ static int read_retpoline_hints(struct objtool_file *file) static int read_instr_hints(struct objtool_file *file) { - struct section *sec; + struct section *rsec; struct instruction *insn; struct reloc *reloc; - sec = find_section_by_name(file->elf, ".rela.discard.instr_end"); - if (!sec) + rsec = find_section_by_name(file->elf, ".rela.discard.instr_end"); + if (!rsec) return 0; - list_for_each_entry(reloc, &sec->reloc_list, list) { + list_for_each_entry(reloc, &rsec->reloc_list, list) { if (reloc->sym->type != STT_SECTION) { - WARN("unexpected relocation symbol type in %s", sec->name); + WARN("unexpected relocation symbol type in %s", rsec->name); return -1; } @@ -2377,13 +2377,13 @@ static int read_instr_hints(struct objtool_file *file) insn->instr--; } - sec = find_section_by_name(file->elf, ".rela.discard.instr_begin"); - if (!sec) + rsec = find_section_by_name(file->elf, ".rela.discard.instr_begin"); + if (!rsec) return 0; - list_for_each_entry(reloc, &sec->reloc_list, list) { + list_for_each_entry(reloc, &rsec->reloc_list, list) { if (reloc->sym->type != STT_SECTION) { - WARN("unexpected relocation symbol type in %s", sec->name); + WARN("unexpected relocation symbol type in %s", rsec->name); return -1; } @@ -2401,17 +2401,17 @@ static int read_instr_hints(struct objtool_file *file) static int read_validate_unret_hints(struct objtool_file *file) { - struct section *sec; + struct section *rsec; struct instruction *insn; struct reloc *reloc; - sec = find_section_by_name(file->elf, ".rela.discard.validate_unret"); - if (!sec) + rsec = find_section_by_name(file->elf, ".rela.discard.validate_unret"); + if (!rsec) return 0; - list_for_each_entry(reloc, &sec->reloc_list, list) { + list_for_each_entry(reloc, &rsec->reloc_list, list) { if (reloc->sym->type != STT_SECTION) { - WARN("unexpected relocation symbol type in %s", sec->name); + WARN("unexpected relocation symbol type in %s", rsec->name); return -1; } @@ -2430,19 +2430,19 @@ static int read_validate_unret_hints(struct objtool_file *file) static int read_intra_function_calls(struct objtool_file *file) { struct instruction *insn; - struct section *sec; + struct section *rsec; struct reloc *reloc; - sec = find_section_by_name(file->elf, ".rela.discard.intra_function_calls"); - if (!sec) + rsec = find_section_by_name(file->elf, ".rela.discard.intra_function_calls"); + if (!rsec) return 0; - list_for_each_entry(reloc, &sec->reloc_list, list) { + list_for_each_entry(reloc, &rsec->reloc_list, list) { unsigned long dest_off; if (reloc->sym->type != STT_SECTION) { WARN("unexpected relocation symbol type in %s", - sec->name); + rsec->name); return -1; } @@ -3342,15 +3342,15 @@ static inline bool func_uaccess_safe(struct symbol *func) static inline const char *call_dest_name(struct instruction *insn) { static char pvname[19]; - struct reloc *rel; + struct reloc *reloc; int idx; if (insn_call_dest(insn)) return insn_call_dest(insn)->name; - rel = insn_reloc(NULL, insn); - if (rel && !strcmp(rel->sym->name, "pv_ops")) { - idx = (rel->addend / sizeof(void *)); + reloc = insn_reloc(NULL, insn); + if (reloc && !strcmp(reloc->sym->name, "pv_ops")) { + idx = (reloc->addend / sizeof(void *)); snprintf(pvname, sizeof(pvname), "pv_ops[%d]", idx); return pvname; } @@ -3361,14 +3361,14 @@ static inline const char *call_dest_name(struct instruction *insn) static bool pv_call_dest(struct objtool_file *file, struct instruction *insn) { struct symbol *target; - struct reloc *rel; + struct reloc *reloc; int idx; - rel = insn_reloc(file, insn); - if (!rel || strcmp(rel->sym->name, "pv_ops")) + reloc = insn_reloc(file, insn); + if (!reloc || strcmp(reloc->sym->name, "pv_ops")) return false; - idx = (arch_dest_reloc_offset(rel->addend) / sizeof(void *)); + idx = (arch_dest_reloc_offset(reloc->addend) / sizeof(void *)); if (file->pv_ops[idx].clean) return true; @@ -4410,7 +4410,7 @@ static int validate_ibt(struct objtool_file *file) if (sec->sh.sh_flags & SHF_EXECINSTR) continue; - if (!sec->reloc) + if (!sec->rsec) continue; /* @@ -4437,7 +4437,7 @@ static int validate_ibt(struct objtool_file *file) strstr(sec->name, "__patchable_function_entries")) continue; - list_for_each_entry(reloc, &sec->reloc->reloc_list, list) + list_for_each_entry(reloc, &sec->rsec->reloc_list, list) warnings += validate_ibt_data_reloc(file, reloc); } diff --git a/tools/objtool/elf.c b/tools/objtool/elf.c index 7598c0a2633d..86ae62dfdba2 100644 --- a/tools/objtool/elf.c +++ b/tools/objtool/elf.c @@ -233,17 +233,17 @@ struct reloc *find_reloc_by_dest_range(const struct elf *elf, struct section *se unsigned long offset, unsigned int len) { struct reloc *reloc, *r = NULL; + struct section *rsec; unsigned long o; - if (!sec->reloc) + rsec = sec->rsec; + if (!rsec) return NULL; - sec = sec->reloc; - for_offset_range(o, offset, offset + len) { elf_hash_for_each_possible(reloc, reloc, hash, - sec_offset_hash(sec, o)) { - if (reloc->sec != sec) + sec_offset_hash(rsec, o)) { + if (reloc->sec != rsec) continue; if (reloc->offset >= offset && reloc->offset < offset + len) { @@ -534,7 +534,7 @@ err: } static struct section *elf_create_reloc_section(struct elf *elf, - struct section *base, + struct section *sec, int reltype); int elf_add_reloc(struct elf *elf, struct section *sec, unsigned long offset, @@ -542,7 +542,7 @@ int elf_add_reloc(struct elf *elf, struct section *sec, unsigned long offset, { struct reloc *reloc; - if (!sec->reloc && !elf_create_reloc_section(elf, sec, SHT_RELA)) + if (!sec->rsec && !elf_create_reloc_section(elf, sec, SHT_RELA)) return -1; reloc = malloc(sizeof(*reloc)); @@ -552,18 +552,18 @@ int elf_add_reloc(struct elf *elf, struct section *sec, unsigned long offset, } memset(reloc, 0, sizeof(*reloc)); - reloc->sec = sec->reloc; + reloc->sec = sec->rsec; reloc->offset = offset; reloc->type = type; reloc->sym = sym; reloc->addend = addend; list_add_tail(&reloc->sym_reloc_entry, &sym->reloc_list); - list_add_tail(&reloc->list, &sec->reloc->reloc_list); + list_add_tail(&reloc->list, &sec->rsec->reloc_list); elf_hash_add(reloc, &reloc->hash, reloc_hash(reloc)); - sec->reloc->sh.sh_size += sec->reloc->sh.sh_entsize; - sec->reloc->changed = true; + sec->rsec->sh.sh_size += sec->rsec->sh.sh_entsize; + sec->rsec->changed = true; return 0; } @@ -865,9 +865,9 @@ int elf_add_reloc_to_insn(struct elf *elf, struct section *sec, return elf_add_reloc(elf, sec, offset, type, sym, addend); } -static int read_rel_reloc(struct section *sec, int i, struct reloc *reloc, unsigned int *symndx) +static int read_rel_reloc(struct section *rsec, int i, struct reloc *reloc, unsigned int *symndx) { - if (!gelf_getrel(sec->data, i, &reloc->rel)) { + if (!gelf_getrel(rsec->data, i, &reloc->rel)) { WARN_ELF("gelf_getrel"); return -1; } @@ -878,9 +878,9 @@ static int read_rel_reloc(struct section *sec, int i, struct reloc *reloc, unsig return 0; } -static int read_rela_reloc(struct section *sec, int i, struct reloc *reloc, unsigned int *symndx) +static int read_rela_reloc(struct section *rsec, int i, struct reloc *reloc, unsigned int *symndx) { - if (!gelf_getrela(sec->data, i, &reloc->rela)) { + if (!gelf_getrela(rsec->data, i, &reloc->rela)) { WARN_ELF("gelf_getrela"); return -1; } @@ -894,7 +894,7 @@ static int read_rela_reloc(struct section *sec, int i, struct reloc *reloc, unsi static int read_relocs(struct elf *elf) { unsigned long nr_reloc, max_reloc = 0, tot_reloc = 0; - struct section *sec; + struct section *rsec; struct reloc *reloc; unsigned int symndx; struct symbol *sym; @@ -903,51 +903,52 @@ static int read_relocs(struct elf *elf) if (!elf_alloc_hash(reloc, elf->text_size / 16)) return -1; - list_for_each_entry(sec, &elf->sections, list) { - if ((sec->sh.sh_type != SHT_RELA) && - (sec->sh.sh_type != SHT_REL)) + list_for_each_entry(rsec, &elf->sections, list) { + if ((rsec->sh.sh_type != SHT_RELA) && + (rsec->sh.sh_type != SHT_REL)) continue; - sec->base = find_section_by_index(elf, sec->sh.sh_info); - if (!sec->base) { + rsec->base = find_section_by_index(elf, rsec->sh.sh_info); + if (!rsec->base) { WARN("can't find base section for reloc section %s", - sec->name); + rsec->name); return -1; } - sec->base->reloc = sec; + rsec->base->rsec = rsec; nr_reloc = 0; - sec->reloc_data = calloc(sec->sh.sh_size / sec->sh.sh_entsize, sizeof(*reloc)); - if (!sec->reloc_data) { + rsec->reloc_data = calloc(rsec->sh.sh_size / rsec->sh.sh_entsize, + sizeof(*reloc)); + if (!rsec->reloc_data) { perror("calloc"); return -1; } - for (i = 0; i < sec->sh.sh_size / sec->sh.sh_entsize; i++) { - reloc = &sec->reloc_data[i]; - switch (sec->sh.sh_type) { + for (i = 0; i < rsec->sh.sh_size / rsec->sh.sh_entsize; i++) { + reloc = &rsec->reloc_data[i]; + switch (rsec->sh.sh_type) { case SHT_REL: - if (read_rel_reloc(sec, i, reloc, &symndx)) + if (read_rel_reloc(rsec, i, reloc, &symndx)) return -1; break; case SHT_RELA: - if (read_rela_reloc(sec, i, reloc, &symndx)) + if (read_rela_reloc(rsec, i, reloc, &symndx)) return -1; break; default: return -1; } - reloc->sec = sec; + reloc->sec = rsec; reloc->idx = i; reloc->sym = sym = find_symbol_by_index(elf, symndx); if (!reloc->sym) { WARN("can't find reloc entry symbol %d for %s", - symndx, sec->name); + symndx, rsec->name); return -1; } list_add_tail(&reloc->sym_reloc_entry, &sym->reloc_list); - list_add_tail(&reloc->list, &sec->reloc_list); + list_add_tail(&reloc->list, &rsec->reloc_list); elf_hash_add(reloc, &reloc->hash, reloc_hash(reloc)); nr_reloc++; @@ -1140,40 +1141,41 @@ struct section *elf_create_section(struct elf *elf, const char *name, return sec; } -static struct section *elf_create_rel_reloc_section(struct elf *elf, struct section *base) +static struct section *elf_create_rel_reloc_section(struct elf *elf, + struct section *sec) { char *relocname; - struct section *sec; + struct section *rsec; - relocname = malloc(strlen(base->name) + strlen(".rel") + 1); + relocname = malloc(strlen(sec->name) + strlen(".rel") + 1); if (!relocname) { perror("malloc"); return NULL; } strcpy(relocname, ".rel"); - strcat(relocname, base->name); + strcat(relocname, sec->name); - sec = elf_create_section(elf, relocname, sizeof(GElf_Rel), 0); + rsec = elf_create_section(elf, relocname, sizeof(GElf_Rel), 0); free(relocname); - if (!sec) + if (!rsec) return NULL; - base->reloc = sec; - sec->base = base; + sec->rsec = rsec; + rsec->base = sec; - sec->sh.sh_type = SHT_REL; - sec->sh.sh_addralign = 8; - sec->sh.sh_link = find_section_by_name(elf, ".symtab")->idx; - sec->sh.sh_info = base->idx; - sec->sh.sh_flags = SHF_INFO_LINK; + rsec->sh.sh_type = SHT_REL; + rsec->sh.sh_addralign = 8; + rsec->sh.sh_link = find_section_by_name(elf, ".symtab")->idx; + rsec->sh.sh_info = sec->idx; + rsec->sh.sh_flags = SHF_INFO_LINK; - return sec; + return rsec; } static struct section *elf_create_rela_reloc_section(struct elf *elf, struct section *base) { char *relocname; - struct section *sec; + struct section *rsec; int addrsize = elf_class_addrsize(elf); relocname = malloc(strlen(base->name) + strlen(".rela") + 1); @@ -1185,23 +1187,23 @@ static struct section *elf_create_rela_reloc_section(struct elf *elf, struct sec strcat(relocname, base->name); if (addrsize == sizeof(u32)) - sec = elf_create_section(elf, relocname, sizeof(Elf32_Rela), 0); + rsec = elf_create_section(elf, relocname, sizeof(Elf32_Rela), 0); else - sec = elf_create_section(elf, relocname, sizeof(GElf_Rela), 0); + rsec = elf_create_section(elf, relocname, sizeof(GElf_Rela), 0); free(relocname); - if (!sec) + if (!rsec) return NULL; - base->reloc = sec; - sec->base = base; + base->rsec = rsec; + rsec->base = base; - sec->sh.sh_type = SHT_RELA; - sec->sh.sh_addralign = addrsize; - sec->sh.sh_link = find_section_by_name(elf, ".symtab")->idx; - sec->sh.sh_info = base->idx; - sec->sh.sh_flags = SHF_INFO_LINK; + rsec->sh.sh_type = SHT_RELA; + rsec->sh.sh_addralign = addrsize; + rsec->sh.sh_link = find_section_by_name(elf, ".symtab")->idx; + rsec->sh.sh_info = base->idx; + rsec->sh.sh_flags = SHF_INFO_LINK; - return sec; + return rsec; } static struct section *elf_create_reloc_section(struct elf *elf, @@ -1215,28 +1217,28 @@ static struct section *elf_create_reloc_section(struct elf *elf, } } -static int elf_rebuild_rel_reloc_section(struct section *sec) +static int elf_rebuild_rel_reloc_section(struct section *rsec) { struct reloc *reloc; int idx = 0; void *buf; /* Allocate a buffer for relocations */ - buf = malloc(sec->sh.sh_size); + buf = malloc(rsec->sh.sh_size); if (!buf) { perror("malloc"); return -1; } - sec->data->d_buf = buf; - sec->data->d_size = sec->sh.sh_size; - sec->data->d_type = ELF_T_REL; + rsec->data->d_buf = buf; + rsec->data->d_size = rsec->sh.sh_size; + rsec->data->d_type = ELF_T_REL; idx = 0; - list_for_each_entry(reloc, &sec->reloc_list, list) { + list_for_each_entry(reloc, &rsec->reloc_list, list) { reloc->rel.r_offset = reloc->offset; reloc->rel.r_info = GELF_R_INFO(reloc->sym->idx, reloc->type); - if (!gelf_update_rel(sec->data, idx, &reloc->rel)) { + if (!gelf_update_rel(rsec->data, idx, &reloc->rel)) { WARN_ELF("gelf_update_rel"); return -1; } @@ -1246,29 +1248,29 @@ static int elf_rebuild_rel_reloc_section(struct section *sec) return 0; } -static int elf_rebuild_rela_reloc_section(struct section *sec) +static int elf_rebuild_rela_reloc_section(struct section *rsec) { struct reloc *reloc; int idx = 0; void *buf; /* Allocate a buffer for relocations with addends */ - buf = malloc(sec->sh.sh_size); + buf = malloc(rsec->sh.sh_size); if (!buf) { perror("malloc"); return -1; } - sec->data->d_buf = buf; - sec->data->d_size = sec->sh.sh_size; - sec->data->d_type = ELF_T_RELA; + rsec->data->d_buf = buf; + rsec->data->d_size = rsec->sh.sh_size; + rsec->data->d_type = ELF_T_RELA; idx = 0; - list_for_each_entry(reloc, &sec->reloc_list, list) { + list_for_each_entry(reloc, &rsec->reloc_list, list) { reloc->rela.r_offset = reloc->offset; reloc->rela.r_addend = reloc->addend; reloc->rela.r_info = GELF_R_INFO(reloc->sym->idx, reloc->type); - if (!gelf_update_rela(sec->data, idx, &reloc->rela)) { + if (!gelf_update_rela(rsec->data, idx, &reloc->rela)) { WARN_ELF("gelf_update_rela"); return -1; } @@ -1278,11 +1280,11 @@ static int elf_rebuild_rela_reloc_section(struct section *sec) return 0; } -static int elf_rebuild_reloc_section(struct elf