Commit 22922dea authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'objtool-core-2022-05-23' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip

Pull objtool updates from Ingo Molnar:

 - Comprehensive interface overhaul:
   =================================

   Objtool's interface has some issues:

     - Several features are done unconditionally, without any way to
       turn them off. Some of them might be surprising. This makes
       objtool tricky to use, and prevents porting individual features
       to other arches.

     - The config dependencies are too coarse-grained. Objtool
       enablement is tied to CONFIG_STACK_VALIDATION, but it has several
       other features independent of that.

     - The objtool subcmds ("check" and "orc") are clumsy: "check" is
       really a subset of "orc", so it has all the same options.

       The subcmd model has never really worked for objtool, as it only
       has a single purpose: "do some combination of things on an object
       file".

     - The '--lto' and '--vmlinux' options are nonsensical and have
       surprising behavior.

   Overhaul the interface:

      - get rid of subcmds

      - make all features individually selectable

      - remove and/or clarify confusing/obsolete options

      - update the documentation

      - fix some bugs found along the way

 - Fix x32 regression

 - Fix Kbuild cleanup bugs

 - Add scripts/objdump-func helper script to disassemble a single
   function from an object file.

 - Rewrite scripts/faddr2line to be section-aware, by basing it on
   'readelf', moving it away from 'nm', which doesn't handle multiple
   sections well, which can result in decoding failure.

 - Rewrite & fix symbol handling - which had a number of bugs wrt.
   object files that don't have global symbols - which is rare but
   possible. Also fix a bunch of symbol handling bugs found along the
   way.

* tag 'objtool-core-2022-05-23' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: (23 commits)
  objtool: Fix objtool regression on x32 systems
  objtool: Fix symbol creation
  scripts/faddr2line: Fix overlapping text section failures
  scripts: Create objdump-func helper script
  objtool: Remove libsubcmd.a when make clean
  objtool: Remove inat-tables.c when make clean
  objtool: Update documentation
  objtool: Remove --lto and --vmlinux in favor of --link
  objtool: Add HAVE_NOINSTR_VALIDATION
  objtool: Rename "VMLINUX_VALIDATION" -> "NOINSTR_VALIDATION"
  objtool: Make noinstr hacks optional
  objtool: Make jump label hack optional
  objtool: Make static call annotation optional
  objtool: Make stack validation frame-pointer-specific
  objtool: Add CONFIG_OBJTOOL
  objtool: Extricate sls from stack validation
  objtool: Rework ibt and extricate from stack validation
  objtool: Make stack validation optional
  objtool: Add option to print section addresses
  objtool: Don't print parentheses in function addresses
  ...
parents 2319be13 22682a07
...@@ -1302,7 +1302,7 @@ install: sub_make_done := ...@@ -1302,7 +1302,7 @@ install: sub_make_done :=
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
# Tools # Tools
ifdef CONFIG_STACK_VALIDATION ifdef CONFIG_OBJTOOL
prepare: tools/objtool prepare: tools/objtool
endif endif
......
...@@ -54,6 +54,7 @@ config JUMP_LABEL ...@@ -54,6 +54,7 @@ config JUMP_LABEL
bool "Optimize very unlikely/likely branches" bool "Optimize very unlikely/likely branches"
depends on HAVE_ARCH_JUMP_LABEL depends on HAVE_ARCH_JUMP_LABEL
depends on CC_HAS_ASM_GOTO depends on CC_HAS_ASM_GOTO
select OBJTOOL if HAVE_JUMP_LABEL_HACK
help help
This option enables a transparent branch optimization that This option enables a transparent branch optimization that
makes certain almost-always-true or almost-always-false branch makes certain almost-always-true or almost-always-false branch
...@@ -1034,11 +1035,23 @@ config ARCH_WANT_DEFAULT_TOPDOWN_MMAP_LAYOUT ...@@ -1034,11 +1035,23 @@ config ARCH_WANT_DEFAULT_TOPDOWN_MMAP_LAYOUT
depends on MMU depends on MMU
select ARCH_HAS_ELF_RANDOMIZE select ARCH_HAS_ELF_RANDOMIZE
config HAVE_OBJTOOL
bool
config HAVE_JUMP_LABEL_HACK
bool
config HAVE_NOINSTR_HACK
bool
config HAVE_NOINSTR_VALIDATION
bool
config HAVE_STACK_VALIDATION config HAVE_STACK_VALIDATION
bool bool
help help
Architecture supports the 'objtool check' host tool command, which Architecture supports objtool compile-time frame pointer rule
performs compile-time stack metadata validation. validation.
config HAVE_RELIABLE_STACKTRACE config HAVE_RELIABLE_STACKTRACE
bool bool
...@@ -1308,6 +1321,7 @@ config HAVE_STATIC_CALL ...@@ -1308,6 +1321,7 @@ config HAVE_STATIC_CALL
config HAVE_STATIC_CALL_INLINE config HAVE_STATIC_CALL_INLINE
bool bool
depends on HAVE_STATIC_CALL depends on HAVE_STATIC_CALL
select OBJTOOL
config HAVE_PREEMPT_DYNAMIC config HAVE_PREEMPT_DYNAMIC
bool bool
......
...@@ -188,7 +188,7 @@ config X86 ...@@ -188,7 +188,7 @@ config X86
select HAVE_CONTEXT_TRACKING if X86_64 select HAVE_CONTEXT_TRACKING if X86_64
select HAVE_CONTEXT_TRACKING_OFFSTACK if HAVE_CONTEXT_TRACKING select HAVE_CONTEXT_TRACKING_OFFSTACK if HAVE_CONTEXT_TRACKING
select HAVE_C_RECORDMCOUNT select HAVE_C_RECORDMCOUNT
select HAVE_OBJTOOL_MCOUNT if STACK_VALIDATION select HAVE_OBJTOOL_MCOUNT if HAVE_OBJTOOL
select HAVE_BUILDTIME_MCOUNT_SORT select HAVE_BUILDTIME_MCOUNT_SORT
select HAVE_DEBUG_KMEMLEAK select HAVE_DEBUG_KMEMLEAK
select HAVE_DMA_CONTIGUOUS select HAVE_DMA_CONTIGUOUS
...@@ -212,6 +212,7 @@ config X86 ...@@ -212,6 +212,7 @@ config X86
select HAVE_IOREMAP_PROT select HAVE_IOREMAP_PROT
select HAVE_IRQ_EXIT_ON_IRQ_STACK if X86_64 select HAVE_IRQ_EXIT_ON_IRQ_STACK if X86_64
select HAVE_IRQ_TIME_ACCOUNTING select HAVE_IRQ_TIME_ACCOUNTING
select HAVE_JUMP_LABEL_HACK if HAVE_OBJTOOL
select HAVE_KERNEL_BZIP2 select HAVE_KERNEL_BZIP2
select HAVE_KERNEL_GZIP select HAVE_KERNEL_GZIP
select HAVE_KERNEL_LZ4 select HAVE_KERNEL_LZ4
...@@ -230,7 +231,10 @@ config X86 ...@@ -230,7 +231,10 @@ config X86
select HAVE_MOD_ARCH_SPECIFIC select HAVE_MOD_ARCH_SPECIFIC
select HAVE_MOVE_PMD select HAVE_MOVE_PMD
select HAVE_MOVE_PUD select HAVE_MOVE_PUD
select HAVE_NOINSTR_HACK if HAVE_OBJTOOL
select HAVE_NMI select HAVE_NMI
select HAVE_NOINSTR_VALIDATION if HAVE_OBJTOOL
select HAVE_OBJTOOL if X86_64
select HAVE_OPTPROBES select HAVE_OPTPROBES
select HAVE_PCSPKR_PLATFORM select HAVE_PCSPKR_PLATFORM
select HAVE_PERF_EVENTS select HAVE_PERF_EVENTS
...@@ -239,17 +243,17 @@ config X86 ...@@ -239,17 +243,17 @@ config X86
select HAVE_PCI select HAVE_PCI
select HAVE_PERF_REGS select HAVE_PERF_REGS
select HAVE_PERF_USER_STACK_DUMP select HAVE_PERF_USER_STACK_DUMP
select MMU_GATHER_RCU_TABLE_FREE if PARAVIRT select MMU_GATHER_RCU_TABLE_FREE if PARAVIRT
select HAVE_POSIX_CPU_TIMERS_TASK_WORK select HAVE_POSIX_CPU_TIMERS_TASK_WORK
select HAVE_REGS_AND_STACK_ACCESS_API select HAVE_REGS_AND_STACK_ACCESS_API
select HAVE_RELIABLE_STACKTRACE if X86_64 && (UNWINDER_FRAME_POINTER || UNWINDER_ORC) && STACK_VALIDATION select HAVE_RELIABLE_STACKTRACE if UNWINDER_ORC || STACK_VALIDATION
select HAVE_FUNCTION_ARG_ACCESS_API select HAVE_FUNCTION_ARG_ACCESS_API
select HAVE_SETUP_PER_CPU_AREA select HAVE_SETUP_PER_CPU_AREA
select HAVE_SOFTIRQ_ON_OWN_STACK select HAVE_SOFTIRQ_ON_OWN_STACK
select HAVE_STACKPROTECTOR if CC_HAS_SANE_STACKPROTECTOR select HAVE_STACKPROTECTOR if CC_HAS_SANE_STACKPROTECTOR
select HAVE_STACK_VALIDATION if X86_64 select HAVE_STACK_VALIDATION if HAVE_OBJTOOL
select HAVE_STATIC_CALL select HAVE_STATIC_CALL
select HAVE_STATIC_CALL_INLINE if HAVE_STACK_VALIDATION select HAVE_STATIC_CALL_INLINE if HAVE_OBJTOOL
select HAVE_PREEMPT_DYNAMIC_CALL select HAVE_PREEMPT_DYNAMIC_CALL
select HAVE_RSEQ select HAVE_RSEQ
select HAVE_SYSCALL_TRACEPOINTS select HAVE_SYSCALL_TRACEPOINTS
...@@ -268,7 +272,6 @@ config X86 ...@@ -268,7 +272,6 @@ config X86
select RTC_MC146818_LIB select RTC_MC146818_LIB
select SPARSE_IRQ select SPARSE_IRQ
select SRCU select SRCU
select STACK_VALIDATION if HAVE_STACK_VALIDATION && (HAVE_STATIC_CALL_INLINE || RETPOLINE)
select SYSCTL_EXCEPTION_TRACE select SYSCTL_EXCEPTION_TRACE
select THREAD_INFO_IN_TASK select THREAD_INFO_IN_TASK
select TRACE_IRQFLAGS_SUPPORT select TRACE_IRQFLAGS_SUPPORT
...@@ -459,6 +462,7 @@ config GOLDFISH ...@@ -459,6 +462,7 @@ config GOLDFISH
config RETPOLINE config RETPOLINE
bool "Avoid speculative indirect branches in kernel" bool "Avoid speculative indirect branches in kernel"
select OBJTOOL if HAVE_OBJTOOL
default y default y
help help
Compile kernel with the retpoline compiler options to guard against Compile kernel with the retpoline compiler options to guard against
...@@ -472,6 +476,7 @@ config CC_HAS_SLS ...@@ -472,6 +476,7 @@ config CC_HAS_SLS
config SLS config SLS
bool "Mitigate Straight-Line-Speculation" bool "Mitigate Straight-Line-Speculation"
depends on CC_HAS_SLS && X86_64 depends on CC_HAS_SLS && X86_64
select OBJTOOL if HAVE_OBJTOOL
default n default n
help help
Compile the kernel with straight-line-speculation options to guard Compile the kernel with straight-line-speculation options to guard
...@@ -1859,9 +1864,10 @@ config CC_HAS_IBT ...@@ -1859,9 +1864,10 @@ config CC_HAS_IBT
config X86_KERNEL_IBT config X86_KERNEL_IBT
prompt "Indirect Branch Tracking" prompt "Indirect Branch Tracking"
bool bool
depends on X86_64 && CC_HAS_IBT && STACK_VALIDATION depends on X86_64 && CC_HAS_IBT && HAVE_OBJTOOL
# https://github.com/llvm/llvm-project/commit/9d7001eba9c4cb311e03cd8cdc231f9e579f2d0f # https://github.com/llvm/llvm-project/commit/9d7001eba9c4cb311e03cd8cdc231f9e579f2d0f
depends on !LD_IS_LLD || LLD_VERSION >= 140000 depends on !LD_IS_LLD || LLD_VERSION >= 140000
select OBJTOOL
help help
Build the kernel with support for Indirect Branch Tracking, a Build the kernel with support for Indirect Branch Tracking, a
hardware support course-grain forward-edge Control Flow Integrity hardware support course-grain forward-edge Control Flow Integrity
......
...@@ -237,7 +237,7 @@ choice ...@@ -237,7 +237,7 @@ choice
config UNWINDER_ORC config UNWINDER_ORC
bool "ORC unwinder" bool "ORC unwinder"
depends on X86_64 depends on X86_64
select STACK_VALIDATION select OBJTOOL
help help
This option enables the ORC (Oops Rewind Capability) unwinder for This option enables the ORC (Oops Rewind Capability) unwinder for
unwinding kernel stack traces. It uses a custom data format which is unwinding kernel stack traces. It uses a custom data format which is
......
...@@ -20,7 +20,7 @@ ...@@ -20,7 +20,7 @@
_ASM_PTR "%c0 + %c1 - .\n\t" \ _ASM_PTR "%c0 + %c1 - .\n\t" \
".popsection \n\t" ".popsection \n\t"
#ifdef CONFIG_STACK_VALIDATION #ifdef CONFIG_HAVE_JUMP_LABEL_HACK
static __always_inline bool arch_static_branch(struct static_key *key, bool branch) static __always_inline bool arch_static_branch(struct static_key *key, bool branch)
{ {
...@@ -34,7 +34,7 @@ static __always_inline bool arch_static_branch(struct static_key *key, bool bran ...@@ -34,7 +34,7 @@ static __always_inline bool arch_static_branch(struct static_key *key, bool bran
return true; return true;
} }
#else #else /* !CONFIG_HAVE_JUMP_LABEL_HACK */
static __always_inline bool arch_static_branch(struct static_key * const key, const bool branch) static __always_inline bool arch_static_branch(struct static_key * const key, const bool branch)
{ {
...@@ -48,7 +48,7 @@ static __always_inline bool arch_static_branch(struct static_key * const key, co ...@@ -48,7 +48,7 @@ static __always_inline bool arch_static_branch(struct static_key * const key, co
return true; return true;
} }
#endif /* STACK_VALIDATION */ #endif /* CONFIG_HAVE_JUMP_LABEL_HACK */
static __always_inline bool arch_static_branch_jump(struct static_key * const key, const bool branch) static __always_inline bool arch_static_branch_jump(struct static_key * const key, const bool branch)
{ {
......
...@@ -338,7 +338,7 @@ void __init_or_module noinline apply_alternatives(struct alt_instr *start, ...@@ -338,7 +338,7 @@ void __init_or_module noinline apply_alternatives(struct alt_instr *start,
} }
} }
#if defined(CONFIG_RETPOLINE) && defined(CONFIG_STACK_VALIDATION) #if defined(CONFIG_RETPOLINE) && defined(CONFIG_OBJTOOL)
/* /*
* CALL/JMP *%\reg * CALL/JMP *%\reg
...@@ -507,11 +507,11 @@ void __init_or_module noinline apply_retpolines(s32 *start, s32 *end) ...@@ -507,11 +507,11 @@ void __init_or_module noinline apply_retpolines(s32 *start, s32 *end)
} }
} }
#else /* !RETPOLINES || !CONFIG_STACK_VALIDATION */ #else /* !CONFIG_RETPOLINE || !CONFIG_OBJTOOL */
void __init_or_module noinline apply_retpolines(s32 *start, s32 *end) { } void __init_or_module noinline apply_retpolines(s32 *start, s32 *end) { }
#endif /* CONFIG_RETPOLINE && CONFIG_STACK_VALIDATION */ #endif /* CONFIG_RETPOLINE && CONFIG_OBJTOOL */
#ifdef CONFIG_X86_KERNEL_IBT #ifdef CONFIG_X86_KERNEL_IBT
......
...@@ -109,7 +109,7 @@ void ftrace_likely_update(struct ftrace_likely_data *f, int val, ...@@ -109,7 +109,7 @@ void ftrace_likely_update(struct ftrace_likely_data *f, int val,
#endif #endif
/* Unreachable code */ /* Unreachable code */
#ifdef CONFIG_STACK_VALIDATION #ifdef CONFIG_OBJTOOL
/* /*
* These macros help objtool understand GCC code flow for unreachable code. * These macros help objtool understand GCC code flow for unreachable code.
* The __COUNTER__ based labels are a hack to make each instance of the macros * The __COUNTER__ based labels are a hack to make each instance of the macros
...@@ -128,10 +128,10 @@ void ftrace_likely_update(struct ftrace_likely_data *f, int val, ...@@ -128,10 +128,10 @@ void ftrace_likely_update(struct ftrace_likely_data *f, int val,
/* Annotate a C jump table to allow objtool to follow the code flow */ /* Annotate a C jump table to allow objtool to follow the code flow */
#define __annotate_jump_table __section(".rodata..c_jump_table") #define __annotate_jump_table __section(".rodata..c_jump_table")
#else #else /* !CONFIG_OBJTOOL */
#define annotate_unreachable() #define annotate_unreachable()
#define __annotate_jump_table #define __annotate_jump_table
#endif #endif /* CONFIG_OBJTOOL */
#ifndef unreachable #ifndef unreachable
# define unreachable() do { \ # define unreachable() do { \
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
#ifndef __LINUX_INSTRUMENTATION_H #ifndef __LINUX_INSTRUMENTATION_H
#define __LINUX_INSTRUMENTATION_H #define __LINUX_INSTRUMENTATION_H
#if defined(CONFIG_DEBUG_ENTRY) && defined(CONFIG_STACK_VALIDATION) #ifdef CONFIG_NOINSTR_VALIDATION
#include <linux/stringify.h> #include <linux/stringify.h>
...@@ -53,9 +53,9 @@ ...@@ -53,9 +53,9 @@
".popsection\n\t" : : "i" (c)); \ ".popsection\n\t" : : "i" (c)); \
}) })
#define instrumentation_end() __instrumentation_end(__COUNTER__) #define instrumentation_end() __instrumentation_end(__COUNTER__)
#else #else /* !CONFIG_NOINSTR_VALIDATION */
# define instrumentation_begin() do { } while(0) # define instrumentation_begin() do { } while(0)
# define instrumentation_end() do { } while(0) # define instrumentation_end() do { } while(0)
#endif #endif /* CONFIG_NOINSTR_VALIDATION */
#endif /* __LINUX_INSTRUMENTATION_H */ #endif /* __LINUX_INSTRUMENTATION_H */
...@@ -38,7 +38,7 @@ struct unwind_hint { ...@@ -38,7 +38,7 @@ struct unwind_hint {
#define UNWIND_HINT_TYPE_REGS_PARTIAL 2 #define UNWIND_HINT_TYPE_REGS_PARTIAL 2
#define UNWIND_HINT_TYPE_FUNC 3 #define UNWIND_HINT_TYPE_FUNC 3
#ifdef CONFIG_STACK_VALIDATION #ifdef CONFIG_OBJTOOL
#include <asm/asm.h> #include <asm/asm.h>
...@@ -159,7 +159,7 @@ struct unwind_hint { ...@@ -159,7 +159,7 @@ struct unwind_hint {
#endif /* __ASSEMBLY__ */ #endif /* __ASSEMBLY__ */
#else /* !CONFIG_STACK_VALIDATION */ #else /* !CONFIG_OBJTOOL */
#ifndef __ASSEMBLY__ #ifndef __ASSEMBLY__
...@@ -181,6 +181,6 @@ struct unwind_hint { ...@@ -181,6 +181,6 @@ struct unwind_hint {
.endm .endm
#endif #endif
#endif /* CONFIG_STACK_VALIDATION */ #endif /* CONFIG_OBJTOOL */
#endif /* _LINUX_OBJTOOL_H */ #endif /* _LINUX_OBJTOOL_H */
...@@ -729,6 +729,7 @@ config FTRACE_MCOUNT_USE_OBJTOOL ...@@ -729,6 +729,7 @@ config FTRACE_MCOUNT_USE_OBJTOOL
depends on !FTRACE_MCOUNT_USE_PATCHABLE_FUNCTION_ENTRY depends on !FTRACE_MCOUNT_USE_PATCHABLE_FUNCTION_ENTRY
depends on !FTRACE_MCOUNT_USE_CC depends on !FTRACE_MCOUNT_USE_CC
depends on FTRACE_MCOUNT_RECORD depends on FTRACE_MCOUNT_RECORD
select OBJTOOL
config FTRACE_MCOUNT_USE_RECORDMCOUNT config FTRACE_MCOUNT_USE_RECORDMCOUNT
def_bool y def_bool y
......
...@@ -485,24 +485,25 @@ config FRAME_POINTER ...@@ -485,24 +485,25 @@ config FRAME_POINTER
larger and slower, but it gives very useful debugging information larger and slower, but it gives very useful debugging information
in case of kernel bugs. (precise oopses/stacktraces/warnings) in case of kernel bugs. (precise oopses/stacktraces/warnings)
config OBJTOOL
bool
config STACK_VALIDATION config STACK_VALIDATION
bool "Compile-time stack metadata validation" bool "Compile-time stack metadata validation"
depends on HAVE_STACK_VALIDATION depends on HAVE_STACK_VALIDATION && UNWINDER_FRAME_POINTER
select OBJTOOL
default n default n
help help
Add compile-time checks to validate stack metadata, including frame Validate frame pointer rules at compile-time. This helps ensure that
pointers (if CONFIG_FRAME_POINTER is enabled). This helps ensure runtime stack traces are more reliable.
that runtime stack traces are more reliable.
This is also a prerequisite for generation of ORC unwind data, which
is needed for CONFIG_UNWINDER_ORC.
For more information, see For more information, see
tools/objtool/Documentation/stack-validation.txt. tools/objtool/Documentation/stack-validation.txt.
config VMLINUX_VALIDATION config NOINSTR_VALIDATION
bool bool
depends on STACK_VALIDATION && DEBUG_ENTRY depends on HAVE_NOINSTR_VALIDATION && DEBUG_ENTRY
select OBJTOOL
default y default y
config VMLINUX_MAP config VMLINUX_MAP
...@@ -2035,10 +2036,11 @@ config KCOV ...@@ -2035,10 +2036,11 @@ config KCOV
bool "Code coverage for fuzzing" bool "Code coverage for fuzzing"
depends on ARCH_HAS_KCOV depends on ARCH_HAS_KCOV
depends on CC_HAS_SANCOV_TRACE_PC || GCC_PLUGINS depends on CC_HAS_SANCOV_TRACE_PC || GCC_PLUGINS
depends on !ARCH_WANTS_NO_INSTR || STACK_VALIDATION || \ depends on !ARCH_WANTS_NO_INSTR || HAVE_NOINSTR_HACK || \
GCC_VERSION >= 120000 || CLANG_VERSION >= 130000 GCC_VERSION >= 120000 || CLANG_VERSION >= 130000
select DEBUG_FS select DEBUG_FS
select GCC_PLUGIN_SANCOV if !CC_HAS_SANCOV_TRACE_PC select GCC_PLUGIN_SANCOV if !CC_HAS_SANCOV_TRACE_PC
select OBJTOOL if HAVE_NOINSTR_HACK
help help
KCOV exposes kernel code coverage information in a form suitable KCOV exposes kernel code coverage information in a form suitable
for coverage-guided fuzzing (randomized testing). for coverage-guided fuzzing (randomized testing).
......
...@@ -187,7 +187,9 @@ config KCSAN_WEAK_MEMORY ...@@ -187,7 +187,9 @@ config KCSAN_WEAK_MEMORY
# We can either let objtool nop __tsan_func_{entry,exit}() and builtin # We can either let objtool nop __tsan_func_{entry,exit}() and builtin
# atomics instrumentation in .noinstr.text, or use a compiler that can # atomics instrumentation in .noinstr.text, or use a compiler that can
# implement __no_kcsan to really remove all instrumentation. # implement __no_kcsan to really remove all instrumentation.
depends on STACK_VALIDATION || CC_IS_GCC || CLANG_VERSION >= 140000 depends on !ARCH_WANTS_NO_INSTR || HAVE_NOINSTR_HACK || \
CC_IS_GCC || CLANG_VERSION >= 140000
select OBJTOOL if HAVE_NOINSTR_HACK
help help
Enable support for modeling a subset of weak memory, which allows Enable support for modeling a subset of weak memory, which allows
detecting a subset of data races due to missing memory barriers. detecting a subset of data races due to missing memory barriers.
......
...@@ -94,7 +94,7 @@ config UBSAN_UNREACHABLE ...@@ -94,7 +94,7 @@ config UBSAN_UNREACHABLE
bool "Perform checking for unreachable code" bool "Perform checking for unreachable code"
# objtool already handles unreachable checking and gets angry about # objtool already handles unreachable checking and gets angry about
# seeing UBSan instrumentation located in unreachable places. # seeing UBSan instrumentation located in unreachable places.
depends on !STACK_VALIDATION depends on !(OBJTOOL && (STACK_VALIDATION || UNWINDER_ORC || X86_SMAP))
depends on $(cc-option,-fsanitize=unreachable) depends on $(cc-option,-fsanitize=unreachable)
help help
This option enables -fsanitize=unreachable which checks for control This option enables -fsanitize=unreachable which checks for control
......
...@@ -222,25 +222,29 @@ cmd_record_mcount = $(if $(findstring $(strip $(CC_FLAGS_FTRACE)),$(_c_flags)), ...@@ -222,25 +222,29 @@ cmd_record_mcount = $(if $(findstring $(strip $(CC_FLAGS_FTRACE)),$(_c_flags)),
$(sub_cmd_record_mcount)) $(sub_cmd_record_mcount))
endif # CONFIG_FTRACE_MCOUNT_USE_RECORDMCOUNT endif # CONFIG_FTRACE_MCOUNT_USE_RECORDMCOUNT
ifdef CONFIG_STACK_VALIDATION ifdef CONFIG_OBJTOOL
objtool := $(objtree)/tools/objtool/objtool objtool := $(objtree)/tools/objtool/objtool
objtool_args = \ objtool_args = \
$(if $(CONFIG_UNWINDER_ORC),orc generate,check) \ $(if $(CONFIG_HAVE_JUMP_LABEL_HACK), --hacks=jump_label) \
$(if $(part-of-module), --module) \ $(if $(CONFIG_HAVE_NOINSTR_HACK), --hacks=noinstr) \
$(if $(CONFIG_X86_KERNEL_IBT), --lto --ibt) \ $(if $(CONFIG_X86_KERNEL_IBT), --ibt) \
$(if $(CONFIG_FRAME_POINTER),, --no-fp) \ $(if $(CONFIG_FTRACE_MCOUNT_USE_OBJTOOL), --mcount) \
$(if $(CONFIG_GCOV_KERNEL), --no-unreachable) \ $(if $(CONFIG_UNWINDER_ORC), --orc) \
$(if $(CONFIG_RETPOLINE), --retpoline) \ $(if $(CONFIG_RETPOLINE), --retpoline) \
$(if $(CONFIG_SLS), --sls) \
$(if $(CONFIG_STACK_VALIDATION), --stackval) \
$(if $(CONFIG_HAVE_STATIC_CALL_INLINE), --static-call) \
--uaccess \ --uaccess \
$(if $(CONFIG_FTRACE_MCOUNT_USE_OBJTOOL), --mcount) \ $(if $(linked-object), --link) \
$(if $(CONFIG_SLS), --sls) $(if $(part-of-module), --module) \
$(if $(CONFIG_GCOV_KERNEL), --no-unreachable)
cmd_objtool = $(if $(objtool-enabled), ; $(objtool) $(objtool_args) $@) cmd_objtool = $(if $(objtool-enabled), ; $(objtool) $(objtool_args) $@)
cmd_gen_objtooldep = $(if $(objtool-enabled), { echo ; echo '$@: $$(wildcard $(objtool))' ; } >> $(dot-target).cmd) cmd_gen_objtooldep = $(if $(objtool-enabled), { echo ; echo '$@: $$(wildcard $(objtool))' ; } >> $(dot-target).cmd)
endif # CONFIG_STACK_VALIDATION endif # CONFIG_OBJTOOL
ifneq ($(CONFIG_LTO_CLANG)$(CONFIG_X86_KERNEL_IBT),) ifneq ($(CONFIG_LTO_CLANG)$(CONFIG_X86_KERNEL_IBT),)
...@@ -303,6 +307,7 @@ quiet_cmd_cc_prelink_modules = LD [M] $@ ...@@ -303,6 +307,7 @@ quiet_cmd_cc_prelink_modules = LD [M] $@
# modules into native code # modules into native code
$(obj)/%.prelink.o: objtool-enabled = y $(obj)/%.prelink.o: objtool-enabled = y
$(obj)/%.prelink.o: part-of-module := y $(obj)/%.prelink.o: part-of-module := y
$(obj)/%.prelink.o: linked-object := y
$(obj)/%.prelink.o: $(obj)/%.o FORCE $(obj)/%.prelink.o: $(obj)/%.o FORCE
$(call if_changed,cc_prelink_modules) $(call if_changed,cc_prelink_modules)
......
...@@ -44,17 +44,6 @@ ...@@ -44,17 +44,6 @@
set -o errexit set -o errexit
set -o nounset set -o nounset
READELF="${CROSS_COMPILE:-}readelf"
ADDR2LINE="${CROSS_COMPILE:-}addr2line"
SIZE="${CROSS_COMPILE:-}size"
NM="${CROSS_COMPILE:-}nm"
command -v awk >/dev/null 2>&1 || die "awk isn't installed"
command -v ${READELF} >/dev/null 2>&1 || die "readelf isn't installed"
command -v ${ADDR2LINE} >/dev/null 2>&1 || die "addr2line isn't installed"
command -v ${SIZE} >/dev/null 2>&1 || die "size isn't installed"
command -v ${NM} >/dev/null 2>&1 || die "nm isn't installed"
usage() { usage() {
echo "usage: faddr2line [--list] <object file> <func+offset> <func+offset>..." >&2 echo "usage: faddr2line [--list] <object file> <func+offset> <func+offset>..." >&2
exit 1 exit 1
...@@ -69,6 +58,14 @@ die() { ...@@ -69,6 +58,14 @@ die() {
exit 1 exit 1
} }
READELF="${CROSS_COMPILE:-}readelf"
ADDR2LINE="${CROSS_COMPILE:-}addr2line"
AWK="awk"
command -v ${AWK} >/dev/null 2>&1 || die "${AWK} isn't installed"
command -v ${READELF} >/dev/null 2>&1 || die "${READELF} isn't installed"
command -v ${ADDR2LINE} >/dev/null 2>&1 || die "${ADDR2LINE} isn't installed"
# Try to figure out the source directory prefix so we can remove it from the # Try to figure out the source directory prefix so we can remove it from the
# addr2line output. HACK ALERT: This assumes that start_kernel() is in # addr2line output. HACK ALERT: This assumes that start_kernel() is in
# init/main.c! This only works for vmlinux. Otherwise it falls back to # init/main.c! This only works for vmlinux. Otherwise it falls back to
...@@ -76,7 +73,7 @@ die() { ...@@ -76,7 +73,7 @@ die() {
find_dir_prefix() { find_dir_prefix() {
local objfile=$1 local objfile=$1
local start_kernel_addr=$(${READELF} -sW $objfile | awk '$8 == "start_kernel" {printf "0x%s", $2}') local start_kernel_addr=$(${READELF} --symbols --wide $objfile | ${AWK} '$8 == "start_kernel" {printf "0x%s", $2}')
[[ -z $start_kernel_addr ]] && return [[ -z $start_kernel_addr ]] && return
local file_line=$(${ADDR2LINE} -e $objfile $start_kernel_addr) local file_line=$(${ADDR2LINE} -e $objfile $start_kernel_addr)
...@@ -97,86 +94,133 @@ __faddr2line() { ...@@ -97,86 +94,133 @@ __faddr2line() {
local dir_prefix=$3 local dir_prefix=$3
local print_warnings=$4 local print_warnings=$4
local func=${func_addr%+*} local sym_name=${func_addr%+*}
local offset=${func_addr#*+} local offset=${func_addr#*+}
offset=${offset%/*} offset=${offset%/*}
local size= local user_size=
[[ $func_addr =~ "/" ]] && size=${func_addr#*/} [[ $func_addr =~ "/" ]] && user_size=${func_addr#*/}
if [[ -z $func ]] || [[ -z $offset ]] || [[ $func = $func_addr ]]; then if [[ -z $sym_name ]] || [[ -z $offset ]] || [[ $sym_name = $func_addr ]]; then
warn "bad func+offset $func_addr" warn "bad func+offset $func_addr"
DONE=1 DONE=1
return return
fi fi
# Go through each of the object's symbols which match the func name. # Go through each of the object's symbols which match the func name.
# In rare cases there might be duplicates. # In rare cases there might be duplicates, in which case we print all
file_end=$(${SIZE} -Ax $objfile | awk '$1 == ".text" {print $2}') # matches.
while read symbol; do while read line; do
local fields=($symbol) local fields=($line)
local sym_base=0x${fields[0]} local sym_addr=0x${fields[1]}
local sym_type=${fields[1]} local sym_elf_size=${fields[2]}
local sym_end=${fields[3]} local sym_sec=${fields[6]}
# calculate the size # Get the section size:
local sym_size=$(($sym_end - $sym_base)) local sec_size=$(${READELF} --section-headers --wide $objfile |
sed 's/\[ /\[/' |
${AWK} -v sec=$sym_sec '$1 == "[" sec "]" { print "0x" $6; exit }')
if [[ -z $sec_size ]]; then
warn "bad section size: section: $sym_sec"
DONE=1
return
fi
# Calculate the symbol size.
#
# Unfortunately we can't use the ELF size, because kallsyms
# also includes the padding bytes in its size calculation. For
# kallsyms, the size calculation is the distance between the
# symbol and the next symbol in a sorted list.
local sym_size
local cur_sym_addr
local found=0
while read line; do
local fields=($line)
cur_sym_addr=0x${fields[1]}
local cur_sym_elf_size=${fields[2]}
local cur_sym_name=${fields[7]:-}
if [[ $cur_sym_addr = $sym_addr ]] &&
[[ $cur_sym_elf_size = $sym_elf_size ]] &&
[[ $cur_sym_name = $sym_name ]]; then
found=1
continue
fi
if [[ $found = 1 ]]; then
sym_size=$(($cur_sym_addr - $sym_addr))
[[ $sym_size -lt $sym_elf_size ]] && continue;
found=2
break
fi
done < <(${READELF} --symbols --wide $objfile | ${AWK} -v sec=$sym_sec '$7 == sec' | sort --key=2)
if [[ $found = 0 ]]; then
warn "can't find symbol: sym_name: $sym_name sym_sec: $sym_sec sym_addr: $sym_addr sym_elf_size: $sym_elf_size"
DONE=1
return
fi
# If nothing was found after the symbol, assume it's the last
# symbol in the section.
[[ $found = 1 ]] && sym_size=$(($sec_size - $sym_addr))
if [[ -z $sym_size ]] || [[ $sym_size -le 0 ]]; then if [[ -z $sym_size ]] || [[ $sym_size -le 0 ]]; then
warn "bad symbol size: base: $sym_base end: $sym_end" warn "bad symbol size: sym_addr: $sym_addr cur_sym_addr: $cur_sym_addr"
DONE=1 DONE=1
return return
fi fi
sym_size=0x$(printf %x $sym_size) sym_size=0x$(printf %x $sym_size)
# calculate the address # Calculate the section address from user-supplied offset:
local addr=$(($sym_base + $offset)) local addr=$(($sym_addr + $offset))
if [[ -z $addr ]] || [[ $addr = 0 ]]; then if [[ -z $addr ]] || [[ $addr = 0 ]]; then
warn "bad address: $sym_base + $offset" warn "bad address: $sym_addr + $offset"
DONE=1 DONE=1
return return
fi fi
addr=0x$(printf %x $addr) addr=0x$(printf %x $addr)
# weed out non-function symbols # If the user provided a size, make sure it matches the symbol's size:
if [[ $sym_type != t ]] && [[ $sym_type != T ]]; then if [[ -n $user_size ]] && [[ $user_size -ne $sym_size ]]; then
[[ $print_warnings = 1 ]] &&
echo "skipping $func address at $addr due to non-function symbol of type '$sym_type'"
continue
fi
# if the user provided a size, make sure it matches the symbol's size
if [[ -n $size ]] && [[ $size -ne $sym_size ]]; then
[[ $print_warnings = 1 ]] && [[ $print_warnings = 1 ]] &&
echo "skipping $func address at $addr due to size mismatch ($size != $sym_size)" echo "skipping $sym_name address at $addr due to size mismatch ($user_size != $sym_size)"
continue; continue;
fi fi
# make sure the provided offset is within the symbol's range # Make sure the provided offset is within the symbol's range:
if [[ $offset -gt $sym_size ]]; then if [[ $offset -gt $sym_size ]]; then
[[ $print_warnings = 1 ]] && [[ $print_warnings = 1 ]] &&
echo "skipping $func address at $addr due to size mismatch ($offset > $sym_size)" echo "skipping $sym_name address at $addr due to size mismatch ($offset > $sym_size)"
continue continue
fi fi
# separate multiple entries with a blank line # In case of duplicates or multiple addresses specified on the
# cmdline, separate multiple entries with a blank line:
[[ $FIRST = 0 ]] && echo [[ $FIRST = 0 ]] && echo
FIRST=0 FIRST=0
# pass real address to addr2line echo "$sym_name+$offset/$sym_size:"
echo "$func+$offset/$sym_size:"
local file_lines=$(${ADDR2LINE} -fpie $objfile $addr | sed "s; $dir_prefix\(\./\)*; ;")
[[ -z $file_lines ]] && return
# Pass section address to addr2line and strip absolute paths
# from the output:
local output=$(${ADDR2LINE} -fpie $objfile $addr | sed "s; $dir_prefix\(\./\)*; ;")
[[ -z $output ]] && continue
# Default output (non --list):
if [[ $LIST = 0 ]]; then if [[ $LIST = 0 ]]; then
echo "$file_lines" | while read -r line echo "$output" | while read -r line
do do
echo $line echo $line
done done
DONE=1; DONE=1;
return continue
fi fi
# show each line with context # For --list, show each line with its corresponding source code:
echo "$file_lines" | while read -r line echo "$output" | while read -r line
do do
echo echo
echo $line echo $line
...@@ -184,12 +228,12 @@ __faddr2line() { ...@@ -184,12 +228,12 @@ __faddr2line() {
n1=$[$n-5] n1=$[$n-5]
n2=$[$n+5] n2=$[$n+5]
f=$(echo $line | sed 's/.*at \(.\+\):.*/\1/g') f=$(echo $line | sed 's/.*at \(.\+\):.*/\1/g')
awk 'NR>=strtonum("'$n1'") && NR<=strtonum("'$n2'") { if (NR=='$n') printf(">%d<", NR); else printf(" %d ", NR); printf("\t%s\n", $0)}' $f ${AWK} 'NR>=strtonum("'$n1'") && NR<=strtonum("'$n2'") { if (NR=='$n') printf(">%d<", NR); else printf(" %d ", NR); printf("\t%s\n", $0)}' $f
done done
DONE=1 DONE=1
done < <(${NM} -n $objfile | awk -v fn=$func -v end=$file_end '$3 == fn { found=1; line=$0; start=$1; next } found == 1 { found=0; print line, "0x"$1 } END {if (found == 1) print line, end; }') done < <(${READELF} --symbols --wide $objfile | ${AWK} -v fn=$sym_name '$4 == "FUNC" && $8 == fn')
} }
[[ $# -lt 2 ]] && usage [[ $# -lt 2 ]] && usage
......
...@@ -108,16 +108,22 @@ objtool_link() ...@@ -108,16 +108,22 @@ objtool_link()
local objtoolcmd; local objtoolcmd;
local objtoolopt; local objtoolopt;
if is_enabled CONFIG_STACK_VALIDATION && \ if ! is_enabled CONFIG_OBJTOOL; then
( is_enabled CONFIG_LTO_CLANG || is_enabled CONFIG_X86_KERNEL_IBT ); then return;
fi
# Don't perform vmlinux validation unless explicitly requested, if is_enabled CONFIG_LTO_CLANG || is_enabled CONFIG_X86_KERNEL_IBT; then
# but run objtool on vmlinux.o now that we have an object file.
if is_enabled CONFIG_UNWINDER_ORC; then # For LTO and IBT, objtool doesn't run on individual
objtoolcmd="orc generate" # translation units. Run everything on vmlinux instead.
if is_enabled CONFIG_HAVE_JUMP_LABEL_HACK; then
objtoolopt="${objtoolopt} --hacks=jump_label"
fi fi
objtoolopt="${objtoolopt} --lto" if is_enabled CONFIG_HAVE_NOINSTR_HACK; then
objtoolopt="${objtoolopt} --hacks=noinstr"
fi
if is_enabled CONFIG_X86_KERNEL_IBT; then if is_enabled CONFIG_X86_KERNEL_IBT; then
objtoolopt="${objtoolopt} --ibt" objtoolopt="${objtoolopt} --ibt"
...@@ -126,34 +132,44 @@ objtool_link() ...@@ -126,34 +132,44 @@ objtool_link()
if is_enabled CONFIG_FTRACE_MCOUNT_USE_OBJTOOL; then if is_enabled CONFIG_FTRACE_MCOUNT_USE_OBJTOOL; then
objtoolopt="${objtoolopt} --mcount" objtoolopt="${objtoolopt} --mcount"
fi fi
if is_enabled CONFIG_UNWINDER_ORC; then
objtoolopt="${objtoolopt} --orc"
fi
if is_enabled CONFIG_RETPOLINE; then
objtoolopt="${objtoolopt} --retpoline"
fi
if is_enabled CONFIG_SLS; then
objtoolopt="${objtoolopt} --sls"
fi
if is_enabled CONFIG_STACK_VALIDATION; then
objtoolopt="${objtoolopt} --stackval"
fi
if is_enabled CONFIG_HAVE_STATIC_CALL_INLINE; then
objtoolopt="${objtoolopt} --static-call"
fi
objtoolopt="${objtoolopt} --uaccess"
fi fi
if is_enabled CONFIG_VMLINUX_VALIDATION; then if is_enabled CONFIG_NOINSTR_VALIDATION; then
objtoolopt="${objtoolopt} --noinstr" objtoolopt="${objtoolopt} --noinstr"
fi fi
if [ -n "${objtoolopt}" ]; then if [ -n "${objtoolopt}" ]; then
if [ -z "${objtoolcmd}" ]; then
objtoolcmd="check"
fi
objtoolopt="${objtoolopt} --vmlinux"
if ! is_enabled CONFIG_FRAME_POINTER; then
objtoolopt="${objtoolopt} --no-fp"
fi
if is_enabled CONFIG_GCOV_KERNEL; then if is_enabled CONFIG_GCOV_KERNEL; then
objtoolopt="${objtoolopt} --no-unreachable" objtoolopt="${objtoolopt} --no-unreachable"
fi fi
if is_enabled CONFIG_RETPOLINE; then
objtoolopt="${objtoolopt} --retpoline"
fi
objtoolopt="${objtoolopt} --uaccess" objtoolopt="${objtoolopt} --link"
if is_enabled CONFIG_SLS; then
objtoolopt="${objtoolopt} --sls"
fi
info OBJTOOL ${1} info OBJTOOL ${1}
tools/objtool/objtool ${objtoolcmd} ${objtoolopt} ${1} tools/objtool/objtool ${objtoolopt} ${1}
fi fi
} }
......
#!/bin/bash
# SPDX-License-Identifier: GPL-2.0
#
# Disassemble a single function.
#
# usage: objdump-func <file> <func>
set -o errexit
set -o nounset
OBJDUMP="${CROSS_COMPILE:-}objdump"
command -v gawk >/dev/null 2>&1 || die "gawk isn't installed"
usage() {
echo "usage: objdump-func <file> <func>" >&2
exit 1
}
[[ $# -lt 2 ]] && usage
OBJ=$1; shift
FUNC=$1; shift
# Secret feature to allow adding extra objdump args at the end
EXTRA_ARGS=$@
# Note this also matches compiler-added suffixes like ".cold", etc
${OBJDUMP} -wdr $EXTRA_ARGS $OBJ | gawk -M -v f=$FUNC '/^$/ { P=0; } $0 ~ "<" f "(\\..*)?>:" { P=1; O=strtonum("0x" $1); } { if (P) { o=strtonum("0x" $1); printf("%04x ", o-O); print $0; } }'
...@@ -67,7 +67,7 @@ deploy_kernel_headers () { ...@@ -67,7 +67,7 @@ deploy_kernel_headers () {
) > debian/hdrsrcfiles ) > debian/hdrsrcfiles
{ {
if is_enabled CONFIG_STACK_VALIDATION; then if is_enabled CONFIG_OBJTOOL; then
echo tools/objtool/objtool echo tools/objtool/objtool
fi fi
......
...@@ -38,7 +38,7 @@ struct unwind_hint { ...@@ -38,7 +38,7 @@ struct unwind_hint {
#define UNWIND_HINT_TYPE_REGS_PARTIAL 2 #define UNWIND_HINT_TYPE_REGS_PARTIAL 2
#define UNWIND_HINT_TYPE_FUNC 3 #define UNWIND_HINT_TYPE_FUNC 3
#ifdef CONFIG_STACK_VALIDATION #ifdef CONFIG_OBJTOOL
#include <asm/asm.h> #include <asm/asm.h>
...@@ -159,7 +159,7 @@ struct unwind_hint { ...@@ -159,7 +159,7 @@ struct unwind_hint {
#endif /* __ASSEMBLY__ */ #endif /* __ASSEMBLY__ */
#else /* !CONFIG_STACK_VALIDATION */ #else /* !CONFIG_OBJTOOL */
#ifndef __ASSEMBLY__ #ifndef __ASSEMBLY__
...@@ -181,6 +181,6 @@ struct unwind_hint { ...@@ -181,6 +181,6 @@ struct unwind_hint {
.endm .endm
#endif #endif
#endif /* CONFIG_STACK_VALIDATION */ #endif /* CONFIG_OBJTOOL */
#endif /* _LINUX_OBJTOOL_H */ #endif /* _LINUX_OBJTOOL_H */
...@@ -806,9 +806,9 @@ static int option__cmp(const void *va, const void *vb) ...@@ -806,9 +806,9 @@ static int option__cmp(const void *va, const void *vb)
static struct option *options__order(const struct option *opts) static struct option *options__order(const struct option *opts)
{ {
int nr_opts = 0, len; int nr_opts = 0, nr_group = 0, len;
const struct option *o = opts; const struct option *o = opts;
struct option *ordered; struct option *opt, *ordered, *group;
for (o = opts; o->type != OPTION_END; o++) for (o = opts; o->type != OPTION_END; o++)
++nr_opts; ++nr_opts;
...@@ -819,7 +819,18 @@ static struct option *options__order(const struct option *opts) ...@@ -819,7 +819,18 @@ static struct option *options__order(const struct option *opts)
goto out; goto out;
memcpy(ordered, opts, len); memcpy(ordered, opts, len);
qsort(ordered, nr_opts, sizeof(*o), option__cmp); /* sort each option group individually */
for (opt = group = ordered; opt->type != OPTION_END; opt++) {
if (opt->type == OPTION_GROUP) {
qsort(group, nr_group, sizeof(*opt), option__cmp);
group = opt + 1;
nr_group = 0;
continue;
}
nr_group++;
}
qsort(group, nr_group, sizeof(*opt), option__cmp);
out: out:
return ordered; return ordered;
} }
......
...@@ -2,17 +2,15 @@ objtool-y += arch/$(SRCARCH)/ ...@@ -2,17 +2,15 @@ objtool-y += arch/$(SRCARCH)/
objtool-y += weak.o objtool-y += weak.o
objtool-$(SUBCMD_CHECK) += check.o objtool-y += check.o
objtool-$(SUBCMD_CHECK) += special.o objtool-y += special.o
objtool-$(SUBCMD_ORC) += check.o
objtool-$(SUBCMD_ORC) += orc_gen.o
objtool-$(SUBCMD_ORC) += orc_dump.o
objtool-y += builtin-check.o objtool-y += builtin-check.o
objtool-y += builtin-orc.o
objtool-y += elf.o objtool-y += elf.o
objtool-y += objtool.o objtool-y += objtool.o
objtool-$(BUILD_ORC) += orc_gen.o
objtool-$(BUILD_ORC) += orc_dump.o
objtool-y += libstring.o objtool-y += libstring.o
objtool-y += libctype.o objtool-y += libctype.o
objtool-y += str_error_r.o objtool-y += str_error_r.o
......
Compile-time stack metadata validation Objtool
====================================== =======
The kernel CONFIG_OBJTOOL option enables a host tool named 'objtool'
which runs at compile time. It can do various validations and
transformations on .o files.
Overview Objtool has become an integral part of the x86-64 kernel toolchain. The
kernel depends on it for a variety of security and performance features
(and other types of features as well).
Features
-------- --------
The kernel CONFIG_STACK_VALIDATION option enables a host tool named Objtool has the following features:
objtool which runs at compile time. It has a "check" subcommand which
analyzes every .o file and ensures the validity of its stack metadata. - Stack unwinding metadata validation -- useful for helping to ensure
It enforces a set of rules on asm code and C inline assembly code so stack traces are reliable for live patching
that stack traces can be reliable.
- ORC unwinder metadata generation -- a faster and more precise
alternative to frame pointer based unwinding
- Retpoline validation -- ensures that all indirect calls go through
retpoline thunks, for Spectre v2 mitigations
- Retpoline call site annotation -- annotates all retpoline thunk call
sites, enabling the kernel to patch them inline, to prevent "thunk
funneling" for both security and performance reasons
- Non-instrumentation validation -- validates non-instrumentable
("noinstr") code rules, preventing instrumentation in low-level C
entry code
- Static call annotation -- annotates static call sites, enabling the
kernel to implement inline static calls, a faster alternative to some
indirect branches
- Uaccess validation -- validates uaccess rules for a proper
implementation of Supervisor Mode Access Protection (SMAP)
- Straight Line Speculation validation -- validates certain SLS
mitigations
- Indirect Branch Tracking validation -- validates Intel CET IBT rules
to ensure that all functions referenced by function pointers have
corresponding ENDBR instructions
- Indirect Branch Tracking annotation -- annotates unused ENDBR
instruction sites, enabling the kernel to "seal" them (replace them
with NOPs) to further harden IBT
- Function entry annotation -- annotates function entries, enabling
kernel function tracing
- Other toolchain hacks which will go unmentioned at this time...
Each feature can be enabled individually or in combination using the
objtool cmdline.
Objects
-------
Typically, objtool runs on every translation unit (TU, aka ".o file") in
the kernel. If a TU is part of a kernel module, the '--module' option
is added.
However:
- If noinstr validation is enabled, it also runs on vmlinux.o, with all
options removed and '--noinstr' added.
- If IBT or LTO is enabled, it doesn't run on TUs at all. Instead it
runs on vmlinux.o and linked modules, with all options.
In summary:
A) Legacy mode:
TU: objtool [--module] <options>
vmlinux: N/A
module: N/A
B) CONFIG_NOINSTR_VALIDATION=y && !(CONFIG_X86_KERNEL_IBT=y || CONFIG_LTO=y):
TU: objtool [--module] <options> // no --noinstr
vmlinux: objtool --noinstr // other options removed
module: N/A
C) CONFIG_X86_KERNEL_IBT=y || CONFIG_LTO=y:
TU: N/A
vmlinux: objtool --noinstr <options>
module: objtool --module --noinstr <options>
Stack validation
----------------
Objtool's stack validation feature analyzes every .o file and ensures
the validity of its stack metadata. It enforces a set of rules on asm
code and C inline assembly code so that stack traces can be reliable.
For each function, it recursively follows all possible code paths and For each function, it recursively follows all possible code paths and
validates the correct frame pointer state at each instruction. validates the correct frame pointer state at each instruction.
...@@ -20,14 +108,6 @@ alternative execution paths to a given instruction (or set of ...@@ -20,14 +108,6 @@ alternative execution paths to a given instruction (or set of
instructions). Similarly, it knows how to follow switch statements, for instructions). Similarly, it knows how to follow switch statements, for
which gcc sometimes uses jump tables. which gcc sometimes uses jump tables.
(Objtool also has an 'orc generate' subcommand which generates debuginfo
for the ORC unwinder. See Documentation/x86/orc-unwinder.rst in the
kernel tree for more details.)
Why do we need stack metadata validation?
-----------------------------------------
Here are some of the benefits of validating stack metadata: Here are some of the benefits of validating stack metadata:
a) More reliable stack traces for frame pointer enabled kernels a) More reliable stack traces for frame pointer enabled kernels
...@@ -113,9 +193,6 @@ c) Higher live patching compatibility rate ...@@ -113,9 +193,6 @@ c) Higher live patching compatibility rate
For more details, see the livepatch documentation in the Linux kernel For more details, see the livepatch documentation in the Linux kernel
source tree at Documentation/livepatch/livepatch.rst. source tree at Documentation/livepatch/livepatch.rst.
Rules
-----
To achieve the validation, objtool enforces the following rules: To achieve the validation, objtool enforces the following rules:
1. Each callable function must be annotated as such with the ELF 1. Each callable function must be annotated as such with the ELF
...@@ -177,7 +254,8 @@ Another possible cause for errors in C code is if the Makefile removes ...@@ -177,7 +254,8 @@ 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. -fno-omit-frame-pointer or adds -fomit-frame-pointer to the gcc options.
Here are some examples of common warnings reported by objtool, what Here are some examples of common warnings reported by objtool, what
they mean, and suggestions for how to fix them. they mean, and suggestions for how to fix them. When in doubt, ping
the objtool maintainers.
1. file.o: warning: objtool: func()+0x128: call without frame pointer save/setup 1. file.o: warning: objtool: func()+0x128: call without frame pointer save/setup
...@@ -358,3 +436,7 @@ ignore it: ...@@ -358,3 +436,7 @@ ignore it:
OBJECT_FILES_NON_STANDARD := y OBJECT_FILES_NON_STANDARD := y
to the Makefile. to the Makefile.
NOTE: OBJECT_FILES_NON_STANDARD doesn't work for link time validation of
vmlinux.o or a linked module. So it should only be used for files which
aren't linked into vmlinux or a module.
...@@ -39,15 +39,13 @@ CFLAGS += $(if $(elfshdr),,-DLIBELF_USE_DEPRECATED) ...@@ -39,15 +39,13 @@ CFLAGS += $(if $(elfshdr),,-DLIBELF_USE_DEPRECATED)
AWK = awk AWK = awk
SUBCMD_CHECK := n BUILD_ORC := n
SUBCMD_ORC := n
ifeq ($(SRCARCH),x86) ifeq ($(SRCARCH),x86)
SUBCMD_CHECK := y BUILD_ORC := y
SUBCMD_ORC := y
endif endif
export SUBCMD_CHECK SUBCMD_ORC export BUILD_ORC
export srctree OUTPUT CFLAGS SRCARCH AWK export srctree OUTPUT CFLAGS SRCARCH AWK
include $(srctree)/tools/build/Makefile.include include $(srctree)/tools/build/Makefile.include
...@@ -65,7 +63,7 @@ $(LIBSUBCMD): fixdep FORCE ...@@ -65,7 +63,7 @@ $(LIBSUBCMD): fixdep FORCE
clean: clean:
$(call QUIET_CLEAN, objtool) $(RM) $(OBJTOOL) $(call QUIET_CLEAN, objtool) $(RM) $(OBJTOOL)
$(Q)find $(OUTPUT) -name '*.o' -delete -o -name '\.*.cmd' -delete -o -name '\.*.d' -delete $(Q)find $(OUTPUT) -name '*.o' -delete -o -name '\.*.cmd' -delete -o -name '\.*.d' -delete
$(Q)$(RM) $(OUTPUT)arch/x86/inat-tables.c $(OUTPUT)fixdep $(Q)$(RM) $(OUTPUT)arch/x86/lib/inat-tables.c $(OUTPUT)fixdep $(LIBSUBCMD)
FORCE: FORCE:
......
...@@ -581,7 +581,7 @@ int arch_decode_instruction(struct objtool_file *file, const struct section *sec ...@@ -581,7 +581,7 @@ int arch_decode_instruction(struct objtool_file *file, const struct section *sec
break; break;
case 0xc7: /* mov imm, r/m */ case 0xc7: /* mov imm, r/m */
if (!noinstr) if (!opts.noinstr)
break; break;
if (insn.length == 3+4+4 && !strncmp(sec->name, ".init.text", 10)) { if (insn.length == 3+4+4 && !strncmp(sec->name, ".init.text", 10)) {
......
...@@ -20,7 +20,7 @@ void arch_handle_alternative(unsigned short feature, struct special_alt *alt) ...@@ -20,7 +20,7 @@ void arch_handle_alternative(unsigned short feature, struct special_alt *alt)
* find paths that see the STAC but take the NOP instead of * find paths that see the STAC but take the NOP instead of
* CLAC and the other way around. * CLAC and the other way around.
*/ */
if (uaccess) if (opts.uaccess)
alt->skip_orig = true; alt->skip_orig = true;
else else
alt->skip_alt = true; alt->skip_alt = true;
......
...@@ -3,28 +3,21 @@ ...@@ -3,28 +3,21 @@
* Copyright (C) 2015-2017 Josh Poimboeuf <jpoimboe@redhat.com> * Copyright (C) 2015-2017 Josh Poimboeuf <jpoimboe@redhat.com>
*/ */
/*
* objtool check:
*
* This command analyzes every .o file and ensures the validity of its stack
* trace metadata. It enforces a set of rules on asm code and C inline
* assembly code so that stack traces can be reliable.
*
* For more information, see tools/objtool/Documentation/stack-validation.txt.
*/
#include <subcmd/parse-options.h> #include <subcmd/parse-options.h>
#include <string.h> #include <string.h>
#include <stdlib.h> #include <stdlib.h>
#include <objtool/builtin.h> #include <objtool/builtin.h>
#include <objtool/objtool.h> #include <objtool/objtool.h>
bool no_fp, no_unreachable, retpoline, module, backtrace, uaccess, stats, #define ERROR(format, ...) \
lto, vmlinux, mcount, noinstr, backup, sls, dryrun, fprintf(stderr, \
ibt; "error: objtool: " format "\n", \
##__VA_ARGS__)
struct opts opts;
static const char * const check_usage[] = { static const char * const check_usage[] = {
"objtool check [<options>] file.o", "objtool <actions> [<options>] file.o",
NULL, NULL,
}; };
...@@ -33,22 +26,64 @@ static const char * const env_usage[] = { ...@@ -33,22 +26,64 @@ static const char * const env_usage[] = {
NULL, NULL,
}; };
static int parse_dump(const struct option *opt, const char *str, int unset)
{
if (!str || !strcmp(str, "orc")) {
opts.dump_orc = true;
return 0;
}
return -1;
}
static int parse_hacks(const struct option *opt, const char *str, int unset)
{
bool found = false;
/*
* Use strstr() as a lazy method of checking for comma-separated
* options.
*
* No string provided == enable all options.
*/
if (!str || strstr(str, "jump_label")) {
opts.hack_jump_label = true;
found = true;
}
if (!str || strstr(str, "noinstr")) {
opts.hack_noinstr = true;
found = true;
}
return found ? 0 : -1;
}
const struct option check_options[] = { const struct option check_options[] = {
OPT_BOOLEAN('f', "no-fp", &no_fp, "Skip frame pointer validation"), OPT_GROUP("Actions:"),
OPT_BOOLEAN('u', "no-unreachable", &no_unreachable, "Skip 'unreachable instruction' warnings"), OPT_CALLBACK_OPTARG('h', "hacks", NULL, NULL, "jump_label,noinstr", "patch toolchain bugs/limitations", parse_hacks),
OPT_BOOLEAN('r', "retpoline", &retpoline, "Validate retpoline assumptions"), OPT_BOOLEAN('i', "ibt", &opts.ibt, "validate and annotate IBT"),
OPT_BOOLEAN('m', "module", &module, "Indicates the object will be part of a kernel module"), OPT_BOOLEAN('m', "mcount", &opts.mcount, "annotate mcount/fentry calls for ftrace"),
OPT_BOOLEAN('b', "backtrace", &backtrace, "unwind on error"), OPT_BOOLEAN('n', "noinstr", &opts.noinstr, "validate noinstr rules"),
OPT_BOOLEAN('a', "uaccess", &uaccess, "enable uaccess checking"), OPT_BOOLEAN('o', "orc", &opts.orc, "generate ORC metadata"),
OPT_BOOLEAN('s', "stats", &stats, "print statistics"), OPT_BOOLEAN('r', "retpoline", &opts.retpoline, "validate and annotate retpoline usage"),
OPT_BOOLEAN(0, "lto", &lto, "whole-archive like runs"), OPT_BOOLEAN('l', "sls", &opts.sls, "validate straight-line-speculation mitigations"),
OPT_BOOLEAN('n', "noinstr", &noinstr, "noinstr validation for vmlinux.o"), OPT_BOOLEAN('s', "stackval", &opts.stackval, "validate frame pointer rules"),
OPT_BOOLEAN('l', "vmlinux", &vmlinux, "vmlinux.o validation"), OPT_BOOLEAN('t', "static-call", &opts.static_call, "annotate static calls"),
OPT_BOOLEAN('M', "mcount", &mcount, "generate __mcount_loc"), OPT_BOOLEAN('u', "uaccess", &opts.uaccess, "validate uaccess rules for SMAP"),
OPT_BOOLEAN('B', "backup", &backup, "create .orig files before modification"), OPT_CALLBACK_OPTARG(0, "dump", NULL, NULL, "orc", "dump metadata", parse_dump),
OPT_BOOLEAN('S', "sls", &sls, "validate straight-line-speculation"),
OPT_BOOLEAN(0, "dry-run", &dryrun, "don't write the modifications"), OPT_GROUP("Options:"),
OPT_BOOLEAN(0, "ibt", &ibt, "validate ENDBR placement"), OPT_BOOLEAN(0, "backtrace", &opts.backtrace, "unwind on error"),
OPT_BOOLEAN(0, "backup", &opts.backup, "create .orig files before modification"),
OPT_BOOLEAN(0, "dry-run", &opts.dryrun, "don't write modifications"),
OPT_BOOLEAN(0, "link", &opts.link, "object is a linked object"),
OPT_BOOLEAN(0, "module", &opts.module, "object is part of a kernel module"),
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_END(), OPT_END(),
}; };
...@@ -79,7 +114,59 @@ int cmd_parse_options(int argc, const char **argv, const char * const usage[]) ...@@ -79,7 +114,59 @@ int cmd_parse_options(int argc, const char **argv, const char * const usage[])
return argc; return argc;
} }
int cmd_check(int argc, const char **argv) static bool opts_valid(void)
{
if (opts.hack_jump_label ||
opts.hack_noinstr ||
opts.ibt ||
opts.mcount ||
opts.noinstr ||
opts.orc ||
opts.retpoline ||
opts.sls ||
opts.stackval ||
opts.static_call ||
opts.uaccess) {
if (opts.dump_orc) {
ERROR("--dump can't be combined with other options");
return false;
}
return true;
}
if (opts.dump_orc)
return true;
ERROR("At least one command required");
return false;
}
static bool link_opts_valid(struct objtool_file *file)
{
if (opts.link)
return true;
if (has_multiple_files(file->elf)) {
ERROR("Linked object detected, forcing --link");
opts.link = true;
return true;
}
if (opts.noinstr) {
ERROR("--noinstr requires --link");
return false;
}
if (opts.ibt) {
ERROR("--ibt requires --link");
return false;
}
return true;
}
int objtool_run(int argc, const char **argv)
{ {
const char *objname; const char *objname;
struct objtool_file *file; struct objtool_file *file;
...@@ -88,10 +175,19 @@ int cmd_check(int argc, const char **argv) ...@@ -88,10 +175,19 @@ int cmd_check(int argc, const char **argv)
argc = cmd_parse_options(argc, argv, check_usage); argc = cmd_parse_options(argc, argv, check_usage);
objname = argv[0]; objname = argv[0];
if (!opts_valid())
return 1;
if (opts.dump_orc)
return orc_dump(objname);
file = objtool_open_read(objname); file = objtool_open_read(objname);
if (!file) if (!file)
return 1; return 1;
if (!link_opts_valid(file))
return 1;
ret = check(file); ret = check(file);
if (ret) if (ret)
return ret; return ret;
......
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2017 Josh Poimboeuf <jpoimboe@redhat.com>
*/
/*
* objtool orc:
*
* This command analyzes a .o file and adds .orc_unwind and .orc_unwind_ip
* sections to it, which is used by the in-kernel ORC unwinder.
*
* This command is a superset of "objtool check".
*/
#include <string.h>
#include <objtool/builtin.h>
#include <objtool/objtool.h>
static const char *orc_usage[] = {
"objtool orc generate [<options>] file.o",
"objtool orc dump file.o",
NULL,
};
int cmd_orc(int argc, const char **argv)
{
const char *objname;
argc--; argv++;
if (argc <= 0)
usage_with_options(orc_usage, check_options);
if (!strncmp(argv[0], "gen", 3)) {
struct objtool_file *file;
int ret;
argc = cmd_parse_options(argc, argv, orc_usage);
objname = argv[0];
file = objtool_open_read(objname);
if (!file)
return 1;
ret = check(file);
if (ret)
return ret;
if (list_empty(&file->insn_list))
return 0;
ret = orc_create(file);
if (ret)
return ret;
if (!file->elf->changed)
return 0;
return elf_write(file->elf);
}
if (!strcmp(argv[0], "dump")) {
if (argc != 2)
usage_with_options(orc_usage, check_options);
objname = argv[1];
return orc_dump(objname);
}
usage_with_options(orc_usage, check_options);
return 0;
}
This diff is collapsed.
...@@ -355,7 +355,7 @@ static int read_sections(struct elf *elf) ...@@ -355,7 +355,7 @@ static int read_sections(struct elf *elf)
elf_hash_add(section_name, &sec->name_hash, str_hash(sec->name)); elf_hash_add(section_name, &sec->name_hash, str_hash(sec->name));
} }
if (stats) { if (opts.stats) {
printf("nr_sections: %lu\n", (unsigned long)sections_nr); printf("nr_sections: %lu\n", (unsigned long)sections_nr);
printf("section_bits: %d\n", elf->section_bits); printf("section_bits: %d\n", elf->section_bits);
} }
...@@ -374,9 +374,15 @@ static void elf_add_symbol(struct elf *elf, struct symbol *sym) ...@@ -374,9 +374,15 @@ static void elf_add_symbol(struct elf *elf, struct symbol *sym)
struct list_head *entry; struct list_head *entry;
struct rb_node *pnode; struct rb_node *pnode;
INIT_LIST_HEAD(&sym->pv_target);
sym->alias = sym;
sym->type = GELF_ST_TYPE(sym->sym.st_info); sym->type = GELF_ST_TYPE(sym->sym.st_info);
sym->bind = GELF_ST_BIND(sym->sym.st_info); sym->bind = GELF_ST_BIND(sym->sym.st_info);
if (sym->type == STT_FILE)
elf->num_files++;
sym->offset = sym->sym.st_value; sym->offset = sym->sym.st_value;
sym->len = sym->sym.st_size; sym->len = sym->sym.st_size;
...@@ -435,8 +441,6 @@ static int read_symbols(struct elf *elf) ...@@ -435,8 +441,6 @@ static int read_symbols(struct elf *elf)
return -1; return -1;
} }
memset(sym, 0, sizeof(*sym)); memset(sym, 0, sizeof(*sym));
INIT_LIST_HEAD(&sym->pv_target);
sym->alias = sym;
sym->idx = i; sym->idx = i;
...@@ -475,7 +479,7 @@ static int read_symbols(struct elf *elf) ...@@ -475,7 +479,7 @@ static int read_symbols(struct elf *elf)
elf_add_symbol(elf, sym); elf_add_symbol(elf, sym);
} }
if (stats) { if (opts.stats) {
printf("nr_symbols: %lu\n", (unsigned long)symbols_nr); printf("nr_symbols: %lu\n", (unsigned long)symbols_nr);
printf("symbol_bits: %d\n", elf->symbol_bits); printf("symbol_bits: %d\n", elf->symbol_bits);
} }
...@@ -546,7 +550,7 @@ static struct section *elf_create_reloc_section(struct elf *elf, ...@@ -546,7 +550,7 @@ static struct section *elf_create_reloc_section(struct elf *elf,
int reltype); int reltype);
int elf_add_reloc(struct elf *elf, struct section *sec, unsigned long offset, int elf_add_reloc(struct elf *elf, struct section *sec, unsigned long offset,
unsigned int type, struct symbol *sym, long addend) unsigned int type, struct symbol *sym, s64 addend)
{ {
struct reloc *reloc; struct reloc *reloc;
...@@ -600,24 +604,21 @@ static void elf_dirty_reloc_sym(struct elf *elf, struct symbol *sym) ...@@ -600,24 +604,21 @@ static void elf_dirty_reloc_sym(struct elf *elf, struct symbol *sym)
} }
/* /*
* Move the first global symbol, as per sh_info, into a new, higher symbol * The libelf API is terrible; gelf_update_sym*() takes a data block relative
* index. This fees up the shndx for a new local symbol. * index value, *NOT* the symbol index. As such, iterate the data blocks and
* adjust index until it fits.
*
* If no data block is found, allow adding a new data block provided the index
* is only one past the end.
*/ */
static int elf_move_global_symbol(struct elf *elf, struct section *symtab, static int elf_update_symbol(struct elf *elf, struct section *symtab,
struct section *symtab_shndx) struct section *symtab_shndx, struct symbol *sym)
{ {
Elf_Data *data, *shndx_data = NULL; Elf32_Word shndx = sym->sec ? sym->sec->idx : SHN_UNDEF;
Elf32_Word first_non_local; Elf_Data *symtab_data = NULL, *shndx_data = NULL;
struct symbol *sym; Elf64_Xword entsize = symtab->sh.sh_entsize;
Elf_Scn *s; int max_idx, idx = sym->idx;
Elf_Scn *s, *t = NULL;
first_non_local = symtab->sh.sh_info;
sym = find_symbol_by_index(elf, first_non_local);
if (!sym) {
WARN("no non-local symbols !?");
return first_non_local;
}
s = elf_getscn(elf->elf, symtab->idx); s = elf_getscn(elf->elf, symtab->idx);
if (!s) { if (!s) {
...@@ -625,79 +626,124 @@ static int elf_move_global_symbol(struct elf *elf, struct section *symtab, ...@@ -625,79 +626,124 @@ static int elf_move_global_symbol(struct elf *elf, struct section *symtab,
return -1; return -1;
} }
data = elf_newdata(s); if (symtab_shndx) {
if (!data) { t = elf_getscn(elf->elf, symtab_shndx->idx);
WARN_ELF("elf_newdata"); if (!t) {
return -1; WARN_ELF("elf_getscn");
return -1;
}
} }
data->d_buf = &sym->sym; for (;;) {
data->d_size = sizeof(sym->sym); /* get next data descriptor for the relevant sections */
data->d_align = 1; symtab_data = elf_getdata(s, symtab_data);
data->d_type = ELF_T_SYM; if (t)
shndx_data = elf_getdata(t, shndx_data);
sym->idx = symtab->sh.sh_size / sizeof(sym->sym); /* end-of-list */
elf_dirty_reloc_sym(elf, sym); if (!symtab_data) {
void *buf;
symtab->sh.sh_info += 1; if (idx) {
symtab->sh.sh_size += data->d_size; /* we don't do holes in symbol tables */
symtab->changed = true; WARN("index out of range");
return -1;
}
if (symtab_shndx) { /* if @idx == 0, it's the next contiguous entry, create it */
s = elf_getscn(elf->elf, symtab_shndx->idx); symtab_data = elf_newdata(s);
if (!s) { if (t)
WARN_ELF("elf_getscn"); shndx_data = elf_newdata(t);
buf = calloc(1, entsize);
if (!buf) {
WARN("malloc");
return -1;
}
symtab_data->d_buf = buf;
symtab_data->d_size = entsize;
symtab_data->d_align = 1;
symtab_data->d_type = ELF_T_SYM;
symtab->sh.sh_size += entsize;
symtab->changed = true;
if (t) {
shndx_data->d_buf = &sym->sec->idx;
shndx_data->d_size = sizeof(Elf32_Word);
shndx_data->d_align = sizeof(Elf32_Word);
shndx_data->d_type = ELF_T_WORD;
symtab_shndx->sh.sh_size += sizeof(Elf32_Word);
symtab_shndx->changed = true;
}
break;
}
/* empty blocks should not happen */
if (!symtab_data->d_size) {
WARN("zero size data");
return -1; return -1;
} }
shndx_data = elf_newdata(s); /* is this the right block? */
max_idx = symtab_data->d_size / entsize;
if (idx < max_idx)
break;
/* adjust index and try again */
idx -= max_idx;
}
/* something went side-ways */
if (idx < 0) {
WARN("negative index");
return -1;
}
/* setup extended section index magic and write the symbol */
if (shndx >= SHN_UNDEF && shndx < SHN_LORESERVE) {
sym->sym.st_shndx = shndx;
if (!shndx_data)
shndx = 0;
} else {
sym->sym.st_shndx = SHN_XINDEX;
if (!shndx_data) { if (!shndx_data) {
WARN_ELF("elf_newshndx_data"); WARN("no .symtab_shndx");
return -1; return -1;
} }
}
shndx_data->d_buf = &sym->sec->idx; if (!gelf_update_symshndx(symtab_data, shndx_data, idx, &sym->sym, shndx)) {
shndx_data->d_size = sizeof(Elf32_Word); WARN_ELF("gelf_update_symshndx");
shndx_data->d_align = 4; return -1;
shndx_data->d_type = ELF_T_WORD;
symtab_shndx->sh.sh_size += 4;
symtab_shndx->changed = true;
} }
return first_non_local; return 0;
} }
static struct symbol * static struct symbol *
elf_create_section_symbol(struct elf *elf, struct section *sec) elf_create_section_symbol(struct elf *elf, struct section *sec)
{ {
struct section *symtab, *symtab_shndx; struct section *symtab, *symtab_shndx;
Elf_Data *shndx_data = NULL; Elf32_Word first_non_local, new_idx;
struct symbol *sym; struct symbol *sym, *old;
Elf32_Word shndx;
symtab = find_section_by_name(elf, ".symtab"); symtab = find_section_by_name(elf, ".symtab");
if (symtab) { if (symtab) {
symtab_shndx = find_section_by_name(elf, ".symtab_shndx"); symtab_shndx = find_section_by_name(elf, ".symtab_shndx");
if (symtab_shndx)
shndx_data = symtab_shndx->data;
} else { } else {
WARN("no .symtab"); WARN("no .symtab");
return NULL; return NULL;
} }
sym = malloc(sizeof(*sym)); sym = calloc(1, sizeof(*sym));
if (!sym) { if (!sym) {
perror("malloc"); perror("malloc");
return NULL; return NULL;
} }
memset(sym, 0, sizeof(*sym));
sym->idx = elf_move_global_symbol(elf, symtab, symtab_shndx);
if (sym->idx < 0) {
WARN("elf_move_global_symbol");
return NULL;
}
sym->name = sec->name; sym->name = sec->name;
sym->sec = sec; sym->sec = sec;
...@@ -707,24 +753,41 @@ elf_create_section_symbol(struct elf *elf, struct section *sec) ...@@ -707,24 +753,41 @@ elf_create_section_symbol(struct elf *elf, struct section *sec)
// st_other 0 // st_other 0
// st_value 0 // st_value 0
// st_size 0 // st_size 0
shndx = sec->idx;
if (shndx >= SHN_UNDEF && shndx < SHN_LORESERVE) { /*
sym->sym.st_shndx = shndx; * Move the first global symbol, as per sh_info, into a new, higher
if (!shndx_data) * symbol index. This fees up a spot for a new local symbol.
shndx = 0; */
} else { first_non_local = symtab->sh.sh_info;
sym->sym.st_shndx = SHN_XINDEX; new_idx = symtab->sh.sh_size / symtab->sh.sh_entsize;
if (!shndx_data) { old = find_symbol_by_index(elf, first_non_local);
WARN("no .symtab_shndx"); if (old) {
old->idx = new_idx;
hlist_del(&old->hash);
elf_hash_add(symbol, &old->hash, old->idx);
elf_dirty_reloc_sym(elf, old);
if (elf_update_symbol(elf, symtab, symtab_shndx, old)) {
WARN("elf_update_symbol move");
return NULL; return NULL;
} }
new_idx = first_non_local;
} }
if (!gelf_update_symshndx(symtab->data, shndx_data, sym->idx, &sym->sym, shndx)) { sym->idx = new_idx;
WARN_ELF("gelf_update_symshndx"); if (elf_update_symbol(elf, symtab, symtab_shndx, sym)) {
WARN("elf_update_symbol");
return NULL; return NULL;
} }
/*
* Either way, we added a LOCAL symbol.
*/
symtab->sh.sh_info += 1;
elf_add_symbol(elf, sym); elf_add_symbol(elf, sym);
return sym; return sym;
...@@ -843,7 +906,7 @@ static int read_relocs(struct elf *elf) ...@@ -843,7 +906,7 @@ static int read_relocs(struct elf *elf)
tot_reloc += nr_reloc; tot_reloc += nr_reloc;
} }
if (stats) { if (opts.stats) {
printf("max_reloc: %lu\n", max_reloc); printf("max_reloc: %lu\n", max_reloc);
printf("tot_reloc: %lu\n", tot_reloc); printf("tot_reloc: %lu\n", tot_reloc);
printf("reloc_bits: %d\n", elf->reloc_bits); printf("reloc_bits: %d\n", elf->reloc_bits);
...@@ -1222,7 +1285,7 @@ int elf_write(struct elf *elf) ...@@ -1222,7 +1285,7 @@ int elf_write(struct elf *elf)
struct section *sec; struct section *sec;
Elf_Scn *s; Elf_Scn *s;
if (dryrun) if (opts.dryrun)
return 0; return 0;
/* Update changed relocation sections and section headers: */ /* Update changed relocation sections and section headers: */
......
...@@ -8,13 +8,37 @@ ...@@ -8,13 +8,37 @@
#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, backtrace, uaccess, stats,
lto, vmlinux, mcount, noinstr, backup, sls, dryrun, struct opts {
ibt; /* actions: */
bool dump_orc;
bool hack_jump_label;
bool hack_noinstr;
bool ibt;
bool mcount;
bool noinstr;
bool orc;
bool retpoline;
bool sls;
bool stackval;
bool static_call;
bool uaccess;
/* options: */
bool backtrace;
bool backup;
bool dryrun;
bool link;
bool module;
bool no_unreachable;
bool sec_address;
bool stats;
};
extern struct opts opts;
extern int cmd_parse_options(int argc, const char **argv, const char * const usage[]); extern int cmd_parse_options(int argc, const char **argv, const char * const usage[]);
extern int cmd_check(int argc, const char **argv); extern int objtool_run(int argc, const char **argv);
extern int cmd_orc(int argc, const char **argv);
#endif /* _BUILTIN_H */ #endif /* _BUILTIN_H */
...@@ -73,7 +73,7 @@ struct reloc { ...@@ -73,7 +73,7 @@ struct reloc {
struct symbol *sym; struct symbol *sym;
unsigned long offset; unsigned long offset;
unsigned int type; unsigned int type;
long addend; s64 addend;
int idx; int idx;
bool jump_table_start; bool jump_table_start;
}; };
...@@ -86,7 +86,7 @@ struct elf { ...@@ -86,7 +86,7 @@ struct elf {
int fd; int fd;
bool changed; bool changed;
char *name; char *name;
unsigned int text_size; unsigned int text_size, num_files;
struct list_head sections; struct list_head sections;
int symbol_bits; int symbol_bits;
...@@ -131,11 +131,21 @@ static inline u32 reloc_hash(struct reloc *reloc) ...@@ -131,11 +131,21 @@ static inline u32 reloc_hash(struct reloc *reloc)
return sec_offset_hash(reloc->sec, reloc->offset); 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;
}
struct elf *elf_open_read(const char *name, int flags); 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, unsigned int sh_flags, size_t entsize, int nr);
int elf_add_reloc(struct elf *elf, struct section *sec, unsigned long offset, int elf_add_reloc(struct elf *elf, struct section *sec, unsigned long offset,
unsigned int type, struct symbol *sym, long addend); unsigned int type, struct symbol *sym, s64 addend);
int elf_add_reloc_to_insn(struct elf *elf, struct section *sec, int elf_add_reloc_to_insn(struct elf *elf, struct section *sec,
unsigned long offset, unsigned int type, unsigned long offset, unsigned int type,
struct section *insn_sec, unsigned long insn_off); struct section *insn_sec, unsigned long insn_off);
......
...@@ -11,34 +11,33 @@ ...@@ -11,34 +11,33 @@
#include <sys/types.h> #include <sys/types.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <fcntl.h> #include <fcntl.h>
#include <objtool/builtin.h>
#include <objtool/elf.h> #include <objtool/elf.h>
extern const char *objname; extern const char *objname;
static inline char *offstr(struct section *sec, unsigned long offset) static inline char *offstr(struct section *sec, unsigned long offset)
{ {
struct symbol *func; bool is_text = (sec->sh.sh_flags & SHF_EXECINSTR);
char *name, *str; struct symbol *sym = NULL;
unsigned long name_off; char *str;
int len;
func = find_func_containing(sec, offset); if (is_text)
if (!func) sym = find_func_containing(sec, offset);
func = find_symbol_containing(sec, offset); if (!sym)
if (func) { sym = find_symbol_containing(sec, offset);
name = func->name;
name_off = offset - func->offset; if (sym) {
str = malloc(strlen(sym->name) + strlen(sec->name) + 40);
len = sprintf(str, "%s+0x%lx", sym->name, offset - sym->offset);
if (opts.sec_address)
sprintf(str+len, " (%s+0x%lx)", sec->name, offset);
} else { } else {
name = sec->name; str = malloc(strlen(sec->name) + 20);
name_off = offset; sprintf(str, "%s+0x%lx", sec->name, offset);
} }
str = malloc(strlen(name) + 20);
if (func)
sprintf(str, "%s()+0x%lx", name, name_off);
else
sprintf(str, "%s+0x%lx", name, name_off);
return str; return str;
} }
......
...@@ -3,16 +3,6 @@ ...@@ -3,16 +3,6 @@
* Copyright (C) 2015 Josh Poimboeuf <jpoimboe@redhat.com> * Copyright (C) 2015 Josh Poimboeuf <jpoimboe@redhat.com>
*/ */
/*
* objtool:
*
* The 'check' subcmd analyzes every .o file and ensures the validity of its
* stack trace metadata. It enforces a set of rules on asm code and C inline
* assembly code so that stack traces can be reliable.
*
* For more information, see tools/objtool/Documentation/stack-validation.txt.
*/
#include <stdio.h> #include <stdio.h>
#include <stdbool.h> #include <stdbool.h>
#include <string.h> #include <string.h>
...@@ -26,20 +16,6 @@ ...@@ -26,20 +16,6 @@
#include <objtool/objtool.h> #include <objtool/objtool.h>
#include <objtool/warn.h> #include <objtool/warn.h>
struct cmd_struct {
const char *name;
int (*fn)(int, const char **);
const char *help;
};
static const char objtool_usage_string[] =
"objtool COMMAND [ARGS]";
static struct cmd_struct objtool_cmds[] = {
{"check", cmd_check, "Perform stack metadata validation on an object file" },
{"orc", cmd_orc, "Generate in-place ORC unwind tables for an object file" },
};
bool help; bool help;
const char *objname; const char *objname;
...@@ -118,7 +94,7 @@ struct objtool_file *objtool_open_read(const char *_objname) ...@@ -118,7 +94,7 @@ struct objtool_file *objtool_open_read(const char *_objname)
if (!file.elf) if (!file.elf)
return NULL; return NULL;
if (backup && !objtool_create_backup(objname)) { if (opts.backup && !objtool_create_backup(objname)) {
WARN("can't create backup file"); WARN("can't create backup file");
return NULL; return NULL;
} }
...@@ -129,7 +105,7 @@ struct objtool_file *objtool_open_read(const char *_objname) ...@@ -129,7 +105,7 @@ struct objtool_file *objtool_open_read(const char *_objname)
INIT_LIST_HEAD(&file.static_call_list); INIT_LIST_HEAD(&file.static_call_list);
INIT_LIST_HEAD(&file.mcount_loc_list); INIT_LIST_HEAD(&file.mcount_loc_list);
INIT_LIST_HEAD(&file.endbr_list); INIT_LIST_HEAD(&file.endbr_list);
file.ignore_unreachables = no_unreachable; file.ignore_unreachables = opts.no_unreachable;
file.hints = false; file.hints = false;
return &file; return &file;
...@@ -137,7 +113,7 @@ struct objtool_file *objtool_open_read(const char *_objname) ...@@ -137,7 +113,7 @@ struct objtool_file *objtool_open_read(const char *_objname)
void objtool_pv_add(struct objtool_file *f, int idx, struct symbol *func) void objtool_pv_add(struct objtool_file *f, int idx, struct symbol *func)
{ {
if (!noinstr) if (!opts.noinstr)
return; return;
if (!f->pv_ops) { if (!f->pv_ops) {
...@@ -161,70 +137,6 @@ void objtool_pv_add(struct objtool_file *f, int idx, struct symbol *func) ...@@ -161,70 +137,6 @@ void objtool_pv_add(struct objtool_file *f, int idx, struct symbol *func)
f->pv_ops[idx].clean = false; f->pv_ops[idx].clean = false;
} }
static void cmd_usage(void)
{
unsigned int i, longest = 0;
printf("\n usage: %s\n\n", objtool_usage_string);
for (i = 0; i < ARRAY_SIZE(objtool_cmds); i++) {
if (longest < strlen(objtool_cmds[i].name))
longest = strlen(objtool_cmds[i].name);
}
puts(" Commands:");
for (i = 0; i < ARRAY_SIZE(objtool_cmds); i++) {
printf(" %-*s ", longest, objtool_cmds[i].name);
puts(objtool_cmds[i].help);
}
printf("\n");
if (!help)
exit(129);
exit(0);
}
static void handle_options(int *argc, const char ***argv)
{
while (*argc > 0) {
const char *cmd = (*argv)[0];
if (cmd[0] != '-')
break;
if (!strcmp(cmd, "--help") || !strcmp(cmd, "-h")) {
help = true;
break;
} else {
fprintf(stderr, "Unknown option: %s\n", cmd);
cmd_usage();
}
(*argv)++;
(*argc)--;
}
}
static void handle_internal_command(int argc, const char **argv)
{
const char *cmd = argv[0];
unsigned int i, ret;
for (i = 0; i < ARRAY_SIZE(objtool_cmds); i++) {
struct cmd_struct *p = objtool_cmds+i;
if (strcmp(p->name, cmd))
continue;
ret = p->fn(argc, argv);
exit(ret);
}
cmd_usage();
}
int main(int argc, const char **argv) int main(int argc, const char **argv)
{ {
static const char *UNUSED = "OBJTOOL_NOT_IMPLEMENTED"; static const char *UNUSED = "OBJTOOL_NOT_IMPLEMENTED";
...@@ -233,14 +145,7 @@ int main(int argc, const char **argv) ...@@ -233,14 +145,7 @@ int main(int argc, const char **argv)
exec_cmd_init("objtool", UNUSED, UNUSED, UNUSED); exec_cmd_init("objtool", UNUSED, UNUSED, UNUSED);
pager_init(UNUSED); pager_init(UNUSED);
argv++; objtool_run(argc, argv);
argc--;
handle_options(&argc, &argv);
if (!argc || help)
cmd_usage();
handle_internal_command(argc, argv);
return 0; return 0;
} }
...@@ -15,17 +15,12 @@ ...@@ -15,17 +15,12 @@
return ENOSYS; \ return ENOSYS; \
}) })
int __weak check(struct objtool_file *file)
{
UNSUPPORTED("check subcommand");
}
int __weak orc_dump(const char *_objname) int __weak orc_dump(const char *_objname)
{ {
UNSUPPORTED("orc"); UNSUPPORTED("ORC");
} }
int __weak orc_create(struct objtool_file *file) int __weak orc_create(struct objtool_file *file)
{ {
UNSUPPORTED("orc"); UNSUPPORTED("ORC");
} }
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment