diff options
author | Josh Poimboeuf <jpoimboe@redhat.com> | 2016-04-15 16:17:10 +0200 |
---|---|---|
committer | Ingo Molnar <mingo@kernel.org> | 2016-04-16 11:14:17 +0200 |
commit | b1547d3101e74e809b9790174b27f1080747b009 (patch) | |
tree | f5eeb80c700d7dcaf512338063a07751d79d281d | |
parent | objtool: Add workaround for GCC switch jump table bug (diff) | |
download | linux-b1547d3101e74e809b9790174b27f1080747b009.tar.xz linux-b1547d3101e74e809b9790174b27f1080747b009.zip |
objtool: Detect falling through to the next function
There are several cases in compiled C code where a function may not
return at the end, and may instead fall through to the next function.
That may indicate a bug in the code, or a gcc bug, or even an objtool
bug. But in each case, objtool reports an unhelpful warning, something
like:
drivers/scsi/qla2xxx/qla_attr.o: warning: objtool: qla2x00_get_fc_host_stats()+0x0: duplicate frame pointer save
drivers/scsi/qla2xxx/qla_attr.o: warning: objtool: qla2x00_get_fc_host_stats()+0x0: frame pointer state mismatch
Detect this situation and print a more useful error message:
drivers/scsi/qla2xxx/qla_attr.o: warning: objtool: qla2x00_get_host_fabric_name() falls through to next function qla2x00_get_starget_node_name()
Also add some information about this warning and its potential causes to
the documentation.
Reported-by: kbuild test robot <fengguang.wu@intel.com>
Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Cc: Andy Lutomirski <luto@amacapital.net>
Cc: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Borislav Petkov <bp@alien8.de>
Cc: Brian Gerst <brgerst@gmail.com>
Cc: Denys Vlasenko <dvlasenk@redhat.com>
Cc: H. Peter Anvin <hpa@zytor.com>
Cc: Jiri Olsa <jolsa@redhat.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Link: http://lkml.kernel.org/r/caa4ec6c687931db805e692d4e4bf06cd87d33e6.1460729697.git.jpoimboe@redhat.com
Signed-off-by: Ingo Molnar <mingo@kernel.org>
-rw-r--r-- | tools/objtool/Documentation/stack-validation.txt | 38 | ||||
-rw-r--r-- | tools/objtool/builtin-check.c | 46 |
2 files changed, 61 insertions, 23 deletions
diff --git a/tools/objtool/Documentation/stack-validation.txt b/tools/objtool/Documentation/stack-validation.txt index 5a95896105bc..55a60d331f47 100644 --- a/tools/objtool/Documentation/stack-validation.txt +++ b/tools/objtool/Documentation/stack-validation.txt @@ -299,18 +299,38 @@ they mean, and suggestions for how to fix them. Errors in .c files ------------------ -If you're getting an objtool error in a compiled .c file, chances are -the file uses an asm() statement which has a "call" instruction. An -asm() statement with a call instruction must declare the use of the -stack pointer in its output operand. For example, on x86_64: +1. c_file.o: warning: objtool: funcA() falls through to next function funcB() - register void *__sp asm("rsp"); - asm volatile("call func" : "+r" (__sp)); + This means that funcA() doesn't end with a return instruction or an + unconditional jump, and that objtool has determined that the function + can fall through into the next function. There could be different + reasons for this: -Otherwise the stack frame may not get created before the call. + 1) 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. -Another possible cause for errors in C code is if the Makefile removes --fno-omit-frame-pointer or adds -fomit-frame-pointer to the gcc options. + 2) funcA() uses the unreachable() annotation in a section of code + that is actually reachable. + + 3) If funcA() calls an inline function, the object code for funcA() + might be corrupt due to a gcc bug. For more details, see: + https://gcc.gnu.org/bugzilla/show_bug.cgi?id=70646 + +2. If you're getting any other objtool error in a compiled .c file, it + may be because the file uses an asm() statement which has a "call" + instruction. An asm() statement with a call instruction must declare + the use of the stack pointer in its output operand. For example, on + x86_64: + + register void *__sp asm("rsp"); + asm volatile("call func" : "+r" (__sp)); + + Otherwise the stack frame may not get created before the call. + +3. Another possible cause for errors in C code is if the Makefile removes + -fno-omit-frame-pointer or adds -fomit-frame-pointer to the gcc options. Also see the above section for .S file errors for more information what the individual error messages mean. diff --git a/tools/objtool/builtin-check.c b/tools/objtool/builtin-check.c index 157a0f96d64d..e8a1e69eb92c 100644 --- a/tools/objtool/builtin-check.c +++ b/tools/objtool/builtin-check.c @@ -54,6 +54,7 @@ struct instruction { struct symbol *call_dest; struct instruction *jump_dest; struct list_head alts; + struct symbol *func; }; struct alternative { @@ -66,7 +67,7 @@ struct objtool_file { struct list_head insn_list; DECLARE_HASHTABLE(insn_hash, 16); struct section *rodata, *whitelist; - bool ignore_unreachables; + bool ignore_unreachables, c_file; }; const char *objname; @@ -229,7 +230,7 @@ static int __dead_end_function(struct objtool_file *file, struct symbol *func, } } - if (insn->type == INSN_JUMP_DYNAMIC) + if (insn->type == INSN_JUMP_DYNAMIC && list_empty(&insn->alts)) /* sibling call */ return 0; } @@ -249,6 +250,7 @@ static int dead_end_function(struct objtool_file *file, struct symbol *func) static int decode_instructions(struct objtool_file *file) { struct section *sec; + struct symbol *func; unsigned long offset; struct instruction *insn; int ret; @@ -282,6 +284,21 @@ static int decode_instructions(struct objtool_file *file) hash_add(file->insn_hash, &insn->hash, insn->offset); list_add_tail(&insn->list, &file->insn_list); } + + list_for_each_entry(func, &sec->symbol_list, list) { + if (func->type != STT_FUNC) + continue; + + if (!find_insn(file, sec, func->offset)) { + WARN("%s(): can't find starting instruction", + func->name); + return -1; + } + + func_for_each_insn(file, func, insn) + if (!insn->func) + insn->func = func; + } } return 0; @@ -824,6 +841,7 @@ static int validate_branch(struct objtool_file *file, struct alternative *alt; struct instruction *insn; struct section *sec; + struct symbol *func = NULL; unsigned char state; int ret; @@ -838,6 +856,16 @@ static int validate_branch(struct objtool_file *file, } while (1) { + if (file->c_file && insn->func) { + if (func && func != insn->func) { + WARN("%s() falls through to next function %s()", + func->name, insn->func->name); + return 1; + } + + func = insn->func; + } + if (insn->visited) { if (frame_state(insn->state) != frame_state(state)) { WARN_FUNC("frame pointer state mismatch", @@ -848,13 +876,6 @@ static int validate_branch(struct objtool_file *file, return 0; } - /* - * Catch a rare case where a noreturn function falls through to - * the next function. - */ - if (is_fentry_call(insn) && (state & STATE_FENTRY)) - return 0; - insn->visited = true; insn->state = state; @@ -1060,12 +1081,8 @@ static int validate_functions(struct objtool_file *file) continue; insn = find_insn(file, sec, func->offset); - if (!insn) { - WARN("%s(): can't find starting instruction", - func->name); - warnings++; + if (!insn) continue; - } ret = validate_branch(file, insn, 0); warnings += ret; @@ -1162,6 +1179,7 @@ int cmd_check(int argc, const char **argv) file.whitelist = find_section_by_name(file.elf, "__func_stack_frame_non_standard"); file.rodata = find_section_by_name(file.elf, ".rodata"); file.ignore_unreachables = false; + file.c_file = find_section_by_name(file.elf, ".comment"); ret = decode_sections(&file); if (ret < 0) |