objtool: Add --backtrace support

For when you want to know the path that reached your fail state:

  $ ./objtool check --no-fp --backtrace arch/x86/lib/usercopy_64.o
  arch/x86/lib/usercopy_64.o: warning: objtool: .altinstr_replacement+0x3: UACCESS disable without MEMOPs: __clear_user()
  arch/x86/lib/usercopy_64.o: warning: objtool:   __clear_user()+0x3a: (alt)
  arch/x86/lib/usercopy_64.o: warning: objtool:   __clear_user()+0x2e: (branch)
  arch/x86/lib/usercopy_64.o: warning: objtool:   __clear_user()+0x18: (branch)
  arch/x86/lib/usercopy_64.o: warning: objtool:   .altinstr_replacement+0xffffffffffffffff: (branch)
  arch/x86/lib/usercopy_64.o: warning: objtool:   __clear_user()+0x5: (alt)
  arch/x86/lib/usercopy_64.o: warning: objtool:   __clear_user()+0x0: <=== (func)

  0000000000000000 <__clear_user>:
    0:   e8 00 00 00 00          callq  5 <__clear_user+0x5>
                 1: R_X86_64_PLT32       __fentry__-0x4
    5:   90                      nop
    6:   90                      nop
    7:   90                      nop
    8:   48 89 f0                mov    %rsi,%rax
    b:   48 c1 ee 03             shr    $0x3,%rsi
    f:   83 e0 07                and    $0x7,%eax
   12:   48 89 f1                mov    %rsi,%rcx
   15:   48 85 c9                test   %rcx,%rcx
   18:   74 0f                   je     29 <__clear_user+0x29>
   1a:   48 c7 07 00 00 00 00    movq   $0x0,(%rdi)
   21:   48 83 c7 08             add    $0x8,%rdi
   25:   ff c9                   dec    %ecx
   27:   75 f1                   jne    1a <__clear_user+0x1a>
   29:   48 89 c1                mov    %rax,%rcx
   2c:   85 c9                   test   %ecx,%ecx
   2e:   74 0a                   je     3a <__clear_user+0x3a>
   30:   c6 07 00                movb   $0x0,(%rdi)
   33:   48 ff c7                inc    %rdi
   36:   ff c9                   dec    %ecx
   38:   75 f6                   jne    30 <__clear_user+0x30>
   3a:   90                      nop
   3b:   90                      nop
   3c:   90                      nop
   3d:   48 89 c8                mov    %rcx,%rax
   40:   c3                      retq

Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Acked-by: Josh Poimboeuf <jpoimboe@redhat.com>
Cc: Borislav Petkov <bp@alien8.de>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Ingo Molnar <mingo@kernel.org>
This commit is contained in:
Peter Zijlstra 2019-03-01 11:15:49 +01:00 committed by Ingo Molnar
parent aaf5c623b9
commit 7697eee3dd
4 changed files with 25 additions and 6 deletions

View File

@ -29,7 +29,7 @@
#include "builtin.h" #include "builtin.h"
#include "check.h" #include "check.h"
bool no_fp, no_unreachable, retpoline, module; bool no_fp, no_unreachable, retpoline, module, backtrace;
static const char * const check_usage[] = { static const char * const check_usage[] = {
"objtool check [<options>] file.o", "objtool check [<options>] file.o",
@ -41,6 +41,7 @@ const struct option check_options[] = {
OPT_BOOLEAN('u', "no-unreachable", &no_unreachable, "Skip 'unreachable instruction' warnings"), OPT_BOOLEAN('u', "no-unreachable", &no_unreachable, "Skip 'unreachable instruction' warnings"),
OPT_BOOLEAN('r', "retpoline", &retpoline, "Validate retpoline assumptions"), OPT_BOOLEAN('r', "retpoline", &retpoline, "Validate retpoline assumptions"),
OPT_BOOLEAN('m', "module", &module, "Indicates the object will be part of a kernel module"), OPT_BOOLEAN('m', "module", &module, "Indicates the object will be part of a kernel module"),
OPT_BOOLEAN('b', "backtrace", &backtrace, "unwind on error"),
OPT_END(), OPT_END(),
}; };

View File

@ -20,7 +20,7 @@
#include <subcmd/parse-options.h> #include <subcmd/parse-options.h>
extern const struct option check_options[]; extern const struct option check_options[];
extern bool no_fp, no_unreachable, retpoline, module; extern bool no_fp, no_unreachable, retpoline, module, backtrace;
extern int cmd_check(int argc, const char **argv); extern int cmd_check(int argc, const char **argv);
extern int cmd_orc(int argc, const char **argv); extern int cmd_orc(int argc, const char **argv);

View File

@ -1885,8 +1885,11 @@ static int validate_branch(struct objtool_file *file, struct instruction *first,
if (!insn->ignore_alts) { if (!insn->ignore_alts) {
list_for_each_entry(alt, &insn->alts, list) { list_for_each_entry(alt, &insn->alts, list) {
ret = validate_branch(file, alt->insn, state); ret = validate_branch(file, alt->insn, state);
if (ret) if (ret) {
return 1; if (backtrace)
BT_FUNC("(alt)", insn);
return ret;
}
} }
} }
@ -1933,8 +1936,11 @@ static int validate_branch(struct objtool_file *file, struct instruction *first,
insn->jump_dest->func->pfunc == func)) { insn->jump_dest->func->pfunc == func)) {
ret = validate_branch(file, insn->jump_dest, ret = validate_branch(file, insn->jump_dest,
state); state);
if (ret) if (ret) {
return 1; if (backtrace)
BT_FUNC("(branch)", insn);
return ret;
}
} else if (func && has_modified_stack_frame(&state)) { } else if (func && has_modified_stack_frame(&state)) {
WARN_FUNC("sibling call from callable instruction with modified stack frame", WARN_FUNC("sibling call from callable instruction with modified stack frame",
@ -2005,6 +2011,8 @@ static int validate_unwind_hints(struct objtool_file *file)
for_each_insn(file, insn) { for_each_insn(file, insn) {
if (insn->hint && !insn->visited) { if (insn->hint && !insn->visited) {
ret = validate_branch(file, insn, state); ret = validate_branch(file, insn, state);
if (ret && backtrace)
BT_FUNC("<=== (hint)", insn);
warnings += ret; warnings += ret;
} }
} }
@ -2133,6 +2141,8 @@ static int validate_functions(struct objtool_file *file)
continue; continue;
ret = validate_branch(file, insn, state); ret = validate_branch(file, insn, state);
if (ret && backtrace)
BT_FUNC("<=== (func)", insn);
warnings += ret; warnings += ret;
} }
} }

View File

@ -64,6 +64,14 @@ static inline char *offstr(struct section *sec, unsigned long offset)
free(_str); \ free(_str); \
}) })
#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 WARN_ELF(format, ...) \ #define WARN_ELF(format, ...) \
WARN(format ": %s", ##__VA_ARGS__, elf_errmsg(-1)) WARN(format ": %s", ##__VA_ARGS__, elf_errmsg(-1))