Merge branch into tip/master: 'objtool/core'

# New commits in objtool/core:
    41a1e976623e ("x86/mm: Convert unreachable() to BUG()")
    c3cb6c158c64 ("objtool: Allow arch code to discover jump table size")
    e7e0eb53c2f0 ("objtool: Warn about unknown annotation types")
    87116ae6da03 ("objtool: Fix ANNOTATE_REACHABLE to be a normal annotation")
    e7a174fb43d2 ("objtool: Convert {.UN}REACHABLE to ANNOTATE")
    06e24745985c ("objtool: Remove annotate_{,un}reachable()")
    624bde3465f6 ("loongarch: Use ASM_REACHABLE")
    2190966fbc14 ("x86: Convert unreachable() to BUG()")
    c837de381098 ("unreachable: Unify")
    bb8170067470 ("objtool: Collect more annotations in objtool.h")
    a8a330dd9900 ("objtool: Collapse annotate sequences")
    112765ca1cb9 ("objtool: Convert ANNOTATE_INTRA_FUNCTION_CALL to ANNOTATE")
    f0cd57c35a75 ("objtool: Convert ANNOTATE_IGNORE_ALTERNATIVE to ANNOTATE")
    18aa6118a168 ("objtool: Convert VALIDATE_UNRET_BEGIN to ANNOTATE")
    317f2a64618c ("objtool: Convert instrumentation_{begin,end}() to ANNOTATE")
    bf5febebd99f ("objtool: Convert ANNOTATE_RETPOLINE_SAFE to ANNOTATE")
    22c3d5807968 ("objtool: Convert ANNOTATE_NOENDBR to ANNOTATE")
    2116b349e29a ("objtool: Generic annotation infrastructure")

Signed-off-by: Ingo Molnar <mingo@kernel.org>
This commit is contained in:
Ingo Molnar 2025-01-11 17:05:00 +01:00
commit 8d020ac092
22 changed files with 275 additions and 471 deletions

View File

@ -4,6 +4,7 @@
#include <asm/break.h> #include <asm/break.h>
#include <linux/stringify.h> #include <linux/stringify.h>
#include <linux/objtool.h>
#ifndef CONFIG_DEBUG_BUGVERBOSE #ifndef CONFIG_DEBUG_BUGVERBOSE
#define _BUGVERBOSE_LOCATION(file, line) #define _BUGVERBOSE_LOCATION(file, line)
@ -33,25 +34,25 @@
#define ASM_BUG_FLAGS(flags) \ #define ASM_BUG_FLAGS(flags) \
__BUG_ENTRY(flags) \ __BUG_ENTRY(flags) \
break BRK_BUG break BRK_BUG;
#define ASM_BUG() ASM_BUG_FLAGS(0) #define ASM_BUG() ASM_BUG_FLAGS(0)
#define __BUG_FLAGS(flags) \ #define __BUG_FLAGS(flags, extra) \
asm_inline volatile (__stringify(ASM_BUG_FLAGS(flags))); asm_inline volatile (__stringify(ASM_BUG_FLAGS(flags)) \
extra);
#define __WARN_FLAGS(flags) \ #define __WARN_FLAGS(flags) \
do { \ do { \
instrumentation_begin(); \ instrumentation_begin(); \
__BUG_FLAGS(BUGFLAG_WARNING|(flags)); \ __BUG_FLAGS(BUGFLAG_WARNING|(flags), ANNOTATE_REACHABLE(10001b));\
annotate_reachable(); \
instrumentation_end(); \ instrumentation_end(); \
} while (0) } while (0)
#define BUG() \ #define BUG() \
do { \ do { \
instrumentation_begin(); \ instrumentation_begin(); \
__BUG_FLAGS(0); \ __BUG_FLAGS(0, ""); \
unreachable(); \ unreachable(); \
} while (0) } while (0)

View File

@ -308,10 +308,9 @@ SYM_CODE_END(xen_error_entry)
movq $-1, ORIG_RAX(%rsp) /* no syscall to restart */ movq $-1, ORIG_RAX(%rsp) /* no syscall to restart */
.endif .endif
call \cfunc
/* For some configurations \cfunc ends up being a noreturn. */ /* For some configurations \cfunc ends up being a noreturn. */
REACHABLE ANNOTATE_REACHABLE
call \cfunc
jmp error_return jmp error_return
.endm .endm
@ -529,10 +528,10 @@ SYM_CODE_START(\asmsym)
movq %rsp, %rdi /* pt_regs pointer into first argument */ movq %rsp, %rdi /* pt_regs pointer into first argument */
movq ORIG_RAX(%rsp), %rsi /* get error code into 2nd argument*/ movq ORIG_RAX(%rsp), %rsi /* get error code into 2nd argument*/
movq $-1, ORIG_RAX(%rsp) /* no syscall to restart */ movq $-1, ORIG_RAX(%rsp) /* no syscall to restart */
call \cfunc
/* For some configurations \cfunc ends up being a noreturn. */ /* For some configurations \cfunc ends up being a noreturn. */
REACHABLE ANNOTATE_REACHABLE
call \cfunc
jmp paranoid_exit jmp paranoid_exit

View File

@ -4,6 +4,7 @@
#include <linux/types.h> #include <linux/types.h>
#include <linux/stringify.h> #include <linux/stringify.h>
#include <linux/objtool.h>
#include <asm/asm.h> #include <asm/asm.h>
#define ALT_FLAGS_SHIFT 16 #define ALT_FLAGS_SHIFT 16
@ -54,16 +55,6 @@
#define LOCK_PREFIX "" #define LOCK_PREFIX ""
#endif #endif
/*
* objtool annotation to ignore the alternatives and only consider the original
* instruction(s).
*/
#define ANNOTATE_IGNORE_ALTERNATIVE \
"999:\n\t" \
".pushsection .discard.ignore_alts\n\t" \
".long 999b\n\t" \
".popsection\n\t"
/* /*
* The patching flags are part of the upper bits of the @ft_flags parameter when * The patching flags are part of the upper bits of the @ft_flags parameter when
* specifying them. The split is currently like this: * specifying them. The split is currently like this:
@ -310,17 +301,6 @@ void nop_func(void);
.endm .endm
#endif #endif
/*
* objtool annotation to ignore the alternatives and only consider the original
* instruction(s).
*/
.macro ANNOTATE_IGNORE_ALTERNATIVE
.Lannotate_\@:
.pushsection .discard.ignore_alts
.long .Lannotate_\@
.popsection
.endm
/* /*
* Issue one struct alt_instr descriptor entry (need to put it into * Issue one struct alt_instr descriptor entry (need to put it into
* the section .altinstructions, see below). This entry contains * the section .altinstructions, see below). This entry contains

View File

@ -92,7 +92,7 @@ do { \
do { \ do { \
__auto_type __flags = BUGFLAG_WARNING|(flags); \ __auto_type __flags = BUGFLAG_WARNING|(flags); \
instrumentation_begin(); \ instrumentation_begin(); \
_BUG_FLAGS(ASM_UD2, __flags, ASM_REACHABLE); \ _BUG_FLAGS(ASM_UD2, __flags, ANNOTATE_REACHABLE(1b)); \
instrumentation_end(); \ instrumentation_end(); \
} while (0) } while (0)

View File

@ -100,8 +100,8 @@
} }
#define ASM_CALL_ARG0 \ #define ASM_CALL_ARG0 \
"call %c[__func] \n" \ "1: call %c[__func] \n" \
ASM_REACHABLE ANNOTATE_REACHABLE(1b)
#define ASM_CALL_ARG1 \ #define ASM_CALL_ARG1 \
"movq %[arg1], %%rdi \n" \ "movq %[arg1], %%rdi \n" \

View File

@ -179,18 +179,6 @@
#ifdef __ASSEMBLY__ #ifdef __ASSEMBLY__
/*
* This should be used immediately before an indirect jump/call. It tells
* objtool the subsequent indirect jump/call is vouched safe for retpoline
* builds.
*/
.macro ANNOTATE_RETPOLINE_SAFE
.Lhere_\@:
.pushsection .discard.retpoline_safe
.long .Lhere_\@
.popsection
.endm
/* /*
* (ab)use RETPOLINE_SAFE on RET to annotate away 'bare' RET instructions * (ab)use RETPOLINE_SAFE on RET to annotate away 'bare' RET instructions
* vs RETBleed validation. * vs RETBleed validation.
@ -350,12 +338,6 @@
#else /* __ASSEMBLY__ */ #else /* __ASSEMBLY__ */
#define ANNOTATE_RETPOLINE_SAFE \
"999:\n\t" \
".pushsection .discard.retpoline_safe\n\t" \
".long 999b\n\t" \
".popsection\n\t"
typedef u8 retpoline_thunk_t[RETPOLINE_THUNK_SIZE]; typedef u8 retpoline_thunk_t[RETPOLINE_THUNK_SIZE];
extern retpoline_thunk_t __x86_indirect_thunk_array[]; extern retpoline_thunk_t __x86_indirect_thunk_array[];
extern retpoline_thunk_t __x86_indirect_call_thunk_array[]; extern retpoline_thunk_t __x86_indirect_call_thunk_array[];

View File

@ -838,7 +838,7 @@ void __noreturn stop_this_cpu(void *dummy)
#ifdef CONFIG_SMP #ifdef CONFIG_SMP
if (smp_ops.stop_this_cpu) { if (smp_ops.stop_this_cpu) {
smp_ops.stop_this_cpu(); smp_ops.stop_this_cpu();
unreachable(); BUG();
} }
#endif #endif

View File

@ -883,7 +883,7 @@ static int crash_nmi_callback(unsigned int val, struct pt_regs *regs)
if (smp_ops.stop_this_cpu) { if (smp_ops.stop_this_cpu) {
smp_ops.stop_this_cpu(); smp_ops.stop_this_cpu();
unreachable(); BUG();
} }
/* Assume hlt works */ /* Assume hlt works */

View File

@ -3820,7 +3820,7 @@ next_range:
goto next_range; goto next_range;
} }
unreachable(); BUG();
} }
static int __sev_snp_update_protected_guest_state(struct kvm_vcpu *vcpu) static int __sev_snp_update_protected_guest_state(struct kvm_vcpu *vcpu)

View File

@ -678,7 +678,7 @@ page_fault_oops(struct pt_regs *regs, unsigned long error_code,
ASM_CALL_ARG3, ASM_CALL_ARG3,
, [arg1] "r" (regs), [arg2] "r" (address), [arg3] "r" (&info)); , [arg1] "r" (regs), [arg2] "r" (address), [arg3] "r" (&info));
unreachable(); BUG();
} }
#endif #endif

View File

@ -52,18 +52,6 @@
*/ */
#define barrier_before_unreachable() asm volatile("") #define barrier_before_unreachable() asm volatile("")
/*
* Mark a position in code as unreachable. This can be used to
* suppress control flow warnings after asm blocks that transfer
* control elsewhere.
*/
#define unreachable() \
do { \
annotate_unreachable(); \
barrier_before_unreachable(); \
__builtin_unreachable(); \
} while (0)
#if defined(CONFIG_ARCH_USE_BUILTIN_BSWAP) #if defined(CONFIG_ARCH_USE_BUILTIN_BSWAP)
#define __HAVE_BUILTIN_BSWAP32__ #define __HAVE_BUILTIN_BSWAP32__
#define __HAVE_BUILTIN_BSWAP64__ #define __HAVE_BUILTIN_BSWAP64__

View File

@ -109,44 +109,21 @@ void ftrace_likely_update(struct ftrace_likely_data *f, int val,
/* Unreachable code */ /* Unreachable code */
#ifdef CONFIG_OBJTOOL #ifdef CONFIG_OBJTOOL
/*
* 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
* unique, to convince GCC not to merge duplicate inline asm statements.
*/
#define __stringify_label(n) #n
#define __annotate_reachable(c) ({ \
asm volatile(__stringify_label(c) ":\n\t" \
".pushsection .discard.reachable\n\t" \
".long " __stringify_label(c) "b - .\n\t" \
".popsection\n\t"); \
})
#define annotate_reachable() __annotate_reachable(__COUNTER__)
#define __annotate_unreachable(c) ({ \
asm volatile(__stringify_label(c) ":\n\t" \
".pushsection .discard.unreachable\n\t" \
".long " __stringify_label(c) "b - .\n\t" \
".popsection\n\t" : : "i" (c)); \
})
#define annotate_unreachable() __annotate_unreachable(__COUNTER__)
/* 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,\"a\",@progbits #") #define __annotate_jump_table __section(".rodata..c_jump_table,\"a\",@progbits #")
#else /* !CONFIG_OBJTOOL */ #else /* !CONFIG_OBJTOOL */
#define annotate_reachable()
#define annotate_unreachable()
#define __annotate_jump_table #define __annotate_jump_table
#endif /* CONFIG_OBJTOOL */ #endif /* CONFIG_OBJTOOL */
#ifndef unreachable /*
# define unreachable() do { \ * Mark a position in code as unreachable. This can be used to
annotate_unreachable(); \ * suppress control flow warnings after asm blocks that transfer
* control elsewhere.
*/
#define unreachable() do { \
barrier_before_unreachable(); \
__builtin_unreachable(); \ __builtin_unreachable(); \
} while (0) } while (0)
#endif
/* /*
* KENTRY - kernel entry point * KENTRY - kernel entry point

View File

@ -4,14 +4,14 @@
#ifdef CONFIG_NOINSTR_VALIDATION #ifdef CONFIG_NOINSTR_VALIDATION
#include <linux/objtool.h>
#include <linux/stringify.h> #include <linux/stringify.h>
/* Begin/end of an instrumentation safe region */ /* Begin/end of an instrumentation safe region */
#define __instrumentation_begin(c) ({ \ #define __instrumentation_begin(c) ({ \
asm volatile(__stringify(c) ": nop\n\t" \ asm volatile(__stringify(c) ": nop\n\t" \
".pushsection .discard.instr_begin\n\t" \ ANNOTATE_INSTR_BEGIN(__ASM_BREF(c)) \
".long " __stringify(c) "b - .\n\t" \ : : "i" (c)); \
".popsection\n\t" : : "i" (c)); \
}) })
#define instrumentation_begin() __instrumentation_begin(__COUNTER__) #define instrumentation_begin() __instrumentation_begin(__COUNTER__)
@ -48,9 +48,8 @@
*/ */
#define __instrumentation_end(c) ({ \ #define __instrumentation_end(c) ({ \
asm volatile(__stringify(c) ": nop\n\t" \ asm volatile(__stringify(c) ": nop\n\t" \
".pushsection .discard.instr_end\n\t" \ ANNOTATE_INSTR_END(__ASM_BREF(c)) \
".long " __stringify(c) "b - .\n\t" \ : : "i" (c)); \
".popsection\n\t" : : "i" (c)); \
}) })
#define instrumentation_end() __instrumentation_end(__COUNTER__) #define instrumentation_end() __instrumentation_end(__COUNTER__)
#else /* !CONFIG_NOINSTR_VALIDATION */ #else /* !CONFIG_NOINSTR_VALIDATION */

View File

@ -45,29 +45,25 @@
#define STACK_FRAME_NON_STANDARD_FP(func) #define STACK_FRAME_NON_STANDARD_FP(func)
#endif #endif
#define ANNOTATE_NOENDBR \
"986: \n\t" \
".pushsection .discard.noendbr\n\t" \
".long 986b\n\t" \
".popsection\n\t"
#define ASM_REACHABLE \ #define ASM_REACHABLE \
"998:\n\t" \ "998:\n\t" \
".pushsection .discard.reachable\n\t" \ ".pushsection .discard.reachable\n\t" \
".long 998b\n\t" \ ".long 998b\n\t" \
".popsection\n\t" ".popsection\n\t"
#else /* __ASSEMBLY__ */ #define __ASM_BREF(label) label ## b
/* #define __ASM_ANNOTATE(label, type) \
* This macro indicates that the following intra-function call is valid. ".pushsection .discard.annotate_insn,\"M\",@progbits,8\n\t" \
* Any non-annotated intra-function call will cause objtool to issue a warning. ".long " __stringify(label) " - .\n\t" \
*/ ".long " __stringify(type) "\n\t" \
#define ANNOTATE_INTRA_FUNCTION_CALL \ ".popsection\n\t"
999: \
.pushsection .discard.intra_function_calls; \ #define ASM_ANNOTATE(type) \
.long 999b; \ "911:\n\t" \
.popsection; __ASM_ANNOTATE(911b, type)
#else /* __ASSEMBLY__ */
/* /*
* In asm, there are two kinds of code: normal C-type callable functions and * In asm, there are two kinds of code: normal C-type callable functions and
@ -115,34 +111,11 @@
#endif #endif
.endm .endm
.macro ANNOTATE_NOENDBR .macro ANNOTATE type:req
.Lhere_\@: .Lhere_\@:
.pushsection .discard.noendbr .pushsection .discard.annotate_insn,"M",@progbits,8
.long .Lhere_\@
.popsection
.endm
/*
* Use objtool to validate the entry requirement that all code paths do
* VALIDATE_UNRET_END before RET.
*
* NOTE: The macro must be used at the beginning of a global symbol, otherwise
* it will be ignored.
*/
.macro VALIDATE_UNRET_BEGIN
#if defined(CONFIG_NOINSTR_VALIDATION) && \
(defined(CONFIG_MITIGATION_UNRET_ENTRY) || defined(CONFIG_MITIGATION_SRSO))
.Lhere_\@:
.pushsection .discard.validate_unret
.long .Lhere_\@ - . .long .Lhere_\@ - .
.popsection .long \type
#endif
.endm
.macro REACHABLE
.Lhere_\@:
.pushsection .discard.reachable
.long .Lhere_\@
.popsection .popsection
.endm .endm
@ -155,20 +128,77 @@
#define UNWIND_HINT(type, sp_reg, sp_offset, signal) "\n\t" #define UNWIND_HINT(type, sp_reg, sp_offset, signal) "\n\t"
#define STACK_FRAME_NON_STANDARD(func) #define STACK_FRAME_NON_STANDARD(func)
#define STACK_FRAME_NON_STANDARD_FP(func) #define STACK_FRAME_NON_STANDARD_FP(func)
#define ANNOTATE_NOENDBR #define __ASM_ANNOTATE(label, type)
#define ASM_REACHABLE #define ASM_ANNOTATE(type)
#else #else
#define ANNOTATE_INTRA_FUNCTION_CALL
.macro UNWIND_HINT type:req sp_reg=0 sp_offset=0 signal=0 .macro UNWIND_HINT type:req sp_reg=0 sp_offset=0 signal=0
.endm .endm
.macro STACK_FRAME_NON_STANDARD func:req .macro STACK_FRAME_NON_STANDARD func:req
.endm .endm
.macro ANNOTATE_NOENDBR .macro ANNOTATE type:req
.endm
.macro REACHABLE
.endm .endm
#endif #endif
#endif /* CONFIG_OBJTOOL */ #endif /* CONFIG_OBJTOOL */
#ifndef __ASSEMBLY__
/*
* Annotate away the various 'relocation to !ENDBR` complaints; knowing that
* these relocations will never be used for indirect calls.
*/
#define ANNOTATE_NOENDBR ASM_ANNOTATE(ANNOTYPE_NOENDBR)
/*
* This should be used immediately before an indirect jump/call. It tells
* objtool the subsequent indirect jump/call is vouched safe for retpoline
* builds.
*/
#define ANNOTATE_RETPOLINE_SAFE ASM_ANNOTATE(ANNOTYPE_RETPOLINE_SAFE)
/*
* See linux/instrumentation.h
*/
#define ANNOTATE_INSTR_BEGIN(label) __ASM_ANNOTATE(label, ANNOTYPE_INSTR_BEGIN)
#define ANNOTATE_INSTR_END(label) __ASM_ANNOTATE(label, ANNOTYPE_INSTR_END)
/*
* objtool annotation to ignore the alternatives and only consider the original
* instruction(s).
*/
#define ANNOTATE_IGNORE_ALTERNATIVE ASM_ANNOTATE(ANNOTYPE_IGNORE_ALTS)
/*
* 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 ASM_ANNOTATE(ANNOTYPE_INTRA_FUNCTION_CALL)
/*
* Use objtool to validate the entry requirement that all code paths do
* VALIDATE_UNRET_END before RET.
*
* NOTE: The macro must be used at the beginning of a global symbol, otherwise
* it will be ignored.
*/
#define ANNOTATE_UNRET_BEGIN ASM_ANNOTATE(ANNOTYPE_UNRET_BEGIN)
/*
* This should be used to refer to an instruction that is considered
* terminating, like a noreturn CALL or UD2 when we know they are not -- eg
* WARN using UD2.
*/
#define ANNOTATE_REACHABLE(label) __ASM_ANNOTATE(label, ANNOTYPE_REACHABLE)
#else
#define ANNOTATE_NOENDBR ANNOTATE type=ANNOTYPE_NOENDBR
#define ANNOTATE_RETPOLINE_SAFE ANNOTATE type=ANNOTYPE_RETPOLINE_SAFE
/* ANNOTATE_INSTR_BEGIN ANNOTATE type=ANNOTYPE_INSTR_BEGIN */
/* ANNOTATE_INSTR_END ANNOTATE type=ANNOTYPE_INSTR_END */
#define ANNOTATE_IGNORE_ALTERNATIVE ANNOTATE type=ANNOTYPE_IGNORE_ALTS
#define ANNOTATE_INTRA_FUNCTION_CALL ANNOTATE type=ANNOTYPE_INTRA_FUNCTION_CALL
#define ANNOTATE_UNRET_BEGIN ANNOTATE type=ANNOTYPE_UNRET_BEGIN
#define ANNOTATE_REACHABLE ANNOTATE type=ANNOTYPE_REACHABLE
#endif
#if defined(CONFIG_NOINSTR_VALIDATION) && \
(defined(CONFIG_MITIGATION_UNRET_ENTRY) || defined(CONFIG_MITIGATION_SRSO))
#define VALIDATE_UNRET_BEGIN ANNOTATE_UNRET_BEGIN
#else
#define VALIDATE_UNRET_BEGIN
#endif
#endif /* _LINUX_OBJTOOL_H */ #endif /* _LINUX_OBJTOOL_H */

View File

@ -54,4 +54,16 @@ struct unwind_hint {
#define UNWIND_HINT_TYPE_SAVE 6 #define UNWIND_HINT_TYPE_SAVE 6
#define UNWIND_HINT_TYPE_RESTORE 7 #define UNWIND_HINT_TYPE_RESTORE 7
/*
* Annotate types
*/
#define ANNOTYPE_NOENDBR 1
#define ANNOTYPE_RETPOLINE_SAFE 2
#define ANNOTYPE_INSTR_BEGIN 3
#define ANNOTYPE_INSTR_END 4
#define ANNOTYPE_UNRET_BEGIN 5
#define ANNOTYPE_IGNORE_ALTS 6
#define ANNOTYPE_INTRA_FUNCTION_CALL 7
#define ANNOTYPE_REACHABLE 8
#endif /* _LINUX_OBJTOOL_TYPES_H */ #endif /* _LINUX_OBJTOOL_TYPES_H */

View File

@ -54,4 +54,16 @@ struct unwind_hint {
#define UNWIND_HINT_TYPE_SAVE 6 #define UNWIND_HINT_TYPE_SAVE 6
#define UNWIND_HINT_TYPE_RESTORE 7 #define UNWIND_HINT_TYPE_RESTORE 7
/*
* Annotate types
*/
#define ANNOTYPE_NOENDBR 1
#define ANNOTYPE_RETPOLINE_SAFE 2
#define ANNOTYPE_INSTR_BEGIN 3
#define ANNOTYPE_INSTR_END 4
#define ANNOTYPE_UNRET_BEGIN 5
#define ANNOTYPE_IGNORE_ALTS 6
#define ANNOTYPE_INTRA_FUNCTION_CALL 7
#define ANNOTYPE_REACHABLE 8
#endif /* _LINUX_OBJTOOL_TYPES_H */ #endif /* _LINUX_OBJTOOL_TYPES_H */

View File

@ -9,7 +9,8 @@ bool arch_support_alt_relocation(struct special_alt *special_alt,
} }
struct reloc *arch_find_switch_table(struct objtool_file *file, struct reloc *arch_find_switch_table(struct objtool_file *file,
struct instruction *insn) struct instruction *insn,
unsigned long *table_size)
{ {
return NULL; return NULL;
} }

View File

@ -13,7 +13,8 @@ bool arch_support_alt_relocation(struct special_alt *special_alt,
} }
struct reloc *arch_find_switch_table(struct objtool_file *file, struct reloc *arch_find_switch_table(struct objtool_file *file,
struct instruction *insn) struct instruction *insn,
unsigned long *table_size)
{ {
exit(-1); exit(-1);
} }

View File

@ -109,7 +109,8 @@ bool arch_support_alt_relocation(struct special_alt *special_alt,
* NOTE: MITIGATION_RETPOLINE made it harder still to decode dynamic jumps. * NOTE: MITIGATION_RETPOLINE made it harder still to decode dynamic jumps.
*/ */
struct reloc *arch_find_switch_table(struct objtool_file *file, struct reloc *arch_find_switch_table(struct objtool_file *file,
struct instruction *insn) struct instruction *insn,
unsigned long *table_size)
{ {
struct reloc *text_reloc, *rodata_reloc; struct reloc *text_reloc, *rodata_reloc;
struct section *table_sec; struct section *table_sec;
@ -158,5 +159,6 @@ struct reloc *arch_find_switch_table(struct objtool_file *file,
if (reloc_type(text_reloc) == R_X86_64_PC32) if (reloc_type(text_reloc) == R_X86_64_PC32)
file->ignore_unreachables = true; file->ignore_unreachables = true;
*table_size = 0;
return rodata_reloc; return rodata_reloc;
} }

View File

@ -150,6 +150,15 @@ static inline struct reloc *insn_jump_table(struct instruction *insn)
return NULL; return NULL;
} }
static inline unsigned long insn_jump_table_size(struct instruction *insn)
{
if (insn->type == INSN_JUMP_DYNAMIC ||
insn->type == INSN_CALL_DYNAMIC)
return insn->_jump_table_size;
return 0;
}
static bool is_jump_table_jump(struct instruction *insn) static bool is_jump_table_jump(struct instruction *insn)
{ {
struct alt_group *alt_group = insn->alt_group; struct alt_group *alt_group = insn->alt_group;
@ -614,108 +623,6 @@ static int init_pv_ops(struct objtool_file *file)
return 0; return 0;
} }
static struct instruction *find_last_insn(struct objtool_file *file,
struct section *sec)
{
struct instruction *insn = NULL;
unsigned int offset;
unsigned int end = (sec->sh.sh_size > 10) ? sec->sh.sh_size - 10 : 0;
for (offset = sec->sh.sh_size - 1; offset >= end && !insn; offset--)
insn = find_insn(file, sec, offset);
return insn;
}
/*
* Mark "ud2" instructions and manually annotated dead ends.
*/
static int add_dead_ends(struct objtool_file *file)
{
struct section *rsec;
struct reloc *reloc;
struct instruction *insn;
uint64_t offset;
/*
* Check for manually annotated dead ends.
*/
rsec = find_section_by_name(file->elf, ".rela.discard.unreachable");
if (!rsec)
goto reachable;
for_each_reloc(rsec, reloc) {
if (reloc->sym->type == STT_SECTION) {
offset = reloc_addend(reloc);
} else if (reloc->sym->local_label) {
offset = reloc->sym->offset;
} else {
WARN("unexpected relocation symbol type in %s", rsec->name);
return -1;
}
insn = find_insn(file, reloc->sym->sec, offset);
if (insn)
insn = prev_insn_same_sec(file, insn);
else if (offset == reloc->sym->sec->sh.sh_size) {
insn = find_last_insn(file, reloc->sym->sec);
if (!insn) {
WARN("can't find unreachable insn at %s+0x%" PRIx64,
reloc->sym->sec->name, offset);
return -1;
}
} else {
WARN("can't find unreachable insn at %s+0x%" PRIx64,
reloc->sym->sec->name, offset);
return -1;
}
insn->dead_end = true;
}
reachable:
/*
* These manually annotated reachable checks are needed for GCC 4.4,
* where the Linux unreachable() macro isn't supported. In that case
* GCC doesn't know the "ud2" is fatal, so it generates code as if it's
* not a dead end.
*/
rsec = find_section_by_name(file->elf, ".rela.discard.reachable");
if (!rsec)
return 0;
for_each_reloc(rsec, reloc) {
if (reloc->sym->type == STT_SECTION) {
offset = reloc_addend(reloc);
} else if (reloc->sym->local_label) {
offset = reloc->sym->offset;
} else {
WARN("unexpected relocation symbol type in %s", rsec->name);
return -1;
}
insn = find_insn(file, reloc->sym->sec, offset);
if (insn)
insn = prev_insn_same_sec(file, insn);
else if (offset == reloc->sym->sec->sh.sh_size) {
insn = find_last_insn(file, reloc->sym->sec);
if (!insn) {
WARN("can't find reachable insn at %s+0x%" PRIx64,
reloc->sym->sec->name, offset);
return -1;
}
} else {
WARN("can't find reachable insn at %s+0x%" PRIx64,
reloc->sym->sec->name, offset);
return -1;
}
insn->dead_end = false;
}
return 0;
}
static int create_static_call_sections(struct objtool_file *file) static int create_static_call_sections(struct objtool_file *file)
{ {
struct static_call_site *site; struct static_call_site *site;
@ -1309,40 +1216,6 @@ static void add_uaccess_safe(struct objtool_file *file)
} }
} }
/*
* FIXME: For now, just ignore any alternatives which add retpolines. This is
* a temporary hack, as it doesn't allow ORC to unwind from inside a retpoline.
* But it at least allows objtool to understand the control flow *around* the
* retpoline.
*/
static int add_ignore_alternatives(struct objtool_file *file)
{
struct section *rsec;
struct reloc *reloc;
struct instruction *insn;
rsec = find_section_by_name(file->elf, ".rela.discard.ignore_alts");
if (!rsec)
return 0;
for_each_reloc(rsec, reloc) {
if (reloc->sym->type != STT_SECTION) {
WARN("unexpected relocation symbol type in %s", rsec->name);
return -1;
}
insn = find_insn(file, reloc->sym->sec, reloc_addend(reloc));
if (!insn) {
WARN("bad .discard.ignore_alts entry");
return -1;
}
insn->ignore_alts = true;
}
return 0;
}
/* /*
* Symbols that replace INSN_CALL_DYNAMIC, every (tail) call to such a symbol * Symbols that replace INSN_CALL_DYNAMIC, every (tail) call to such a symbol
* will be added to the .retpoline_sites section. * will be added to the .retpoline_sites section.
@ -2073,6 +1946,7 @@ out:
static int add_jump_table(struct objtool_file *file, struct instruction *insn, static int add_jump_table(struct objtool_file *file, struct instruction *insn,
struct reloc *next_table) struct reloc *next_table)
{ {
unsigned long table_size = insn_jump_table_size(insn);
struct symbol *pfunc = insn_func(insn)->pfunc; struct symbol *pfunc = insn_func(insn)->pfunc;
struct reloc *table = insn_jump_table(insn); struct reloc *table = insn_jump_table(insn);
struct instruction *dest_insn; struct instruction *dest_insn;
@ -2087,6 +1961,8 @@ static int add_jump_table(struct objtool_file *file, struct instruction *insn,
for_each_reloc_from(table->sec, reloc) { for_each_reloc_from(table->sec, reloc) {
/* Check for the end of the table: */ /* Check for the end of the table: */
if (table_size && reloc_offset(reloc) - reloc_offset(table) >= table_size)
break;
if (reloc != table && reloc == next_table) if (reloc != table && reloc == next_table)
break; break;
@ -2131,12 +2007,12 @@ static int add_jump_table(struct objtool_file *file, struct instruction *insn,
* find_jump_table() - Given a dynamic jump, find the switch jump table * find_jump_table() - Given a dynamic jump, find the switch jump table
* associated with it. * associated with it.
*/ */
static struct reloc *find_jump_table(struct objtool_file *file, static void find_jump_table(struct objtool_file *file, struct symbol *func,
struct symbol *func, struct instruction *insn)
struct instruction *insn)
{ {
struct reloc *table_reloc; struct reloc *table_reloc;
struct instruction *dest_insn, *orig_insn = insn; struct instruction *dest_insn, *orig_insn = insn;
unsigned long table_size;
/* /*
* Backward search using the @first_jump_src links, these help avoid * Backward search using the @first_jump_src links, these help avoid
@ -2157,17 +2033,17 @@ static struct reloc *find_jump_table(struct objtool_file *file,
insn->jump_dest->offset > orig_insn->offset)) insn->jump_dest->offset > orig_insn->offset))
break; break;
table_reloc = arch_find_switch_table(file, insn); table_reloc = arch_find_switch_table(file, insn, &table_size);
if (!table_reloc) if (!table_reloc)
continue; continue;
dest_insn = find_insn(file, table_reloc->sym->sec, reloc_addend(table_reloc)); dest_insn = find_insn(file, table_reloc->sym->sec, reloc_addend(table_reloc));
if (!dest_insn || !insn_func(dest_insn) || insn_func(dest_insn)->pfunc != func) if (!dest_insn || !insn_func(dest_insn) || insn_func(dest_insn)->pfunc != func)
continue; continue;
return table_reloc; orig_insn->_jump_table = table_reloc;
orig_insn->_jump_table_size = table_size;
break;
} }
return NULL;
} }
/* /*
@ -2178,7 +2054,6 @@ static void mark_func_jump_tables(struct objtool_file *file,
struct symbol *func) struct symbol *func)
{ {
struct instruction *insn, *last = NULL; struct instruction *insn, *last = NULL;
struct reloc *reloc;
func_for_each_insn(file, func, insn) { func_for_each_insn(file, func, insn) {
if (!last) if (!last)
@ -2201,9 +2076,7 @@ static void mark_func_jump_tables(struct objtool_file *file,
if (insn->type != INSN_JUMP_DYNAMIC) if (insn->type != INSN_JUMP_DYNAMIC)
continue; continue;
reloc = find_jump_table(file, func, insn); find_jump_table(file, func, insn);
if (reloc)
insn->_jump_table = reloc;
} }
} }
@ -2373,52 +2246,109 @@ static int read_unwind_hints(struct objtool_file *file)
return 0; return 0;
} }
static int read_noendbr_hints(struct objtool_file *file) static int read_annotate(struct objtool_file *file,
int (*func)(struct objtool_file *file, int type, struct instruction *insn))
{ {
struct section *sec;
struct instruction *insn; struct instruction *insn;
struct section *rsec;
struct reloc *reloc; struct reloc *reloc;
uint64_t offset;
int type, ret;
rsec = find_section_by_name(file->elf, ".rela.discard.noendbr"); sec = find_section_by_name(file->elf, ".discard.annotate_insn");
if (!rsec) if (!sec)
return 0; return 0;
for_each_reloc(rsec, reloc) { if (!sec->rsec)
insn = find_insn(file, reloc->sym->sec, return 0;
reloc->sym->offset + reloc_addend(reloc));
if (sec->sh.sh_entsize != 8) {
static bool warned = false;
if (!warned) {
WARN("%s: dodgy linker, sh_entsize != 8", sec->name);
warned = true;
}
sec->sh.sh_entsize = 8;
}
for_each_reloc(sec->rsec, reloc) {
type = *(u32 *)(sec->data->d_buf + (reloc_idx(reloc) * sec->sh.sh_entsize) + 4);
offset = reloc->sym->offset + reloc_addend(reloc);
insn = find_insn(file, reloc->sym->sec, offset);
if (!insn) { if (!insn) {
WARN("bad .discard.noendbr entry"); WARN("bad .discard.annotate_insn entry: %d of type %d", reloc_idx(reloc), type);
return -1; return -1;
} }
insn->noendbr = 1; ret = func(file, type, insn);
if (ret < 0)
return ret;
} }
return 0; return 0;
} }
static int read_retpoline_hints(struct objtool_file *file) static int __annotate_early(struct objtool_file *file, int type, struct instruction *insn)
{ {
struct section *rsec; switch (type) {
struct instruction *insn; case ANNOTYPE_IGNORE_ALTS:
struct reloc *reloc; insn->ignore_alts = true;
break;
rsec = find_section_by_name(file->elf, ".rela.discard.retpoline_safe"); /*
if (!rsec) * Must be before read_unwind_hints() since that needs insn->noendbr.
*/
case ANNOTYPE_NOENDBR:
insn->noendbr = 1;
break;
default:
break;
}
return 0;
}
static int __annotate_ifc(struct objtool_file *file, int type, struct instruction *insn)
{
unsigned long dest_off;
if (type != ANNOTYPE_INTRA_FUNCTION_CALL)
return 0; return 0;
for_each_reloc(rsec, reloc) { if (insn->type != INSN_CALL) {
if (reloc->sym->type != STT_SECTION) { WARN_INSN(insn, "intra_function_call not a direct call");
WARN("unexpected relocation symbol type in %s", rsec->name); return -1;
return -1; }
}
insn = find_insn(file, reloc->sym->sec, reloc_addend(reloc)); /*
if (!insn) { * Treat intra-function CALLs as JMPs, but with a stack_op.
WARN("bad .discard.retpoline_safe entry"); * See add_call_destinations(), which strips stack_ops from
return -1; * normal CALLs.
} */
insn->type = INSN_JUMP_UNCONDITIONAL;
dest_off = arch_jump_destination(insn);
insn->jump_dest = find_insn(file, insn->sec, dest_off);
if (!insn->jump_dest) {
WARN_INSN(insn, "can't find call dest at %s+0x%lx",
insn->sec->name, dest_off);
return -1;
}
return 0;
}
static int __annotate_late(struct objtool_file *file, int type, struct instruction *insn)
{
switch (type) {
case ANNOTYPE_NOENDBR:
/* early */
break;
case ANNOTYPE_RETPOLINE_SAFE:
if (insn->type != INSN_JUMP_DYNAMIC && if (insn->type != INSN_JUMP_DYNAMIC &&
insn->type != INSN_CALL_DYNAMIC && insn->type != INSN_CALL_DYNAMIC &&
insn->type != INSN_RETURN && insn->type != INSN_RETURN &&
@ -2428,130 +2358,35 @@ static int read_retpoline_hints(struct objtool_file *file)
} }
insn->retpoline_safe = true; insn->retpoline_safe = true;
} break;
return 0;
}
static int read_instr_hints(struct objtool_file *file)
{
struct section *rsec;
struct instruction *insn;
struct reloc *reloc;
rsec = find_section_by_name(file->elf, ".rela.discard.instr_end");
if (!rsec)
return 0;
for_each_reloc(rsec, reloc) {
if (reloc->sym->type != STT_SECTION) {
WARN("unexpected relocation symbol type in %s", rsec->name);
return -1;
}
insn = find_insn(file, reloc->sym->sec, reloc_addend(reloc));
if (!insn) {
WARN("bad .discard.instr_end entry");
return -1;
}
insn->instr--;
}
rsec = find_section_by_name(file->elf, ".rela.discard.instr_begin");
if (!rsec)
return 0;
for_each_reloc(rsec, reloc) {
if (reloc->sym->type != STT_SECTION) {
WARN("unexpected relocation symbol type in %s", rsec->name);
return -1;
}
insn = find_insn(file, reloc->sym->sec, reloc_addend(reloc));
if (!insn) {
WARN("bad .discard.instr_begin entry");
return -1;
}
case ANNOTYPE_INSTR_BEGIN:
insn->instr++; insn->instr++;
} break;
return 0; case ANNOTYPE_INSTR_END:
} insn->instr--;
break;
static int read_validate_unret_hints(struct objtool_file *file) case ANNOTYPE_UNRET_BEGIN:
{
struct section *rsec;
struct instruction *insn;
struct reloc *reloc;
rsec = find_section_by_name(file->elf, ".rela.discard.validate_unret");
if (!rsec)
return 0;
for_each_reloc(rsec, reloc) {
if (reloc->sym->type != STT_SECTION) {
WARN("unexpected relocation symbol type in %s", rsec->name);
return -1;
}
insn = find_insn(file, reloc->sym->sec, reloc_addend(reloc));
if (!insn) {
WARN("bad .discard.instr_end entry");
return -1;
}
insn->unret = 1; insn->unret = 1;
} break;
return 0; case ANNOTYPE_IGNORE_ALTS:
} /* early */
break;
case ANNOTYPE_INTRA_FUNCTION_CALL:
/* ifc */
break;
static int read_intra_function_calls(struct objtool_file *file) case ANNOTYPE_REACHABLE:
{ insn->dead_end = false;
struct instruction *insn; break;
struct section *rsec;
struct reloc *reloc;
rsec = find_section_by_name(file->elf, ".rela.discard.intra_function_calls"); default:
if (!rsec) WARN_INSN(insn, "Unknown annotation type: %d", type);
return 0; break;
for_each_reloc(rsec, reloc) {
unsigned long dest_off;
if (reloc->sym->type != STT_SECTION) {
WARN("unexpected relocation symbol type in %s",
rsec->name);
return -1;
}
insn = find_insn(file, reloc->sym->sec, reloc_addend(reloc));
if (!insn) {
WARN("bad .discard.intra_function_call entry");
return -1;
}
if (insn->type != INSN_CALL) {
WARN_INSN(insn, "intra_function_call not a direct call");
return -1;
}
/*
* Treat intra-function CALLs as JMPs, but with a stack_op.
* See add_call_destinations(), which strips stack_ops from
* normal CALLs.
*/
insn->type = INSN_JUMP_UNCONDITIONAL;
dest_off = arch_jump_destination(insn);
insn->jump_dest = find_insn(file, insn->sec, dest_off);
if (!insn->jump_dest) {
WARN_INSN(insn, "can't find call dest at %s+0x%lx",
insn->sec->name, dest_off);
return -1;
}
} }
return 0; return 0;
@ -2666,14 +2501,7 @@ static int decode_sections(struct objtool_file *file)
add_ignores(file); add_ignores(file);
add_uaccess_safe(file); add_uaccess_safe(file);
ret = add_ignore_alternatives(file); ret = read_annotate(file, __annotate_early);
if (ret)
return ret;
/*
* Must be before read_unwind_hints() since that needs insn->noendbr.
*/
ret = read_noendbr_hints(file);
if (ret) if (ret)
return ret; return ret;
@ -2695,7 +2523,7 @@ static int decode_sections(struct objtool_file *file)
* Must be before add_call_destination(); it changes INSN_CALL to * Must be before add_call_destination(); it changes INSN_CALL to
* INSN_JUMP. * INSN_JUMP.
*/ */
ret = read_intra_function_calls(file); ret = read_annotate(file, __annotate_ifc);
if (ret) if (ret)
return ret; return ret;
@ -2703,14 +2531,6 @@ static int decode_sections(struct objtool_file *file)
if (ret) if (ret)
return ret; return ret;
/*
* Must be after add_call_destinations() such that it can override
* dead_end_function() marks.
*/
ret = add_dead_ends(file);
if (ret)
return ret;
ret = add_jump_table_alts(file); ret = add_jump_table_alts(file);
if (ret) if (ret)
return ret; return ret;
@ -2719,15 +2539,11 @@ static int decode_sections(struct objtool_file *file)
if (ret) if (ret)
return ret; return ret;
ret = read_retpoline_hints(file); /*
if (ret) * Must be after add_call_destinations() such that it can override
return ret; * dead_end_function() marks.
*/
ret = read_instr_hints(file); ret = read_annotate(file, __annotate_late);
if (ret)
return ret;
ret = read_validate_unret_hints(file);
if (ret) if (ret)
return ret; return ret;

View File

@ -71,7 +71,10 @@ struct instruction {
struct instruction *first_jump_src; struct instruction *first_jump_src;
union { union {
struct symbol *_call_dest; struct symbol *_call_dest;
struct reloc *_jump_table; struct {
struct reloc *_jump_table;
unsigned long _jump_table_size;
};
}; };
struct alternative *alts; struct alternative *alts;
struct symbol *sym; struct symbol *sym;

View File

@ -38,5 +38,6 @@ bool arch_support_alt_relocation(struct special_alt *special_alt,
struct instruction *insn, struct instruction *insn,
struct reloc *reloc); struct reloc *reloc);
struct reloc *arch_find_switch_table(struct objtool_file *file, struct reloc *arch_find_switch_table(struct objtool_file *file,
struct instruction *insn); struct instruction *insn,
unsigned long *table_size);
#endif /* _SPECIAL_H */ #endif /* _SPECIAL_H */