diff options
author | Josh Poimboeuf <jpoimboe@redhat.com> | 2016-04-15 10:17:10 -0400 |
---|---|---|
committer | Ingo Molnar <mingo@kernel.org> | 2016-04-16 05:14:17 -0400 |
commit | b1547d3101e74e809b9790174b27f1080747b009 (patch) | |
tree | f5eeb80c700d7dcaf512338063a07751d79d281d /tools | |
parent | 7e578441a4a3bba2a79426ca0f709c801210d08e (diff) |
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>
Diffstat (limited to 'tools')
-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. | |||
299 | Errors in .c files | 299 | Errors in .c files |
300 | ------------------ | 300 | ------------------ |
301 | 301 | ||
302 | If you're getting an objtool error in a compiled .c file, chances are | 302 | 1. c_file.o: warning: objtool: funcA() falls through to next function funcB() |
303 | the file uses an asm() statement which has a "call" instruction. An | ||
304 | asm() statement with a call instruction must declare the use of the | ||
305 | stack pointer in its output operand. For example, on x86_64: | ||
306 | 303 | ||
307 | register void *__sp asm("rsp"); | 304 | This means that funcA() doesn't end with a return instruction or an |
308 | asm volatile("call func" : "+r" (__sp)); | 305 | unconditional jump, and that objtool has determined that the function |
306 | can fall through into the next function. There could be different | ||
307 | reasons for this: | ||
309 | 308 | ||
310 | Otherwise the stack frame may not get created before the call. | 309 | 1) funcA()'s last instruction is a call to a "noreturn" function like |
310 | panic(). In this case the noreturn function needs to be added to | ||
311 | objtool's hard-coded global_noreturns array. Feel free to bug the | ||
312 | objtool maintainer, or you can submit a patch. | ||
311 | 313 | ||
312 | Another possible cause for errors in C code is if the Makefile removes | 314 | 2) funcA() uses the unreachable() annotation in a section of code |
313 | -fno-omit-frame-pointer or adds -fomit-frame-pointer to the gcc options. | 315 | that is actually reachable. |
316 | |||
317 | 3) If funcA() calls an inline function, the object code for funcA() | ||
318 | might be corrupt due to a gcc bug. For more details, see: | ||
319 | https://gcc.gnu.org/bugzilla/show_bug.cgi?id=70646 | ||
320 | |||
321 | 2. If you're getting any other objtool error in a compiled .c file, it | ||
322 | may be because the file uses an asm() statement which has a "call" | ||
323 | instruction. An asm() statement with a call instruction must declare | ||
324 | the use of the stack pointer in its output operand. For example, on | ||
325 | x86_64: | ||
326 | |||
327 | register void *__sp asm("rsp"); | ||
328 | asm volatile("call func" : "+r" (__sp)); | ||
329 | |||
330 | Otherwise the stack frame may not get created before the call. | ||
331 | |||
332 | 3. Another possible cause for errors in C code is if the Makefile removes | ||
333 | -fno-omit-frame-pointer or adds -fomit-frame-pointer to the gcc options. | ||
314 | 334 | ||
315 | Also see the above section for .S file errors for more information what | 335 | Also see the above section for .S file errors for more information what |
316 | the individual error messages mean. | 336 | 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 { | |||
54 | struct symbol *call_dest; | 54 | struct symbol *call_dest; |
55 | struct instruction *jump_dest; | 55 | struct instruction *jump_dest; |
56 | struct list_head alts; | 56 | struct list_head alts; |
57 | struct symbol *func; | ||
57 | }; | 58 | }; |
58 | 59 | ||
59 | struct alternative { | 60 | struct alternative { |
@@ -66,7 +67,7 @@ struct objtool_file { | |||
66 | struct list_head insn_list; | 67 | struct list_head insn_list; |
67 | DECLARE_HASHTABLE(insn_hash, 16); | 68 | DECLARE_HASHTABLE(insn_hash, 16); |
68 | struct section *rodata, *whitelist; | 69 | struct section *rodata, *whitelist; |
69 | bool ignore_unreachables; | 70 | bool ignore_unreachables, c_file; |
70 | }; | 71 | }; |
71 | 72 | ||
72 | const char *objname; | 73 | const char *objname; |
@@ -229,7 +230,7 @@ static int __dead_end_function(struct objtool_file *file, struct symbol *func, | |||
229 | } | 230 | } |
230 | } | 231 | } |
231 | 232 | ||
232 | if (insn->type == INSN_JUMP_DYNAMIC) | 233 | if (insn->type == INSN_JUMP_DYNAMIC && list_empty(&insn->alts)) |
233 | /* sibling call */ | 234 | /* sibling call */ |
234 | return 0; | 235 | return 0; |
235 | } | 236 | } |
@@ -249,6 +250,7 @@ static int dead_end_function(struct objtool_file *file, struct symbol *func) | |||
249 | static int decode_instructions(struct objtool_file *file) | 250 | static int decode_instructions(struct objtool_file *file) |
250 | { | 251 | { |
251 | struct section *sec; | 252 | struct section *sec; |
253 | struct symbol *func; | ||
252 | unsigned long offset; | 254 | unsigned long offset; |
253 | struct instruction *insn; | 255 | struct instruction *insn; |
254 | int ret; | 256 | int ret; |
@@ -282,6 +284,21 @@ static int decode_instructions(struct objtool_file *file) | |||
282 | hash_add(file->insn_hash, &insn->hash, insn->offset); | 284 | hash_add(file->insn_hash, &insn->hash, insn->offset); |
283 | list_add_tail(&insn->list, &file->insn_list); | 285 | list_add_tail(&insn->list, &file->insn_list); |
284 | } | 286 | } |
287 | |||
288 | list_for_each_entry(func, &sec->symbol_list, list) { | ||
289 | if (func->type != STT_FUNC) | ||
290 | continue; | ||
291 | |||
292 | if (!find_insn(file, sec, func->offset)) { | ||
293 | WARN("%s(): can't find starting instruction", | ||
294 | func->name); | ||
295 | return -1; | ||
296 | } | ||
297 | |||
298 | func_for_each_insn(file, func, insn) | ||
299 | if (!insn->func) | ||
300 | insn->func = func; | ||
301 | } | ||
285 | } | 302 | } |
286 | 303 | ||
287 | return 0; | 304 | return 0; |
@@ -824,6 +841,7 @@ static int validate_branch(struct objtool_file *file, | |||
824 | struct alternative *alt; | 841 | struct alternative *alt; |
825 | struct instruction *insn; | 842 | struct instruction *insn; |
826 | struct section *sec; | 843 | struct section *sec; |
844 | struct symbol *func = NULL; | ||
827 | unsigned char state; | 845 | unsigned char state; |
828 | int ret; | 846 | int ret; |
829 | 847 | ||
@@ -838,6 +856,16 @@ static int validate_branch(struct objtool_file *file, | |||
838 | } | 856 | } |
839 | 857 | ||
840 | while (1) { | 858 | while (1) { |
859 | if (file->c_file && insn->func) { | ||
860 | if (func && func != insn->func) { | ||
861 | WARN("%s() falls through to next function %s()", | ||
862 | func->name, insn->func->name); | ||
863 | return 1; | ||
864 | } | ||
865 | |||
866 | func = insn->func; | ||
867 | } | ||
868 | |||
841 | if (insn->visited) { | 869 | if (insn->visited) { |
842 | if (frame_state(insn->state) != frame_state(state)) { | 870 | if (frame_state(insn->state) != frame_state(state)) { |
843 | WARN_FUNC("frame pointer state mismatch", | 871 | WARN_FUNC("frame pointer state mismatch", |
@@ -848,13 +876,6 @@ static int validate_branch(struct objtool_file *file, | |||
848 | return 0; | 876 | return 0; |
849 | } | 877 | } |
850 | 878 | ||
851 | /* | ||
852 | * Catch a rare case where a noreturn function falls through to | ||
853 | * the next function. | ||
854 | */ | ||
855 | if (is_fentry_call(insn) && (state & STATE_FENTRY)) | ||
856 | return 0; | ||
857 | |||
858 | insn->visited = true; | 879 | insn->visited = true; |
859 | insn->state = state; | 880 | insn->state = state; |
860 | 881 | ||
@@ -1060,12 +1081,8 @@ static int validate_functions(struct objtool_file *file) | |||
1060 | continue; | 1081 | continue; |
1061 | 1082 | ||
1062 | insn = find_insn(file, sec, func->offset); | 1083 | insn = find_insn(file, sec, func->offset); |
1063 | if (!insn) { | 1084 | if (!insn) |
1064 | WARN("%s(): can't find starting instruction", | ||
1065 | func->name); | ||
1066 | warnings++; | ||
1067 | continue; | 1085 | continue; |
1068 | } | ||
1069 | 1086 | ||
1070 | ret = validate_branch(file, insn, 0); | 1087 | ret = validate_branch(file, insn, 0); |
1071 | warnings += ret; | 1088 | warnings += ret; |
@@ -1162,6 +1179,7 @@ int cmd_check(int argc, const char **argv) | |||
1162 | file.whitelist = find_section_by_name(file.elf, "__func_stack_frame_non_standard"); | 1179 | file.whitelist = find_section_by_name(file.elf, "__func_stack_frame_non_standard"); |
1163 | file.rodata = find_section_by_name(file.elf, ".rodata"); | 1180 | file.rodata = find_section_by_name(file.elf, ".rodata"); |
1164 | file.ignore_unreachables = false; | 1181 | file.ignore_unreachables = false; |
1182 | file.c_file = find_section_by_name(file.elf, ".comment"); | ||
1165 | 1183 | ||
1166 | ret = decode_sections(&file); | 1184 | ret = decode_sections(&file); |
1167 | if (ret < 0) | 1185 | if (ret < 0) |