objtool changes for v5.10:
- Most of the changes are cleanups and reorganization to make the objtool code more arch-agnostic. This is in preparation for non-x86 support. Fixes: - KASAN fixes. - Handle unreachable trap after call to noreturn functions better. - Ignore unreachable fake jumps. - Misc smaller fixes & cleanups. Signed-off-by: Ingo Molnar <mingo@kernel.org> -----BEGIN PGP SIGNATURE----- iQJFBAABCgAvFiEEBpT5eoXrXCwVQwEKEnMQ0APhK1gFAl+FgwIRHG1pbmdvQGtl cm5lbC5vcmcACgkQEnMQ0APhK1juGw/6A6goA5/HHapM965yG1eY/rTLp3eIbcma 1ZbkUsP0YfT6wVUzw/sOeZzKNOwOq1FuMfkjuH2KcnlxlcMekIaKvLk8uauW4igM hbFGuuZfZ0An5ka9iQ1W6HGdsuD3vVlN1w/kxdWk0c3lJCVQSTxdCfzF8fuF3gxX lF3Bc1D/ZFcHIHT/hu/jeIUCgCYpD3qZDjQJBScSwVthZC+Fw6weLLGp2rKDaCao HhSQft6MUfDrUKfH3LBIUNPRPCOrHo5+AX6BXxLXJVxqlwO/YU3e0GMwSLedMtBy TASWo7/9GAp+wNNZe8EliyTKrfC3sLxN1QImfjuojxbBVXx/YQ/ToTt9fVGpF4Y+ XhhRFv9520v1tS2wPHIgQGwbh7EWG6mdrmo10RAs/31ViONPrbEZ4WmcA08b/5FY KEkOVb18yfmDVzVZPpSc+HpIFkppEBOf7wPg27Bj3RTZmzIl/y+rKSnxROpsJsWb R6iov7SFVET14lHl1G7tPNXfqRaS7HaOQIj3rSUyAP0ZfX+yIupVJp32dc6Ofg8b SddUCwdIHoFdUNz4Y9csUCrewtCVJbxhV4MIdv0GpWbrgSw96RFZgetaH+6mGRpj 0Kh6M1eC3irDbhBuarWUBAr2doPAq4iOUeQU36Q6YSAbCs83Ws2uKOWOHoFBVwCH uSKT0wqqG+E= =KX5o -----END PGP SIGNATURE----- Merge tag 'objtool-core-2020-10-13' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip Pull objtool updates from Ingo Molnar: "Most of the changes are cleanups and reorganization to make the objtool code more arch-agnostic. This is in preparation for non-x86 support. Other changes: - KASAN fixes - Handle unreachable trap after call to noreturn functions better - Ignore unreachable fake jumps - Misc smaller fixes & cleanups" * tag 'objtool-core-2020-10-13' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: (21 commits) perf build: Allow nested externs to enable BUILD_BUG() usage objtool: Allow nested externs to enable BUILD_BUG() objtool: Permit __kasan_check_{read,write} under UACCESS objtool: Ignore unreachable trap after call to noreturn functions objtool: Handle calling non-function symbols in other sections objtool: Ignore unreachable fake jumps objtool: Remove useless tests before save_reg() objtool: Decode unwind hint register depending on architecture objtool: Make unwind hint definitions available to other architectures objtool: Only include valid definitions depending on source file type objtool: Rename frame.h -> objtool.h objtool: Refactor jump table code to support other architectures objtool: Make relocation in alternative handling arch dependent objtool: Abstract alternative special case handling objtool: Move macros describing structures to arch-dependent code objtool: Make sync-check consider the target architecture objtool: Group headers to check in a single list objtool: Define 'struct orc_entry' only when needed objtool: Skip ORC entry creation for non-text sections objtool: Move ORC logic out of check() ...
This commit is contained in:
commit
6873139ed0
|
@ -12515,6 +12515,7 @@ M: Josh Poimboeuf <jpoimboe@redhat.com>
|
|||
M: Peter Zijlstra <peterz@infradead.org>
|
||||
S: Supported
|
||||
F: tools/objtool/
|
||||
F: include/linux/objtool.h
|
||||
|
||||
OCELOT ETHERNET SWITCH DRIVER
|
||||
M: Microchip Linux Driver Support <UNGLinuxDriver@microchip.com>
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
#define _ASM_X86_NOSPEC_BRANCH_H_
|
||||
|
||||
#include <linux/static_key.h>
|
||||
#include <linux/frame.h>
|
||||
#include <linux/objtool.h>
|
||||
|
||||
#include <asm/alternative.h>
|
||||
#include <asm/alternative-asm.h>
|
||||
|
|
|
@ -39,27 +39,6 @@
|
|||
#define ORC_REG_SP_INDIRECT 9
|
||||
#define ORC_REG_MAX 15
|
||||
|
||||
/*
|
||||
* ORC_TYPE_CALL: Indicates that sp_reg+sp_offset resolves to PREV_SP (the
|
||||
* caller's SP right before it made the call). Used for all callable
|
||||
* functions, i.e. all C code and all callable asm functions.
|
||||
*
|
||||
* ORC_TYPE_REGS: Used in entry code to indicate that sp_reg+sp_offset points
|
||||
* to a fully populated pt_regs from a syscall, interrupt, or exception.
|
||||
*
|
||||
* ORC_TYPE_REGS_IRET: Used in entry code to indicate that sp_reg+sp_offset
|
||||
* points to the iret return frame.
|
||||
*
|
||||
* The UNWIND_HINT macros are used only for the unwind_hint struct. They
|
||||
* aren't used in struct orc_entry due to size and complexity constraints.
|
||||
* Objtool converts them to real types when it converts the hints to orc
|
||||
* entries.
|
||||
*/
|
||||
#define ORC_TYPE_CALL 0
|
||||
#define ORC_TYPE_REGS 1
|
||||
#define ORC_TYPE_REGS_IRET 2
|
||||
#define UNWIND_HINT_TYPE_RET_OFFSET 3
|
||||
|
||||
#ifndef __ASSEMBLY__
|
||||
/*
|
||||
* This struct is more or less a vastly simplified version of the DWARF Call
|
||||
|
@ -78,19 +57,6 @@ struct orc_entry {
|
|||
unsigned end:1;
|
||||
} __packed;
|
||||
|
||||
/*
|
||||
* This struct is used by asm and inline asm code to manually annotate the
|
||||
* location of registers on the stack for the ORC unwinder.
|
||||
*
|
||||
* Type can be either ORC_TYPE_* or UNWIND_HINT_TYPE_*.
|
||||
*/
|
||||
struct unwind_hint {
|
||||
u32 ip;
|
||||
s16 sp_offset;
|
||||
u8 sp_reg;
|
||||
u8 type;
|
||||
u8 end;
|
||||
};
|
||||
#endif /* __ASSEMBLY__ */
|
||||
|
||||
#endif /* _ORC_TYPES_H */
|
||||
|
|
|
@ -1,51 +1,17 @@
|
|||
#ifndef _ASM_X86_UNWIND_HINTS_H
|
||||
#define _ASM_X86_UNWIND_HINTS_H
|
||||
|
||||
#include <linux/objtool.h>
|
||||
|
||||
#include "orc_types.h"
|
||||
|
||||
#ifdef __ASSEMBLY__
|
||||
|
||||
/*
|
||||
* In asm, there are two kinds of code: normal C-type callable functions and
|
||||
* the rest. The normal callable functions can be called by other code, and
|
||||
* don't do anything unusual with the stack. Such normal callable functions
|
||||
* are annotated with the ENTRY/ENDPROC macros. Most asm code falls in this
|
||||
* category. In this case, no special debugging annotations are needed because
|
||||
* objtool can automatically generate the ORC data for the ORC unwinder to read
|
||||
* at runtime.
|
||||
*
|
||||
* Anything which doesn't fall into the above category, such as syscall and
|
||||
* interrupt handlers, tends to not be called directly by other functions, and
|
||||
* often does unusual non-C-function-type things with the stack pointer. Such
|
||||
* code needs to be annotated such that objtool can understand it. The
|
||||
* following CFI hint macros are for this type of code.
|
||||
*
|
||||
* These macros provide hints to objtool about the state of the stack at each
|
||||
* instruction. Objtool starts from the hints and follows the code flow,
|
||||
* making automatic CFI adjustments when it sees pushes and pops, filling out
|
||||
* the debuginfo as necessary. It will also warn if it sees any
|
||||
* inconsistencies.
|
||||
*/
|
||||
.macro UNWIND_HINT sp_reg=ORC_REG_SP sp_offset=0 type=ORC_TYPE_CALL end=0
|
||||
#ifdef CONFIG_STACK_VALIDATION
|
||||
.Lunwind_hint_ip_\@:
|
||||
.pushsection .discard.unwind_hints
|
||||
/* struct unwind_hint */
|
||||
.long .Lunwind_hint_ip_\@ - .
|
||||
.short \sp_offset
|
||||
.byte \sp_reg
|
||||
.byte \type
|
||||
.byte \end
|
||||
.balign 4
|
||||
.popsection
|
||||
#endif
|
||||
.endm
|
||||
|
||||
.macro UNWIND_HINT_EMPTY
|
||||
UNWIND_HINT sp_reg=ORC_REG_UNDEFINED end=1
|
||||
UNWIND_HINT sp_reg=ORC_REG_UNDEFINED type=UNWIND_HINT_TYPE_CALL end=1
|
||||
.endm
|
||||
|
||||
.macro UNWIND_HINT_REGS base=%rsp offset=0 indirect=0 extra=1 iret=0
|
||||
.macro UNWIND_HINT_REGS base=%rsp offset=0 indirect=0 extra=1 partial=0
|
||||
.if \base == %rsp
|
||||
.if \indirect
|
||||
.set sp_reg, ORC_REG_SP_INDIRECT
|
||||
|
@ -66,24 +32,24 @@
|
|||
|
||||
.set sp_offset, \offset
|
||||
|
||||
.if \iret
|
||||
.set type, ORC_TYPE_REGS_IRET
|
||||
.if \partial
|
||||
.set type, UNWIND_HINT_TYPE_REGS_PARTIAL
|
||||
.elseif \extra == 0
|
||||
.set type, ORC_TYPE_REGS_IRET
|
||||
.set type, UNWIND_HINT_TYPE_REGS_PARTIAL
|
||||
.set sp_offset, \offset + (16*8)
|
||||
.else
|
||||
.set type, ORC_TYPE_REGS
|
||||
.set type, UNWIND_HINT_TYPE_REGS
|
||||
.endif
|
||||
|
||||
UNWIND_HINT sp_reg=sp_reg sp_offset=sp_offset type=type
|
||||
.endm
|
||||
|
||||
.macro UNWIND_HINT_IRET_REGS base=%rsp offset=0
|
||||
UNWIND_HINT_REGS base=\base offset=\offset iret=1
|
||||
UNWIND_HINT_REGS base=\base offset=\offset partial=1
|
||||
.endm
|
||||
|
||||
.macro UNWIND_HINT_FUNC sp_offset=8
|
||||
UNWIND_HINT sp_offset=\sp_offset
|
||||
UNWIND_HINT sp_reg=ORC_REG_SP sp_offset=\sp_offset type=UNWIND_HINT_TYPE_CALL
|
||||
.endm
|
||||
|
||||
/*
|
||||
|
@ -92,7 +58,7 @@
|
|||
* initial_func_cfi.
|
||||
*/
|
||||
.macro UNWIND_HINT_RET_OFFSET sp_offset=8
|
||||
UNWIND_HINT type=UNWIND_HINT_TYPE_RET_OFFSET sp_offset=\sp_offset
|
||||
UNWIND_HINT sp_reg=ORC_REG_SP type=UNWIND_HINT_TYPE_RET_OFFSET sp_offset=\sp_offset
|
||||
.endm
|
||||
|
||||
#endif /* __ASSEMBLY__ */
|
||||
|
|
|
@ -38,9 +38,9 @@
|
|||
#include <linux/kdebug.h>
|
||||
#include <linux/kallsyms.h>
|
||||
#include <linux/ftrace.h>
|
||||
#include <linux/frame.h>
|
||||
#include <linux/kasan.h>
|
||||
#include <linux/moduleloader.h>
|
||||
#include <linux/objtool.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/pgtable.h>
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
#include <linux/kdebug.h>
|
||||
#include <linux/kallsyms.h>
|
||||
#include <linux/ftrace.h>
|
||||
#include <linux/frame.h>
|
||||
#include <linux/objtool.h>
|
||||
#include <linux/pgtable.h>
|
||||
#include <linux/static_call.h>
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
#include <linux/sched.h>
|
||||
#include <linux/tboot.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/frame.h>
|
||||
#include <linux/objtool.h>
|
||||
#include <linux/pgtable.h>
|
||||
#include <acpi/reboot.h>
|
||||
#include <asm/io.h>
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
#include <linux/objtool.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/sort.h>
|
||||
#include <asm/ptrace.h>
|
||||
|
@ -127,12 +128,12 @@ static struct orc_entry null_orc_entry = {
|
|||
.sp_offset = sizeof(long),
|
||||
.sp_reg = ORC_REG_SP,
|
||||
.bp_reg = ORC_REG_UNDEFINED,
|
||||
.type = ORC_TYPE_CALL
|
||||
.type = UNWIND_HINT_TYPE_CALL
|
||||
};
|
||||
|
||||
/* Fake frame pointer entry -- used as a fallback for generated code */
|
||||
static struct orc_entry orc_fp_entry = {
|
||||
.type = ORC_TYPE_CALL,
|
||||
.type = UNWIND_HINT_TYPE_CALL,
|
||||
.sp_reg = ORC_REG_BP,
|
||||
.sp_offset = 16,
|
||||
.bp_reg = ORC_REG_PREV_SP,
|
||||
|
@ -531,7 +532,7 @@ bool unwind_next_frame(struct unwind_state *state)
|
|||
|
||||
/* Find IP, SP and possibly regs: */
|
||||
switch (orc->type) {
|
||||
case ORC_TYPE_CALL:
|
||||
case UNWIND_HINT_TYPE_CALL:
|
||||
ip_p = sp - sizeof(long);
|
||||
|
||||
if (!deref_stack_reg(state, ip_p, &state->ip))
|
||||
|
@ -546,7 +547,7 @@ bool unwind_next_frame(struct unwind_state *state)
|
|||
state->signal = false;
|
||||
break;
|
||||
|
||||
case ORC_TYPE_REGS:
|
||||
case UNWIND_HINT_TYPE_REGS:
|
||||
if (!deref_stack_regs(state, sp, &state->ip, &state->sp)) {
|
||||
orc_warn_current("can't access registers at %pB\n",
|
||||
(void *)orig_ip);
|
||||
|
@ -559,7 +560,7 @@ bool unwind_next_frame(struct unwind_state *state)
|
|||
state->signal = true;
|
||||
break;
|
||||
|
||||
case ORC_TYPE_REGS_IRET:
|
||||
case UNWIND_HINT_TYPE_REGS_PARTIAL:
|
||||
if (!deref_stack_iret_regs(state, sp, &state->ip, &state->sp)) {
|
||||
orc_warn_current("can't access iret registers at %pB\n",
|
||||
(void *)orig_ip);
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
#include <linux/trace_events.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/hashtable.h>
|
||||
#include <linux/frame.h>
|
||||
#include <linux/objtool.h>
|
||||
#include <linux/psp-sev.h>
|
||||
#include <linux/file.h>
|
||||
#include <linux/pagemap.h>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
#include <linux/frame.h>
|
||||
#include <linux/objtool.h>
|
||||
#include <linux/percpu.h>
|
||||
|
||||
#include <asm/debugreg.h>
|
||||
|
|
|
@ -13,7 +13,6 @@
|
|||
* Yaniv Kamay <yaniv@qumranet.com>
|
||||
*/
|
||||
|
||||
#include <linux/frame.h>
|
||||
#include <linux/highmem.h>
|
||||
#include <linux/hrtimer.h>
|
||||
#include <linux/kernel.h>
|
||||
|
@ -22,6 +21,7 @@
|
|||
#include <linux/moduleparam.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/objtool.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/sched/smt.h>
|
||||
#include <linux/slab.h>
|
||||
|
|
|
@ -32,7 +32,7 @@
|
|||
#include <linux/pci.h>
|
||||
#include <linux/gfp.h>
|
||||
#include <linux/edd.h>
|
||||
#include <linux/frame.h>
|
||||
#include <linux/objtool.h>
|
||||
|
||||
#include <xen/xen.h>
|
||||
#include <xen/events.h>
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
*
|
||||
*/
|
||||
|
||||
#include <linux/frame.h>
|
||||
#include <linux/objtool.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
|
@ -599,4 +599,3 @@ out_open:
|
|||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,35 +0,0 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
#ifndef _LINUX_FRAME_H
|
||||
#define _LINUX_FRAME_H
|
||||
|
||||
#ifdef CONFIG_STACK_VALIDATION
|
||||
/*
|
||||
* This macro marks the given function's stack frame as "non-standard", which
|
||||
* tells objtool to ignore the function when doing stack metadata validation.
|
||||
* It should only be used in special cases where you're 100% sure it won't
|
||||
* affect the reliability of frame pointers and kernel stack traces.
|
||||
*
|
||||
* For more information, see tools/objtool/Documentation/stack-validation.txt.
|
||||
*/
|
||||
#define STACK_FRAME_NON_STANDARD(func) \
|
||||
static void __used __section(.discard.func_stack_frame_non_standard) \
|
||||
*__func_stack_frame_non_standard_##func = func
|
||||
|
||||
/*
|
||||
* This macro indicates that the following intra-function call is valid.
|
||||
* Any non-annotated intra-function call will cause objtool to issue a warning.
|
||||
*/
|
||||
#define ANNOTATE_INTRA_FUNCTION_CALL \
|
||||
999: \
|
||||
.pushsection .discard.intra_function_calls; \
|
||||
.long 999b; \
|
||||
.popsection;
|
||||
|
||||
#else /* !CONFIG_STACK_VALIDATION */
|
||||
|
||||
#define STACK_FRAME_NON_STANDARD(func)
|
||||
#define ANNOTATE_INTRA_FUNCTION_CALL
|
||||
|
||||
#endif /* CONFIG_STACK_VALIDATION */
|
||||
|
||||
#endif /* _LINUX_FRAME_H */
|
|
@ -0,0 +1,129 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
#ifndef _LINUX_OBJTOOL_H
|
||||
#define _LINUX_OBJTOOL_H
|
||||
|
||||
#ifndef __ASSEMBLY__
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
/*
|
||||
* This struct is used by asm and inline asm code to manually annotate the
|
||||
* location of registers on the stack.
|
||||
*/
|
||||
struct unwind_hint {
|
||||
u32 ip;
|
||||
s16 sp_offset;
|
||||
u8 sp_reg;
|
||||
u8 type;
|
||||
u8 end;
|
||||
};
|
||||
#endif
|
||||
|
||||
/*
|
||||
* UNWIND_HINT_TYPE_CALL: Indicates that sp_reg+sp_offset resolves to PREV_SP
|
||||
* (the caller's SP right before it made the call). Used for all callable
|
||||
* functions, i.e. all C code and all callable asm functions.
|
||||
*
|
||||
* UNWIND_HINT_TYPE_REGS: Used in entry code to indicate that sp_reg+sp_offset
|
||||
* points to a fully populated pt_regs from a syscall, interrupt, or exception.
|
||||
*
|
||||
* UNWIND_HINT_TYPE_REGS_PARTIAL: Used in entry code to indicate that
|
||||
* sp_reg+sp_offset points to the iret return frame.
|
||||
*/
|
||||
#define UNWIND_HINT_TYPE_CALL 0
|
||||
#define UNWIND_HINT_TYPE_REGS 1
|
||||
#define UNWIND_HINT_TYPE_REGS_PARTIAL 2
|
||||
#define UNWIND_HINT_TYPE_RET_OFFSET 3
|
||||
|
||||
#ifdef CONFIG_STACK_VALIDATION
|
||||
|
||||
#ifndef __ASSEMBLY__
|
||||
|
||||
#define UNWIND_HINT(sp_reg, sp_offset, type, end) \
|
||||
"987: \n\t" \
|
||||
".pushsection .discard.unwind_hints\n\t" \
|
||||
/* struct unwind_hint */ \
|
||||
".long 987b - .\n\t" \
|
||||
".short " __stringify(sp_offset) "\n\t" \
|
||||
".byte " __stringify(sp_reg) "\n\t" \
|
||||
".byte " __stringify(type) "\n\t" \
|
||||
".byte " __stringify(end) "\n\t" \
|
||||
".balign 4 \n\t" \
|
||||
".popsection\n\t"
|
||||
|
||||
/*
|
||||
* This macro marks the given function's stack frame as "non-standard", which
|
||||
* tells objtool to ignore the function when doing stack metadata validation.
|
||||
* It should only be used in special cases where you're 100% sure it won't
|
||||
* affect the reliability of frame pointers and kernel stack traces.
|
||||
*
|
||||
* For more information, see tools/objtool/Documentation/stack-validation.txt.
|
||||
*/
|
||||
#define STACK_FRAME_NON_STANDARD(func) \
|
||||
static void __used __section(.discard.func_stack_frame_non_standard) \
|
||||
*__func_stack_frame_non_standard_##func = func
|
||||
|
||||
#else /* __ASSEMBLY__ */
|
||||
|
||||
/*
|
||||
* This macro indicates that the following intra-function call is valid.
|
||||
* Any non-annotated intra-function call will cause objtool to issue a warning.
|
||||
*/
|
||||
#define ANNOTATE_INTRA_FUNCTION_CALL \
|
||||
999: \
|
||||
.pushsection .discard.intra_function_calls; \
|
||||
.long 999b; \
|
||||
.popsection;
|
||||
|
||||
/*
|
||||
* In asm, there are two kinds of code: normal C-type callable functions and
|
||||
* the rest. The normal callable functions can be called by other code, and
|
||||
* don't do anything unusual with the stack. Such normal callable functions
|
||||
* are annotated with the ENTRY/ENDPROC macros. Most asm code falls in this
|
||||
* category. In this case, no special debugging annotations are needed because
|
||||
* objtool can automatically generate the ORC data for the ORC unwinder to read
|
||||
* at runtime.
|
||||
*
|
||||
* Anything which doesn't fall into the above category, such as syscall and
|
||||
* interrupt handlers, tends to not be called directly by other functions, and
|
||||
* often does unusual non-C-function-type things with the stack pointer. Such
|
||||
* code needs to be annotated such that objtool can understand it. The
|
||||
* following CFI hint macros are for this type of code.
|
||||
*
|
||||
* These macros provide hints to objtool about the state of the stack at each
|
||||
* instruction. Objtool starts from the hints and follows the code flow,
|
||||
* making automatic CFI adjustments when it sees pushes and pops, filling out
|
||||
* the debuginfo as necessary. It will also warn if it sees any
|
||||
* inconsistencies.
|
||||
*/
|
||||
.macro UNWIND_HINT sp_reg:req sp_offset=0 type:req end=0
|
||||
.Lunwind_hint_ip_\@:
|
||||
.pushsection .discard.unwind_hints
|
||||
/* struct unwind_hint */
|
||||
.long .Lunwind_hint_ip_\@ - .
|
||||
.short \sp_offset
|
||||
.byte \sp_reg
|
||||
.byte \type
|
||||
.byte \end
|
||||
.balign 4
|
||||
.popsection
|
||||
.endm
|
||||
|
||||
#endif /* __ASSEMBLY__ */
|
||||
|
||||
#else /* !CONFIG_STACK_VALIDATION */
|
||||
|
||||
#ifndef __ASSEMBLY__
|
||||
|
||||
#define UNWIND_HINT(sp_reg, sp_offset, type, end) \
|
||||
"\n\t"
|
||||
#define STACK_FRAME_NON_STANDARD(func)
|
||||
#else
|
||||
#define ANNOTATE_INTRA_FUNCTION_CALL
|
||||
.macro UNWIND_HINT sp_reg:req sp_offset=0 type:req end=0
|
||||
.endm
|
||||
#endif
|
||||
|
||||
#endif /* CONFIG_STACK_VALIDATION */
|
||||
|
||||
#endif /* _LINUX_OBJTOOL_H */
|
|
@ -25,7 +25,7 @@
|
|||
#include <linux/moduleloader.h>
|
||||
#include <linux/bpf.h>
|
||||
#include <linux/btf.h>
|
||||
#include <linux/frame.h>
|
||||
#include <linux/objtool.h>
|
||||
#include <linux/rbtree_latch.h>
|
||||
#include <linux/kallsyms.h>
|
||||
#include <linux/rcupdate.h>
|
||||
|
|
|
@ -36,7 +36,7 @@
|
|||
#include <linux/syscore_ops.h>
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/hugetlb.h>
|
||||
#include <linux/frame.h>
|
||||
#include <linux/objtool.h>
|
||||
|
||||
#include <asm/page.h>
|
||||
#include <asm/sections.h>
|
||||
|
|
|
@ -39,27 +39,6 @@
|
|||
#define ORC_REG_SP_INDIRECT 9
|
||||
#define ORC_REG_MAX 15
|
||||
|
||||
/*
|
||||
* ORC_TYPE_CALL: Indicates that sp_reg+sp_offset resolves to PREV_SP (the
|
||||
* caller's SP right before it made the call). Used for all callable
|
||||
* functions, i.e. all C code and all callable asm functions.
|
||||
*
|
||||
* ORC_TYPE_REGS: Used in entry code to indicate that sp_reg+sp_offset points
|
||||
* to a fully populated pt_regs from a syscall, interrupt, or exception.
|
||||
*
|
||||
* ORC_TYPE_REGS_IRET: Used in entry code to indicate that sp_reg+sp_offset
|
||||
* points to the iret return frame.
|
||||
*
|
||||
* The UNWIND_HINT macros are used only for the unwind_hint struct. They
|
||||
* aren't used in struct orc_entry due to size and complexity constraints.
|
||||
* Objtool converts them to real types when it converts the hints to orc
|
||||
* entries.
|
||||
*/
|
||||
#define ORC_TYPE_CALL 0
|
||||
#define ORC_TYPE_REGS 1
|
||||
#define ORC_TYPE_REGS_IRET 2
|
||||
#define UNWIND_HINT_TYPE_RET_OFFSET 3
|
||||
|
||||
#ifndef __ASSEMBLY__
|
||||
/*
|
||||
* This struct is more or less a vastly simplified version of the DWARF Call
|
||||
|
@ -78,19 +57,6 @@ struct orc_entry {
|
|||
unsigned end:1;
|
||||
} __packed;
|
||||
|
||||
/*
|
||||
* This struct is used by asm and inline asm code to manually annotate the
|
||||
* location of registers on the stack for the ORC unwinder.
|
||||
*
|
||||
* Type can be either ORC_TYPE_* or UNWIND_HINT_TYPE_*.
|
||||
*/
|
||||
struct unwind_hint {
|
||||
u32 ip;
|
||||
s16 sp_offset;
|
||||
u8 sp_reg;
|
||||
u8 type;
|
||||
u8 end;
|
||||
};
|
||||
#endif /* __ASSEMBLY__ */
|
||||
|
||||
#endif /* _ORC_TYPES_H */
|
||||
|
|
|
@ -0,0 +1,129 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
#ifndef _LINUX_OBJTOOL_H
|
||||
#define _LINUX_OBJTOOL_H
|
||||
|
||||
#ifndef __ASSEMBLY__
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
/*
|
||||
* This struct is used by asm and inline asm code to manually annotate the
|
||||
* location of registers on the stack.
|
||||
*/
|
||||
struct unwind_hint {
|
||||
u32 ip;
|
||||
s16 sp_offset;
|
||||
u8 sp_reg;
|
||||
u8 type;
|
||||
u8 end;
|
||||
};
|
||||
#endif
|
||||
|
||||
/*
|
||||
* UNWIND_HINT_TYPE_CALL: Indicates that sp_reg+sp_offset resolves to PREV_SP
|
||||
* (the caller's SP right before it made the call). Used for all callable
|
||||
* functions, i.e. all C code and all callable asm functions.
|
||||
*
|
||||
* UNWIND_HINT_TYPE_REGS: Used in entry code to indicate that sp_reg+sp_offset
|
||||
* points to a fully populated pt_regs from a syscall, interrupt, or exception.
|
||||
*
|
||||
* UNWIND_HINT_TYPE_REGS_PARTIAL: Used in entry code to indicate that
|
||||
* sp_reg+sp_offset points to the iret return frame.
|
||||
*/
|
||||
#define UNWIND_HINT_TYPE_CALL 0
|
||||
#define UNWIND_HINT_TYPE_REGS 1
|
||||
#define UNWIND_HINT_TYPE_REGS_PARTIAL 2
|
||||
#define UNWIND_HINT_TYPE_RET_OFFSET 3
|
||||
|
||||
#ifdef CONFIG_STACK_VALIDATION
|
||||
|
||||
#ifndef __ASSEMBLY__
|
||||
|
||||
#define UNWIND_HINT(sp_reg, sp_offset, type, end) \
|
||||
"987: \n\t" \
|
||||
".pushsection .discard.unwind_hints\n\t" \
|
||||
/* struct unwind_hint */ \
|
||||
".long 987b - .\n\t" \
|
||||
".short " __stringify(sp_offset) "\n\t" \
|
||||
".byte " __stringify(sp_reg) "\n\t" \
|
||||
".byte " __stringify(type) "\n\t" \
|
||||
".byte " __stringify(end) "\n\t" \
|
||||
".balign 4 \n\t" \
|
||||
".popsection\n\t"
|
||||
|
||||
/*
|
||||
* This macro marks the given function's stack frame as "non-standard", which
|
||||
* tells objtool to ignore the function when doing stack metadata validation.
|
||||
* It should only be used in special cases where you're 100% sure it won't
|
||||
* affect the reliability of frame pointers and kernel stack traces.
|
||||
*
|
||||
* For more information, see tools/objtool/Documentation/stack-validation.txt.
|
||||
*/
|
||||
#define STACK_FRAME_NON_STANDARD(func) \
|
||||
static void __used __section(.discard.func_stack_frame_non_standard) \
|
||||
*__func_stack_frame_non_standard_##func = func
|
||||
|
||||
#else /* __ASSEMBLY__ */
|
||||
|
||||
/*
|
||||
* This macro indicates that the following intra-function call is valid.
|
||||
* Any non-annotated intra-function call will cause objtool to issue a warning.
|
||||
*/
|
||||
#define ANNOTATE_INTRA_FUNCTION_CALL \
|
||||
999: \
|
||||
.pushsection .discard.intra_function_calls; \
|
||||
.long 999b; \
|
||||
.popsection;
|
||||
|
||||
/*
|
||||
* In asm, there are two kinds of code: normal C-type callable functions and
|
||||
* the rest. The normal callable functions can be called by other code, and
|
||||
* don't do anything unusual with the stack. Such normal callable functions
|
||||
* are annotated with the ENTRY/ENDPROC macros. Most asm code falls in this
|
||||
* category. In this case, no special debugging annotations are needed because
|
||||
* objtool can automatically generate the ORC data for the ORC unwinder to read
|
||||
* at runtime.
|
||||
*
|
||||
* Anything which doesn't fall into the above category, such as syscall and
|
||||
* interrupt handlers, tends to not be called directly by other functions, and
|
||||
* often does unusual non-C-function-type things with the stack pointer. Such
|
||||
* code needs to be annotated such that objtool can understand it. The
|
||||
* following CFI hint macros are for this type of code.
|
||||
*
|
||||
* These macros provide hints to objtool about the state of the stack at each
|
||||
* instruction. Objtool starts from the hints and follows the code flow,
|
||||
* making automatic CFI adjustments when it sees pushes and pops, filling out
|
||||
* the debuginfo as necessary. It will also warn if it sees any
|
||||
* inconsistencies.
|
||||
*/
|
||||
.macro UNWIND_HINT sp_reg:req sp_offset=0 type:req end=0
|
||||
.Lunwind_hint_ip_\@:
|
||||
.pushsection .discard.unwind_hints
|
||||
/* struct unwind_hint */
|
||||
.long .Lunwind_hint_ip_\@ - .
|
||||
.short \sp_offset
|
||||
.byte \sp_reg
|
||||
.byte \type
|
||||
.byte \end
|
||||
.balign 4
|
||||
.popsection
|
||||
.endm
|
||||
|
||||
#endif /* __ASSEMBLY__ */
|
||||
|
||||
#else /* !CONFIG_STACK_VALIDATION */
|
||||
|
||||
#ifndef __ASSEMBLY__
|
||||
|
||||
#define UNWIND_HINT(sp_reg, sp_offset, type, end) \
|
||||
"\n\t"
|
||||
#define STACK_FRAME_NON_STANDARD(func)
|
||||
#else
|
||||
#define ANNOTATE_INTRA_FUNCTION_CALL
|
||||
.macro UNWIND_HINT sp_reg:req sp_offset=0 type:req end=0
|
||||
.endm
|
||||
#endif
|
||||
|
||||
#endif /* CONFIG_STACK_VALIDATION */
|
||||
|
||||
#endif /* _LINUX_OBJTOOL_H */
|
|
@ -37,7 +37,7 @@ INCLUDES := -I$(srctree)/tools/include \
|
|||
-I$(srctree)/tools/arch/$(HOSTARCH)/include/uapi \
|
||||
-I$(srctree)/tools/arch/$(SRCARCH)/include \
|
||||
-I$(srctree)/tools/objtool/arch/$(SRCARCH)/include
|
||||
WARNINGS := $(EXTRA_WARNINGS) -Wno-switch-default -Wno-switch-enum -Wno-packed
|
||||
WARNINGS := $(EXTRA_WARNINGS) -Wno-switch-default -Wno-switch-enum -Wno-packed -Wno-nested-externs
|
||||
CFLAGS := -Werror $(WARNINGS) $(KBUILD_HOSTCFLAGS) -g $(INCLUDES) $(LIBELF_FLAGS)
|
||||
LDFLAGS += $(LIBELF_LIBS) $(LIBSUBCMD) $(KBUILD_HOSTLDFLAGS)
|
||||
|
||||
|
@ -55,6 +55,10 @@ ifeq ($(SRCARCH),x86)
|
|||
SUBCMD_ORC := y
|
||||
endif
|
||||
|
||||
ifeq ($(SUBCMD_ORC),y)
|
||||
CFLAGS += -DINSN_USE_ORC
|
||||
endif
|
||||
|
||||
export SUBCMD_CHECK SUBCMD_ORC
|
||||
export srctree OUTPUT CFLAGS SRCARCH AWK
|
||||
include $(srctree)/tools/build/Makefile.include
|
||||
|
|
|
@ -11,7 +11,9 @@
|
|||
#include "objtool.h"
|
||||
#include "cfi.h"
|
||||
|
||||
#ifdef INSN_USE_ORC
|
||||
#include <asm/orc_types.h>
|
||||
#endif
|
||||
|
||||
enum insn_type {
|
||||
INSN_JUMP_CONDITIONAL,
|
||||
|
@ -86,4 +88,6 @@ unsigned long arch_dest_reloc_offset(int addend);
|
|||
|
||||
const char *arch_nop_insn(int len);
|
||||
|
||||
int arch_decode_hint_reg(struct instruction *insn, u8 sp_reg);
|
||||
|
||||
#endif /* _ARCH_H */
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
objtool-y += special.o
|
||||
objtool-y += decode.o
|
||||
|
||||
inat_tables_script = ../arch/x86/tools/gen-insn-attr-x86.awk
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include "../../elf.h"
|
||||
#include "../../arch.h"
|
||||
#include "../../warn.h"
|
||||
#include <asm/orc_types.h>
|
||||
|
||||
static unsigned char op_to_cfi_reg[][2] = {
|
||||
{CFI_AX, CFI_R8},
|
||||
|
@ -583,3 +584,39 @@ const char *arch_nop_insn(int len)
|
|||
|
||||
return nops[len-1];
|
||||
}
|
||||
|
||||
int arch_decode_hint_reg(struct instruction *insn, u8 sp_reg)
|
||||
{
|
||||
struct cfi_reg *cfa = &insn->cfi.cfa;
|
||||
|
||||
switch (sp_reg) {
|
||||
case ORC_REG_UNDEFINED:
|
||||
cfa->base = CFI_UNDEFINED;
|
||||
break;
|
||||
case ORC_REG_SP:
|
||||
cfa->base = CFI_SP;
|
||||
break;
|
||||
case ORC_REG_BP:
|
||||
cfa->base = CFI_BP;
|
||||
break;
|
||||
case ORC_REG_SP_INDIRECT:
|
||||
cfa->base = CFI_SP_INDIRECT;
|
||||
break;
|
||||
case ORC_REG_R10:
|
||||
cfa->base = CFI_R10;
|
||||
break;
|
||||
case ORC_REG_R13:
|
||||
cfa->base = CFI_R13;
|
||||
break;
|
||||
case ORC_REG_DI:
|
||||
cfa->base = CFI_DI;
|
||||
break;
|
||||
case ORC_REG_DX:
|
||||
cfa->base = CFI_DX;
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
#ifndef _X86_ARCH_SPECIAL_H
|
||||
#define _X86_ARCH_SPECIAL_H
|
||||
|
||||
#define EX_ENTRY_SIZE 12
|
||||
#define EX_ORIG_OFFSET 0
|
||||
#define EX_NEW_OFFSET 4
|
||||
|
||||
#define JUMP_ENTRY_SIZE 16
|
||||
#define JUMP_ORIG_OFFSET 0
|
||||
#define JUMP_NEW_OFFSET 4
|
||||
|
||||
#define ALT_ENTRY_SIZE 13
|
||||
#define ALT_ORIG_OFFSET 0
|
||||
#define ALT_NEW_OFFSET 4
|
||||
#define ALT_FEATURE_OFFSET 8
|
||||
#define ALT_ORIG_LEN_OFFSET 10
|
||||
#define ALT_NEW_LEN_OFFSET 11
|
||||
|
||||
#endif /* _X86_ARCH_SPECIAL_H */
|
|
@ -0,0 +1,145 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
#include <string.h>
|
||||
|
||||
#include "../../special.h"
|
||||
#include "../../builtin.h"
|
||||
|
||||
#define X86_FEATURE_POPCNT (4 * 32 + 23)
|
||||
#define X86_FEATURE_SMAP (9 * 32 + 20)
|
||||
|
||||
void arch_handle_alternative(unsigned short feature, struct special_alt *alt)
|
||||
{
|
||||
switch (feature) {
|
||||
case X86_FEATURE_SMAP:
|
||||
/*
|
||||
* If UACCESS validation is enabled; force that alternative;
|
||||
* otherwise force it the other way.
|
||||
*
|
||||
* What we want to avoid is having both the original and the
|
||||
* alternative code flow at the same time, in that case we can
|
||||
* find paths that see the STAC but take the NOP instead of
|
||||
* CLAC and the other way around.
|
||||
*/
|
||||
if (uaccess)
|
||||
alt->skip_orig = true;
|
||||
else
|
||||
alt->skip_alt = true;
|
||||
break;
|
||||
case X86_FEATURE_POPCNT:
|
||||
/*
|
||||
* It has been requested that we don't validate the !POPCNT
|
||||
* feature path which is a "very very small percentage of
|
||||
* machines".
|
||||
*/
|
||||
alt->skip_orig = true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool arch_support_alt_relocation(struct special_alt *special_alt,
|
||||
struct instruction *insn,
|
||||
struct reloc *reloc)
|
||||
{
|
||||
/*
|
||||
* The x86 alternatives code adjusts the offsets only when it
|
||||
* encounters a branch instruction at the very beginning of the
|
||||
* replacement group.
|
||||
*/
|
||||
return insn->offset == special_alt->new_off &&
|
||||
(insn->type == INSN_CALL || is_static_jump(insn));
|
||||
}
|
||||
|
||||
/*
|
||||
* There are 3 basic jump table patterns:
|
||||
*
|
||||
* 1. jmpq *[rodata addr](,%reg,8)
|
||||
*
|
||||
* This is the most common case by far. It jumps to an address in a simple
|
||||
* jump table which is stored in .rodata.
|
||||
*
|
||||
* 2. jmpq *[rodata addr](%rip)
|
||||
*
|
||||
* This is caused by a rare GCC quirk, currently only seen in three driver
|
||||
* functions in the kernel, only with certain obscure non-distro configs.
|
||||
*
|
||||
* As part of an optimization, GCC makes a copy of an existing switch jump
|
||||
* table, modifies it, and then hard-codes the jump (albeit with an indirect
|
||||
* jump) to use a single entry in the table. The rest of the jump table and
|
||||
* some of its jump targets remain as dead code.
|
||||
*
|
||||
* In such a case we can just crudely ignore all unreachable instruction
|
||||
* warnings for the entire object file. Ideally we would just ignore them
|
||||
* for the function, but that would require redesigning the code quite a
|
||||
* bit. And honestly that's just not worth doing: unreachable instruction
|
||||
* warnings are of questionable value anyway, and this is such a rare issue.
|
||||
*
|
||||
* 3. mov [rodata addr],%reg1
|
||||
* ... some instructions ...
|
||||
* jmpq *(%reg1,%reg2,8)
|
||||
*
|
||||
* This is a fairly uncommon pattern which is new for GCC 6. As of this
|
||||
* writing, there are 11 occurrences of it in the allmodconfig kernel.
|
||||
*
|
||||
* As of GCC 7 there are quite a few more of these and the 'in between' code
|
||||
* is significant. Esp. with KASAN enabled some of the code between the mov
|
||||
* and jmpq uses .rodata itself, which can confuse things.
|
||||
*
|
||||
* TODO: Once we have DWARF CFI and smarter instruction decoding logic,
|
||||
* ensure the same register is used in the mov and jump instructions.
|
||||
*
|
||||
* NOTE: RETPOLINE made it harder still to decode dynamic jumps.
|
||||
*/
|
||||
struct reloc *arch_find_switch_table(struct objtool_file *file,
|
||||
struct instruction *insn)
|
||||
{
|
||||
struct reloc *text_reloc, *rodata_reloc;
|
||||
struct section *table_sec;
|
||||
unsigned long table_offset;
|
||||
|
||||
/* look for a relocation which references .rodata */
|
||||
text_reloc = find_reloc_by_dest_range(file->elf, insn->sec,
|
||||
insn->offset, insn->len);
|
||||
if (!text_reloc || text_reloc->sym->type != STT_SECTION ||
|
||||
!text_reloc->sym->sec->rodata)
|
||||
return NULL;
|
||||
|
||||
table_offset = text_reloc->addend;
|
||||
table_sec = text_reloc->sym->sec;
|
||||
|
||||
if (text_reloc->type == R_X86_64_PC32)
|
||||
table_offset += 4;
|
||||
|
||||
/*
|
||||
* Make sure the .rodata address isn't associated with a
|
||||
* symbol. GCC jump tables are anonymous data.
|
||||
*
|
||||
* Also support C jump tables which are in the same format as
|
||||
* switch jump tables. For objtool to recognize them, they
|
||||
* need to be placed in the C_JUMP_TABLE_SECTION section. They
|
||||
* have symbols associated with them.
|
||||
*/
|
||||
if (find_symbol_containing(table_sec, table_offset) &&
|
||||
strcmp(table_sec->name, C_JUMP_TABLE_SECTION))
|
||||
return NULL;
|
||||
|
||||
/*
|
||||
* Each table entry has a rela associated with it. The rela
|
||||
* should reference text in the same function as the original
|
||||
* instruction.
|
||||
*/
|
||||
rodata_reloc = find_reloc_by_dest(file->elf, table_sec, table_offset);
|
||||
if (!rodata_reloc)
|
||||
return NULL;
|
||||
|
||||
/*
|
||||
* Use of RIP-relative switch jumps is quite rare, and
|
||||
* indicates a rare GCC quirk/bug which can leave dead
|
||||
* code behind.
|
||||
*/
|
||||
if (text_reloc->type == R_X86_64_PC32)
|
||||
file->ignore_unreachables = true;
|
||||
|
||||
return rodata_reloc;
|
||||
}
|
|
@ -41,6 +41,8 @@ const struct option check_options[] = {
|
|||
int cmd_check(int argc, const char **argv)
|
||||
{
|
||||
const char *objname, *s;
|
||||
struct objtool_file *file;
|
||||
int ret;
|
||||
|
||||
argc = parse_options(argc, argv, check_options, check_usage, 0);
|
||||
|
||||
|
@ -53,5 +55,16 @@ int cmd_check(int argc, const char **argv)
|
|||
if (s && !s[9])
|
||||
vmlinux = true;
|
||||
|
||||
return check(objname, false);
|
||||
file = objtool_open_read(objname);
|
||||
if (!file)
|
||||
return 1;
|
||||
|
||||
ret = check(file);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (file->elf->changed)
|
||||
return elf_write(file->elf);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -31,13 +31,38 @@ int cmd_orc(int argc, const char **argv)
|
|||
usage_with_options(orc_usage, check_options);
|
||||
|
||||
if (!strncmp(argv[0], "gen", 3)) {
|
||||
struct objtool_file *file;
|
||||
int ret;
|
||||
|
||||
argc = parse_options(argc, argv, check_options, orc_usage, 0);
|
||||
if (argc != 1)
|
||||
usage_with_options(orc_usage, check_options);
|
||||
|
||||
objname = argv[0];
|
||||
|
||||
return check(objname, true);
|
||||
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 = create_orc(file);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = create_orc_sections(file);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (!file->elf->changed)
|
||||
return 0;
|
||||
|
||||
return elf_write(file->elf);
|
||||
}
|
||||
|
||||
if (!strcmp(argv[0], "dump")) {
|
||||
|
|
|
@ -14,21 +14,19 @@
|
|||
#include "warn.h"
|
||||
#include "arch_elf.h"
|
||||
|
||||
#include <linux/objtool.h>
|
||||
#include <linux/hashtable.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/static_call_types.h>
|
||||
|
||||
#define FAKE_JUMP_OFFSET -1
|
||||
|
||||
#define C_JUMP_TABLE_SECTION ".rodata..c_jump_table"
|
||||
|
||||
struct alternative {
|
||||
struct list_head list;
|
||||
struct instruction *insn;
|
||||
bool skip_orig;
|
||||
};
|
||||
|
||||
const char *objname;
|
||||
struct cfi_init_state initial_func_cfi;
|
||||
|
||||
struct instruction *find_insn(struct objtool_file *file,
|
||||
|
@ -111,12 +109,6 @@ static struct instruction *prev_insn_same_sym(struct objtool_file *file,
|
|||
for (insn = next_insn_same_sec(file, insn); insn; \
|
||||
insn = next_insn_same_sec(file, insn))
|
||||
|
||||
static bool is_static_jump(struct instruction *insn)
|
||||
{
|
||||
return insn->type == INSN_JUMP_CONDITIONAL ||
|
||||
insn->type == INSN_JUMP_UNCONDITIONAL;
|
||||
}
|
||||
|
||||
static bool is_sibling_call(struct instruction *insn)
|
||||
{
|
||||
/* An indirect jump is either a sibling call or a jump to a table. */
|
||||
|
@ -591,6 +583,8 @@ static const char *uaccess_safe_builtin[] = {
|
|||
"__asan_store4_noabort",
|
||||
"__asan_store8_noabort",
|
||||
"__asan_store16_noabort",
|
||||
"__kasan_check_read",
|
||||
"__kasan_check_write",
|
||||
/* KASAN in-line */
|
||||
"__asan_report_load_n_noabort",
|
||||
"__asan_report_load1_noabort",
|
||||
|
@ -879,6 +873,17 @@ static void remove_insn_ops(struct instruction *insn)
|
|||
}
|
||||
}
|
||||
|
||||
static struct symbol *find_call_destination(struct section *sec, unsigned long offset)
|
||||
{
|
||||
struct symbol *call_dest;
|
||||
|
||||
call_dest = find_func_by_offset(sec, offset);
|
||||
if (!call_dest)
|
||||
call_dest = find_symbol_by_offset(sec, offset);
|
||||
|
||||
return call_dest;
|
||||
}
|
||||
|
||||
/*
|
||||
* Find the destination instructions for all calls.
|
||||
*/
|
||||
|
@ -896,9 +901,7 @@ static int add_call_destinations(struct objtool_file *file)
|
|||
insn->offset, insn->len);
|
||||
if (!reloc) {
|
||||
dest_off = arch_jump_destination(insn);
|
||||
insn->call_dest = find_func_by_offset(insn->sec, dest_off);
|
||||
if (!insn->call_dest)
|
||||
insn->call_dest = find_symbol_by_offset(insn->sec, dest_off);
|
||||
insn->call_dest = find_call_destination(insn->sec, dest_off);
|
||||
|
||||
if (insn->ignore)
|
||||
continue;
|
||||
|
@ -916,8 +919,8 @@ static int add_call_destinations(struct objtool_file *file)
|
|||
|
||||
} else if (reloc->sym->type == STT_SECTION) {
|
||||
dest_off = arch_dest_reloc_offset(reloc->addend);
|
||||
insn->call_dest = find_func_by_offset(reloc->sym->sec,
|
||||
dest_off);
|
||||
insn->call_dest = find_call_destination(reloc->sym->sec,
|
||||
dest_off);
|
||||
if (!insn->call_dest) {
|
||||
WARN_FUNC("can't find call dest symbol at %s+0x%lx",
|
||||
insn->sec, insn->offset,
|
||||
|
@ -1029,6 +1032,8 @@ static int handle_group_alt(struct objtool_file *file,
|
|||
alt_group = alt_group_next_index++;
|
||||
insn = *new_insn;
|
||||
sec_for_each_insn_from(file, insn) {
|
||||
struct reloc *alt_reloc;
|
||||
|
||||
if (insn->offset >= special_alt->new_off + special_alt->new_len)
|
||||
break;
|
||||
|
||||
|
@ -1045,14 +1050,11 @@ static int handle_group_alt(struct objtool_file *file,
|
|||
* .altinstr_replacement section, unless the arch's
|
||||
* alternatives code can adjust the relative offsets
|
||||
* accordingly.
|
||||
*
|
||||
* The x86 alternatives code adjusts the offsets only when it
|
||||
* encounters a branch instruction at the very beginning of the
|
||||
* replacement group.
|
||||
*/
|
||||
if ((insn->offset != special_alt->new_off ||
|
||||
(insn->type != INSN_CALL && !is_static_jump(insn))) &&
|
||||
find_reloc_by_dest_range(file->elf, insn->sec, insn->offset, insn->len)) {
|
||||
alt_reloc = find_reloc_by_dest_range(file->elf, insn->sec,
|
||||
insn->offset, insn->len);
|
||||
if (alt_reloc &&
|
||||
!arch_support_alt_relocation(special_alt, insn, alt_reloc)) {
|
||||
|
||||
WARN_FUNC("unsupported relocation in alternatives section",
|
||||
insn->sec, insn->offset);
|
||||
|
@ -1254,56 +1256,15 @@ static int add_jump_table(struct objtool_file *file, struct instruction *insn,
|
|||
}
|
||||
|
||||
/*
|
||||
* find_jump_table() - Given a dynamic jump, find the switch jump table in
|
||||
* .rodata associated with it.
|
||||
*
|
||||
* There are 3 basic patterns:
|
||||
*
|
||||
* 1. jmpq *[rodata addr](,%reg,8)
|
||||
*
|
||||
* This is the most common case by far. It jumps to an address in a simple
|
||||
* jump table which is stored in .rodata.
|
||||
*
|
||||
* 2. jmpq *[rodata addr](%rip)
|
||||
*
|
||||
* This is caused by a rare GCC quirk, currently only seen in three driver
|
||||
* functions in the kernel, only with certain obscure non-distro configs.
|
||||
*
|
||||
* As part of an optimization, GCC makes a copy of an existing switch jump
|
||||
* table, modifies it, and then hard-codes the jump (albeit with an indirect
|
||||
* jump) to use a single entry in the table. The rest of the jump table and
|
||||
* some of its jump targets remain as dead code.
|
||||
*
|
||||
* In such a case we can just crudely ignore all unreachable instruction
|
||||
* warnings for the entire object file. Ideally we would just ignore them
|
||||
* for the function, but that would require redesigning the code quite a
|
||||
* bit. And honestly that's just not worth doing: unreachable instruction
|
||||
* warnings are of questionable value anyway, and this is such a rare issue.
|
||||
*
|
||||
* 3. mov [rodata addr],%reg1
|
||||
* ... some instructions ...
|
||||
* jmpq *(%reg1,%reg2,8)
|
||||
*
|
||||
* This is a fairly uncommon pattern which is new for GCC 6. As of this
|
||||
* writing, there are 11 occurrences of it in the allmodconfig kernel.
|
||||
*
|
||||
* As of GCC 7 there are quite a few more of these and the 'in between' code
|
||||
* is significant. Esp. with KASAN enabled some of the code between the mov
|
||||
* and jmpq uses .rodata itself, which can confuse things.
|
||||
*
|
||||
* TODO: Once we have DWARF CFI and smarter instruction decoding logic,
|
||||
* ensure the same register is used in the mov and jump instructions.
|
||||
*
|
||||
* NOTE: RETPOLINE made it harder still to decode dynamic jumps.
|
||||
* find_jump_table() - Given a dynamic jump, find the switch jump table
|
||||
* associated with it.
|
||||
*/
|
||||
static struct reloc *find_jump_table(struct objtool_file *file,
|
||||
struct symbol *func,
|
||||
struct instruction *insn)
|
||||
{
|
||||
struct reloc *text_reloc, *table_reloc;
|
||||
struct reloc *table_reloc;
|
||||
struct instruction *dest_insn, *orig_insn = insn;
|
||||
struct section *table_sec;
|
||||
unsigned long table_offset;
|
||||
|
||||
/*
|
||||
* Backward search using the @first_jump_src links, these help avoid
|
||||
|
@ -1324,52 +1285,13 @@ static struct reloc *find_jump_table(struct objtool_file *file,
|
|||
insn->jump_dest->offset > orig_insn->offset))
|
||||
break;
|
||||
|
||||
/* look for a relocation which references .rodata */
|
||||
text_reloc = find_reloc_by_dest_range(file->elf, insn->sec,
|
||||
insn->offset, insn->len);
|
||||
if (!text_reloc || text_reloc->sym->type != STT_SECTION ||
|
||||
!text_reloc->sym->sec->rodata)
|
||||
continue;
|
||||
|
||||
table_offset = text_reloc->addend;
|
||||
table_sec = text_reloc->sym->sec;
|
||||
|
||||
if (text_reloc->type == R_X86_64_PC32)
|
||||
table_offset += 4;
|
||||
|
||||
/*
|
||||
* Make sure the .rodata address isn't associated with a
|
||||
* symbol. GCC jump tables are anonymous data.
|
||||
*
|
||||
* Also support C jump tables which are in the same format as
|
||||
* switch jump tables. For objtool to recognize them, they
|
||||
* need to be placed in the C_JUMP_TABLE_SECTION section. They
|
||||
* have symbols associated with them.
|
||||
*/
|
||||
if (find_symbol_containing(table_sec, table_offset) &&
|
||||
strcmp(table_sec->name, C_JUMP_TABLE_SECTION))
|
||||
continue;
|
||||
|
||||
/*
|
||||
* Each table entry has a reloc associated with it. The reloc
|
||||
* should reference text in the same function as the original
|
||||
* instruction.
|
||||
*/
|
||||
table_reloc = find_reloc_by_dest(file->elf, table_sec, table_offset);
|
||||
table_reloc = arch_find_switch_table(file, insn);
|
||||
if (!table_reloc)
|
||||
continue;
|
||||
dest_insn = find_insn(file, table_reloc->sym->sec, table_reloc->addend);
|
||||
if (!dest_insn || !dest_insn->func || dest_insn->func->pfunc != func)
|
||||
continue;
|
||||
|
||||
/*
|
||||
* Use of RIP-relative switch jumps is quite rare, and
|
||||
* indicates a rare GCC quirk/bug which can leave dead code
|
||||
* behind.
|
||||
*/
|
||||
if (text_reloc->type == R_X86_64_PC32)
|
||||
file->ignore_unreachables = true;
|
||||
|
||||
return table_reloc;
|
||||
}
|
||||
|
||||
|
@ -1512,32 +1434,7 @@ static int read_unwind_hints(struct objtool_file *file)
|
|||
|
||||
insn->hint = true;
|
||||
|
||||
switch (hint->sp_reg) {
|
||||
case ORC_REG_UNDEFINED:
|
||||
cfa->base = CFI_UNDEFINED;
|
||||
break;
|
||||
case ORC_REG_SP:
|
||||
cfa->base = CFI_SP;
|
||||
break;
|
||||
case ORC_REG_BP:
|
||||
cfa->base = CFI_BP;
|
||||
break;
|
||||
case ORC_REG_SP_INDIRECT:
|
||||
cfa->base = CFI_SP_INDIRECT;
|
||||
break;
|
||||
case ORC_REG_R10:
|
||||
cfa->base = CFI_R10;
|
||||
break;
|
||||
case ORC_REG_R13:
|
||||
cfa->base = CFI_R13;
|
||||
break;
|
||||
case ORC_REG_DI:
|
||||
cfa->base = CFI_DI;
|
||||
break;
|
||||
case ORC_REG_DX:
|
||||
cfa->base = CFI_DX;
|
||||
break;
|
||||
default:
|
||||
if (arch_decode_hint_reg(insn, hint->sp_reg)) {
|
||||
WARN_FUNC("unsupported unwind_hint sp base reg %d",
|
||||
insn->sec, insn->offset, hint->sp_reg);
|
||||
return -1;
|
||||
|
@ -1951,7 +1848,8 @@ static int update_cfi_state(struct instruction *insn, struct cfi_state *cfi,
|
|||
return 0;
|
||||
}
|
||||
|
||||
if (cfi->type == ORC_TYPE_REGS || cfi->type == ORC_TYPE_REGS_IRET)
|
||||
if (cfi->type == UNWIND_HINT_TYPE_REGS ||
|
||||
cfi->type == UNWIND_HINT_TYPE_REGS_PARTIAL)
|
||||
return update_cfi_state_regs(insn, cfi, op);
|
||||
|
||||
switch (op->dest.type) {
|
||||
|
@ -2199,7 +2097,7 @@ static int update_cfi_state(struct instruction *insn, struct cfi_state *cfi,
|
|||
/* drap: push %rbp */
|
||||
cfi->stack_size = 0;
|
||||
|
||||
} else if (regs[op->src.reg].base == CFI_UNDEFINED) {
|
||||
} else {
|
||||
|
||||
/* drap: push %reg */
|
||||
save_reg(cfi, op->src.reg, CFI_BP, -cfi->stack_size);
|
||||
|
@ -2228,9 +2126,7 @@ static int update_cfi_state(struct instruction *insn, struct cfi_state *cfi,
|
|||
|
||||
/* save drap offset so we know when to restore it */
|
||||
cfi->drap_offset = op->dest.offset;
|
||||
}
|
||||
|
||||
else if (regs[op->src.reg].base == CFI_UNDEFINED) {
|
||||
} else {
|
||||
|
||||
/* drap: mov reg, disp(%rbp) */
|
||||
save_reg(cfi, op->src.reg, CFI_BP, op->dest.offset);
|
||||
|
@ -2800,9 +2696,10 @@ static bool is_ubsan_insn(struct instruction *insn)
|
|||
"__ubsan_handle_builtin_unreachable"));
|
||||
}
|
||||
|
||||
static bool ignore_unreachable_insn(struct instruction *insn)
|
||||
static bool ignore_unreachable_insn(struct objtool_file *file, struct instruction *insn)
|
||||
{
|
||||
int i;
|
||||
struct instruction *prev_insn;
|
||||
|
||||
if (insn->ignore || insn->type == INSN_NOP)
|
||||
return true;
|
||||
|
@ -2819,6 +2716,9 @@ static bool ignore_unreachable_insn(struct instruction *insn)
|
|||
!strcmp(insn->sec->name, ".altinstr_aux"))
|
||||
return true;
|
||||
|
||||
if (insn->type == INSN_JUMP_UNCONDITIONAL && insn->offset == FAKE_JUMP_OFFSET)
|
||||
return true;
|
||||
|
||||
if (!insn->func)
|
||||
return false;
|
||||
|
||||
|
@ -2827,8 +2727,11 @@ static bool ignore_unreachable_insn(struct instruction *insn)
|
|||
* __builtin_unreachable(). The BUG() macro has an unreachable() after
|
||||
* the UD2, which causes GCC's undefined trap logic to emit another UD2
|
||||
* (or occasionally a JMP to UD2).
|
||||
*
|
||||
* It may also insert a UD2 after calling a __noreturn function.
|
||||
*/
|
||||
if (list_prev_entry(insn, list)->dead_end &&
|
||||
prev_insn = list_prev_entry(insn, list);
|
||||
if ((prev_insn->dead_end || dead_end_function(file, prev_insn->call_dest)) &&
|
||||
(insn->type == INSN_BUG ||
|
||||
(insn->type == INSN_JUMP_UNCONDITIONAL &&
|
||||
insn->jump_dest && insn->jump_dest->type == INSN_BUG)))
|
||||
|
@ -2955,7 +2858,7 @@ static int validate_reachable_instructions(struct objtool_file *file)
|
|||
return 0;
|
||||
|
||||
for_each_insn(file, insn) {
|
||||
if (insn->visited || ignore_unreachable_insn(insn))
|
||||
if (insn->visited || ignore_unreachable_insn(file, insn))
|
||||
continue;
|
||||
|
||||
WARN_FUNC("unreachable instruction", insn->sec, insn->offset);
|
||||
|
@ -2965,37 +2868,22 @@ static int validate_reachable_instructions(struct objtool_file *file)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static struct objtool_file file;
|
||||
|
||||
int check(const char *_objname, bool orc)
|
||||
int check(struct objtool_file *file)
|
||||
{
|
||||
int ret, warnings = 0;
|
||||
|
||||
objname = _objname;
|
||||
|
||||
file.elf = elf_open_read(objname, O_RDWR);
|
||||
if (!file.elf)
|
||||
return 1;
|
||||
|
||||
INIT_LIST_HEAD(&file.insn_list);
|
||||
hash_init(file.insn_hash);
|
||||
INIT_LIST_HEAD(&file.static_call_list);
|
||||
file.c_file = !vmlinux && find_section_by_name(file.elf, ".comment");
|
||||
file.ignore_unreachables = no_unreachable;
|
||||
file.hints = false;
|
||||
|
||||
arch_initial_func_cfi_state(&initial_func_cfi);
|
||||
|
||||
ret = decode_sections(&file);
|
||||
ret = decode_sections(file);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
warnings += ret;
|
||||
|
||||
if (list_empty(&file.insn_list))
|
||||
if (list_empty(&file->insn_list))
|
||||
goto out;
|
||||
|
||||
if (vmlinux && !validate_dup) {
|
||||
ret = validate_vmlinux_functions(&file);
|
||||
ret = validate_vmlinux_functions(file);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
|
@ -3004,50 +2892,34 @@ int check(const char *_objname, bool orc)
|
|||
}
|
||||
|
||||
if (retpoline) {
|
||||
ret = validate_retpoline(&file);
|
||||
ret = validate_retpoline(file);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
warnings += ret;
|
||||
}
|
||||
|
||||
ret = validate_functions(&file);
|
||||
ret = validate_functions(file);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
warnings += ret;
|
||||
|
||||
ret = validate_unwind_hints(&file, NULL);
|
||||
ret = validate_unwind_hints(file, NULL);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
warnings += ret;
|
||||
|
||||
if (!warnings) {
|
||||
ret = validate_reachable_instructions(&file);
|
||||
ret = validate_reachable_instructions(file);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
warnings += ret;
|
||||
}
|
||||
|
||||
ret = create_static_call_sections(&file);
|
||||
ret = create_static_call_sections(file);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
warnings += ret;
|
||||
|
||||
if (orc) {
|
||||
ret = create_orc(&file);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
ret = create_orc_sections(&file);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (file.elf->changed) {
|
||||
ret = elf_write(file.elf);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
if (ret < 0) {
|
||||
/*
|
||||
|
|
|
@ -43,9 +43,17 @@ struct instruction {
|
|||
struct symbol *func;
|
||||
struct list_head stack_ops;
|
||||
struct cfi_state cfi;
|
||||
#ifdef INSN_USE_ORC
|
||||
struct orc_entry orc;
|
||||
#endif
|
||||
};
|
||||
|
||||
static inline bool is_static_jump(struct instruction *insn)
|
||||
{
|
||||
return insn->type == INSN_JUMP_CONDITIONAL ||
|
||||
insn->type == INSN_JUMP_UNCONDITIONAL;
|
||||
}
|
||||
|
||||
struct instruction *find_insn(struct objtool_file *file,
|
||||
struct section *sec, unsigned long offset);
|
||||
|
||||
|
@ -58,5 +66,4 @@ struct instruction *find_insn(struct objtool_file *file,
|
|||
insn->sec == sec; \
|
||||
insn = list_next_entry(insn, list))
|
||||
|
||||
|
||||
#endif /* _CHECK_H */
|
||||
|
|
|
@ -22,6 +22,8 @@
|
|||
#include <linux/kernel.h>
|
||||
|
||||
#include "builtin.h"
|
||||
#include "objtool.h"
|
||||
#include "warn.h"
|
||||
|
||||
struct cmd_struct {
|
||||
const char *name;
|
||||
|
@ -39,6 +41,34 @@ static struct cmd_struct objtool_cmds[] = {
|
|||
|
||||
bool help;
|
||||
|
||||
const char *objname;
|
||||
static struct objtool_file file;
|
||||
|
||||
struct objtool_file *objtool_open_read(const char *_objname)
|
||||
{
|
||||
if (objname) {
|
||||
if (strcmp(objname, _objname)) {
|
||||
WARN("won't handle more than one file at a time");
|
||||
return NULL;
|
||||
}
|
||||
return &file;
|
||||
}
|
||||
objname = _objname;
|
||||
|
||||
file.elf = elf_open_read(objname, O_RDWR);
|
||||
if (!file.elf)
|
||||
return NULL;
|
||||
|
||||
INIT_LIST_HEAD(&file.insn_list);
|
||||
hash_init(file.insn_hash);
|
||||
INIT_LIST_HEAD(&file.static_call_list);
|
||||
file.c_file = !vmlinux && find_section_by_name(file.elf, ".comment");
|
||||
file.ignore_unreachables = no_unreachable;
|
||||
file.hints = false;
|
||||
|
||||
return &file;
|
||||
}
|
||||
|
||||
static void cmd_usage(void)
|
||||
{
|
||||
unsigned int i, longest = 0;
|
||||
|
|
|
@ -12,6 +12,8 @@
|
|||
|
||||
#include "elf.h"
|
||||
|
||||
#define __weak __attribute__((weak))
|
||||
|
||||
struct objtool_file {
|
||||
struct elf *elf;
|
||||
struct list_head insn_list;
|
||||
|
@ -20,7 +22,9 @@ struct objtool_file {
|
|||
bool ignore_unreachables, c_file, hints, rodata;
|
||||
};
|
||||
|
||||
int check(const char *objname, bool orc);
|
||||
struct objtool_file *objtool_open_read(const char *_objname);
|
||||
|
||||
int check(struct objtool_file *file);
|
||||
int orc_dump(const char *objname);
|
||||
int create_orc(struct objtool_file *file);
|
||||
int create_orc_sections(struct objtool_file *file);
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
*/
|
||||
|
||||
#include <unistd.h>
|
||||
#include <linux/objtool.h>
|
||||
#include <asm/orc_types.h>
|
||||
#include "objtool.h"
|
||||
#include "warn.h"
|
||||
|
@ -37,12 +38,12 @@ static const char *reg_name(unsigned int reg)
|
|||
static const char *orc_type_name(unsigned int type)
|
||||
{
|
||||
switch (type) {
|
||||
case ORC_TYPE_CALL:
|
||||
case UNWIND_HINT_TYPE_CALL:
|
||||
return "call";
|
||||
case ORC_TYPE_REGS:
|
||||
case UNWIND_HINT_TYPE_REGS:
|
||||
return "regs";
|
||||
case ORC_TYPE_REGS_IRET:
|
||||
return "iret";
|
||||
case UNWIND_HINT_TYPE_REGS_PARTIAL:
|
||||
return "regs (partial)";
|
||||
default:
|
||||
return "?";
|
||||
}
|
||||
|
|
|
@ -6,6 +6,9 @@
|
|||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <linux/objtool.h>
|
||||
#include <asm/orc_types.h>
|
||||
|
||||
#include "check.h"
|
||||
#include "warn.h"
|
||||
|
||||
|
@ -18,6 +21,9 @@ int create_orc(struct objtool_file *file)
|
|||
struct cfi_reg *cfa = &insn->cfi.cfa;
|
||||
struct cfi_reg *bp = &insn->cfi.regs[CFI_BP];
|
||||
|
||||
if (!insn->sec->text)
|
||||
continue;
|
||||
|
||||
orc->end = insn->cfi.end;
|
||||
|
||||
if (cfa->base == CFI_UNDEFINED) {
|
||||
|
@ -143,7 +149,7 @@ int create_orc_sections(struct objtool_file *file)
|
|||
struct orc_entry empty = {
|
||||
.sp_reg = ORC_REG_UNDEFINED,
|
||||
.bp_reg = ORC_REG_UNDEFINED,
|
||||
.type = ORC_TYPE_CALL,
|
||||
.type = UNWIND_HINT_TYPE_CALL,
|
||||
};
|
||||
|
||||
sec = find_section_by_name(file->elf, ".orc_unwind");
|
||||
|
|
|
@ -14,24 +14,7 @@
|
|||
#include "builtin.h"
|
||||
#include "special.h"
|
||||
#include "warn.h"
|
||||
|
||||
#define EX_ENTRY_SIZE 12
|
||||
#define EX_ORIG_OFFSET 0
|
||||
#define EX_NEW_OFFSET 4
|
||||
|
||||
#define JUMP_ENTRY_SIZE 16
|
||||
#define JUMP_ORIG_OFFSET 0
|
||||
#define JUMP_NEW_OFFSET 4
|
||||
|
||||
#define ALT_ENTRY_SIZE 13
|
||||
#define ALT_ORIG_OFFSET 0
|
||||
#define ALT_NEW_OFFSET 4
|
||||
#define ALT_FEATURE_OFFSET 8
|
||||
#define ALT_ORIG_LEN_OFFSET 10
|
||||
#define ALT_NEW_LEN_OFFSET 11
|
||||
|
||||
#define X86_FEATURE_POPCNT (4*32+23)
|
||||
#define X86_FEATURE_SMAP (9*32+20)
|
||||
#include "arch_special.h"
|
||||
|
||||
struct special_entry {
|
||||
const char *sec;
|
||||
|
@ -68,6 +51,10 @@ struct special_entry entries[] = {
|
|||
{},
|
||||
};
|
||||
|
||||
void __weak arch_handle_alternative(unsigned short feature, struct special_alt *alt)
|
||||
{
|
||||
}
|
||||
|
||||
static int get_alt_entry(struct elf *elf, struct special_entry *entry,
|
||||
struct section *sec, int idx,
|
||||
struct special_alt *alt)
|
||||
|
@ -92,30 +79,7 @@ static int get_alt_entry(struct elf *elf, struct special_entry *entry,
|
|||
|
||||
feature = *(unsigned short *)(sec->data->d_buf + offset +
|
||||
entry->feature);
|
||||
|
||||
/*
|
||||
* It has been requested that we don't validate the !POPCNT
|
||||
* feature path which is a "very very small percentage of
|
||||
* machines".
|
||||
*/
|
||||
if (feature == X86_FEATURE_POPCNT)
|
||||
alt->skip_orig = true;
|
||||
|
||||
/*
|
||||
* If UACCESS validation is enabled; force that alternative;
|
||||
* otherwise force it the other way.
|
||||
*
|
||||
* What we want to avoid is having both the original and the
|
||||
* alternative code flow at the same time, in that case we can
|
||||
* find paths that see the STAC but take the NOP instead of
|
||||
* CLAC and the other way around.
|
||||
*/
|
||||
if (feature == X86_FEATURE_SMAP) {
|
||||
if (uaccess)
|
||||
alt->skip_orig = true;
|
||||
else
|
||||
alt->skip_alt = true;
|
||||
}
|
||||
arch_handle_alternative(feature, alt);
|
||||
}
|
||||
|
||||
orig_reloc = find_reloc_by_dest(elf, sec, offset + entry->orig);
|
||||
|
|
|
@ -7,8 +7,11 @@
|
|||
#define _SPECIAL_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "check.h"
|
||||
#include "elf.h"
|
||||
|
||||
#define C_JUMP_TABLE_SECTION ".rodata..c_jump_table"
|
||||
|
||||
struct special_alt {
|
||||
struct list_head list;
|
||||
|
||||
|
@ -28,4 +31,11 @@ struct special_alt {
|
|||
|
||||
int special_get_alts(struct elf *elf, struct list_head *alts);
|
||||
|
||||
void arch_handle_alternative(unsigned short feature, struct special_alt *alt);
|
||||
|
||||
bool arch_support_alt_relocation(struct special_alt *special_alt,
|
||||
struct instruction *insn,
|
||||
struct reloc *reloc);
|
||||
struct reloc *arch_find_switch_table(struct objtool_file *file,
|
||||
struct instruction *insn);
|
||||
#endif /* _SPECIAL_H */
|
||||
|
|
|
@ -1,14 +1,27 @@
|
|||
#!/bin/sh
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
FILES='
|
||||
if [ -z "$SRCARCH" ]; then
|
||||
echo 'sync-check.sh: error: missing $SRCARCH environment variable' >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
FILES="include/linux/objtool.h"
|
||||
|
||||
if [ "$SRCARCH" = "x86" ]; then
|
||||
FILES="$FILES
|
||||
arch/x86/include/asm/inat_types.h
|
||||
arch/x86/include/asm/orc_types.h
|
||||
arch/x86/include/asm/emulate_prefix.h
|
||||
arch/x86/lib/x86-opcode-map.txt
|
||||
arch/x86/tools/gen-insn-attr-x86.awk
|
||||
include/linux/static_call_types.h
|
||||
'
|
||||
arch/x86/include/asm/inat.h -I '^#include [\"<]\(asm/\)*inat_types.h[\">]'
|
||||
arch/x86/include/asm/insn.h -I '^#include [\"<]\(asm/\)*inat.h[\">]'
|
||||
arch/x86/lib/inat.c -I '^#include [\"<]\(../include/\)*asm/insn.h[\">]'
|
||||
arch/x86/lib/insn.c -I '^#include [\"<]\(../include/\)*asm/in\(at\|sn\).h[\">]' -I '^#include [\"<]\(../include/\)*asm/emulate_prefix.h[\">]'
|
||||
"
|
||||
fi
|
||||
|
||||
check_2 () {
|
||||
file1=$1
|
||||
|
@ -41,11 +54,12 @@ fi
|
|||
|
||||
cd ../..
|
||||
|
||||
for i in $FILES; do
|
||||
check $i
|
||||
done
|
||||
while read -r file_entry; do
|
||||
if [ -z "$file_entry" ]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
check arch/x86/include/asm/inat.h '-I "^#include [\"<]\(asm/\)*inat_types.h[\">]"'
|
||||
check arch/x86/include/asm/insn.h '-I "^#include [\"<]\(asm/\)*inat.h[\">]"'
|
||||
check arch/x86/lib/inat.c '-I "^#include [\"<]\(../include/\)*asm/insn.h[\">]"'
|
||||
check arch/x86/lib/insn.c '-I "^#include [\"<]\(../include/\)*asm/in\(at\|sn\).h[\">]" -I "^#include [\"<]\(../include/\)*asm/emulate_prefix.h[\">]"'
|
||||
check $file_entry
|
||||
done <<EOF
|
||||
$FILES
|
||||
EOF
|
||||
|
|
|
@ -9,17 +9,13 @@
|
|||
#include <errno.h>
|
||||
#include "objtool.h"
|
||||
|
||||
#define __weak __attribute__((weak))
|
||||
|
||||
#define UNSUPPORTED(name) \
|
||||
({ \
|
||||
fprintf(stderr, "error: objtool: " name " not implemented\n"); \
|
||||
return ENOSYS; \
|
||||
})
|
||||
|
||||
const char __weak *objname;
|
||||
|
||||
int __weak check(const char *_objname, bool orc)
|
||||
int __weak check(struct objtool_file *file)
|
||||
{
|
||||
UNSUPPORTED("check subcommand");
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@ $(shell printf "" > $(OUTPUT).config-detected)
|
|||
detected = $(shell echo "$(1)=y" >> $(OUTPUT).config-detected)
|
||||
detected_var = $(shell echo "$(1)=$($(1))" >> $(OUTPUT).config-detected)
|
||||
|
||||
CFLAGS := $(EXTRA_CFLAGS) $(EXTRA_WARNINGS)
|
||||
CFLAGS := $(EXTRA_CFLAGS) $(filter-out -Wnested-externs,$(EXTRA_WARNINGS))
|
||||
|
||||
include $(srctree)/tools/scripts/Makefile.arch
|
||||
|
||||
|
|
Loading…
Reference in New Issue