Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge tag 'objtool-core-2025-01-20' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip

Pull objtool updates from Ingo Molnar:

- Introduce the generic section-based annotation infrastructure a.k.a.
ASM_ANNOTATE/ANNOTATE (Peter Zijlstra)

- Convert various facilities to ASM_ANNOTATE/ANNOTATE: (Peter Zijlstra)
- ANNOTATE_NOENDBR
- ANNOTATE_RETPOLINE_SAFE
- instrumentation_{begin,end}()
- VALIDATE_UNRET_BEGIN
- ANNOTATE_IGNORE_ALTERNATIVE
- ANNOTATE_INTRA_FUNCTION_CALL
- {.UN}REACHABLE

- Optimize the annotation-sections parsing code (Peter Zijlstra)

- Centralize annotation definitions in <linux/objtool.h>

- Unify & simplify the barrier_before_unreachable()/unreachable()
definitions (Peter Zijlstra)

- Convert unreachable() calls to BUG() in x86 code, as unreachable()
has unreliable code generation (Peter Zijlstra)

- Remove annotate_reachable() and annotate_unreachable(), as it's
unreliable against compiler optimizations (Peter Zijlstra)

- Fix non-standard ANNOTATE_REACHABLE annotation order (Peter Zijlstra)

- Robustify the annotation code by warning about unknown annotation
types (Peter Zijlstra)

- Allow arch code to discover jump table size, in preparation of
annotated jump table support (Ard Biesheuvel)

* tag 'objtool-core-2025-01-20' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip:
x86/mm: Convert unreachable() to BUG()
objtool: Allow arch code to discover jump table size
objtool: Warn about unknown annotation types
objtool: Fix ANNOTATE_REACHABLE to be a normal annotation
objtool: Convert {.UN}REACHABLE to ANNOTATE
objtool: Remove annotate_{,un}reachable()
loongarch: Use ASM_REACHABLE
x86: Convert unreachable() to BUG()
unreachable: Unify
objtool: Collect more annotations in objtool.h
objtool: Collapse annotate sequences
objtool: Convert ANNOTATE_INTRA_FUNCTION_CALL to ANNOTATE
objtool: Convert ANNOTATE_IGNORE_ALTERNATIVE to ANNOTATE
objtool: Convert VALIDATE_UNRET_BEGIN to ANNOTATE
objtool: Convert instrumentation_{begin,end}() to ANNOTATE
objtool: Convert ANNOTATE_RETPOLINE_SAFE to ANNOTATE
objtool: Convert ANNOTATE_NOENDBR to ANNOTATE
objtool: Generic annotation infrastructure

+275 -471
+7 -6
arch/loongarch/include/asm/bug.h
··· 4 4 5 5 #include <asm/break.h> 6 6 #include <linux/stringify.h> 7 + #include <linux/objtool.h> 7 8 8 9 #ifndef CONFIG_DEBUG_BUGVERBOSE 9 10 #define _BUGVERBOSE_LOCATION(file, line) ··· 34 33 35 34 #define ASM_BUG_FLAGS(flags) \ 36 35 __BUG_ENTRY(flags) \ 37 - break BRK_BUG 36 + break BRK_BUG; 38 37 39 38 #define ASM_BUG() ASM_BUG_FLAGS(0) 40 39 41 - #define __BUG_FLAGS(flags) \ 42 - asm_inline volatile (__stringify(ASM_BUG_FLAGS(flags))); 40 + #define __BUG_FLAGS(flags, extra) \ 41 + asm_inline volatile (__stringify(ASM_BUG_FLAGS(flags)) \ 42 + extra); 43 43 44 44 #define __WARN_FLAGS(flags) \ 45 45 do { \ 46 46 instrumentation_begin(); \ 47 - __BUG_FLAGS(BUGFLAG_WARNING|(flags)); \ 48 - annotate_reachable(); \ 47 + __BUG_FLAGS(BUGFLAG_WARNING|(flags), ANNOTATE_REACHABLE(10001b));\ 49 48 instrumentation_end(); \ 50 49 } while (0) 51 50 52 51 #define BUG() \ 53 52 do { \ 54 53 instrumentation_begin(); \ 55 - __BUG_FLAGS(0); \ 54 + __BUG_FLAGS(0, ""); \ 56 55 unreachable(); \ 57 56 } while (0) 58 57
+4 -5
arch/x86/entry/entry_64.S
··· 308 308 movq $-1, ORIG_RAX(%rsp) /* no syscall to restart */ 309 309 .endif 310 310 311 - call \cfunc 312 - 313 311 /* For some configurations \cfunc ends up being a noreturn. */ 314 - REACHABLE 312 + ANNOTATE_REACHABLE 313 + call \cfunc 315 314 316 315 jmp error_return 317 316 .endm ··· 528 529 movq %rsp, %rdi /* pt_regs pointer into first argument */ 529 530 movq ORIG_RAX(%rsp), %rsi /* get error code into 2nd argument*/ 530 531 movq $-1, ORIG_RAX(%rsp) /* no syscall to restart */ 531 - call \cfunc 532 532 533 533 /* For some configurations \cfunc ends up being a noreturn. */ 534 - REACHABLE 534 + ANNOTATE_REACHABLE 535 + call \cfunc 535 536 536 537 jmp paranoid_exit 537 538
+1 -21
arch/x86/include/asm/alternative.h
··· 4 4 5 5 #include <linux/types.h> 6 6 #include <linux/stringify.h> 7 + #include <linux/objtool.h> 7 8 #include <asm/asm.h> 8 9 9 10 #define ALT_FLAGS_SHIFT 16 ··· 54 53 #define LOCK_PREFIX_HERE "" 55 54 #define LOCK_PREFIX "" 56 55 #endif 57 - 58 - /* 59 - * objtool annotation to ignore the alternatives and only consider the original 60 - * instruction(s). 61 - */ 62 - #define ANNOTATE_IGNORE_ALTERNATIVE \ 63 - "999:\n\t" \ 64 - ".pushsection .discard.ignore_alts\n\t" \ 65 - ".long 999b\n\t" \ 66 - ".popsection\n\t" 67 56 68 57 /* 69 58 * The patching flags are part of the upper bits of the @ft_flags parameter when ··· 300 309 .macro LOCK_PREFIX 301 310 .endm 302 311 #endif 303 - 304 - /* 305 - * objtool annotation to ignore the alternatives and only consider the original 306 - * instruction(s). 307 - */ 308 - .macro ANNOTATE_IGNORE_ALTERNATIVE 309 - .Lannotate_\@: 310 - .pushsection .discard.ignore_alts 311 - .long .Lannotate_\@ 312 - .popsection 313 - .endm 314 312 315 313 /* 316 314 * Issue one struct alt_instr descriptor entry (need to put it into
+1 -1
arch/x86/include/asm/bug.h
··· 92 92 do { \ 93 93 __auto_type __flags = BUGFLAG_WARNING|(flags); \ 94 94 instrumentation_begin(); \ 95 - _BUG_FLAGS(ASM_UD2, __flags, ASM_REACHABLE); \ 95 + _BUG_FLAGS(ASM_UD2, __flags, ANNOTATE_REACHABLE(1b)); \ 96 96 instrumentation_end(); \ 97 97 } while (0) 98 98
+2 -2
arch/x86/include/asm/irq_stack.h
··· 100 100 } 101 101 102 102 #define ASM_CALL_ARG0 \ 103 - "call %c[__func] \n" \ 104 - ASM_REACHABLE 103 + "1: call %c[__func] \n" \ 104 + ANNOTATE_REACHABLE(1b) 105 105 106 106 #define ASM_CALL_ARG1 \ 107 107 "movq %[arg1], %%rdi \n" \
-18
arch/x86/include/asm/nospec-branch.h
··· 180 180 #ifdef __ASSEMBLY__ 181 181 182 182 /* 183 - * This should be used immediately before an indirect jump/call. It tells 184 - * objtool the subsequent indirect jump/call is vouched safe for retpoline 185 - * builds. 186 - */ 187 - .macro ANNOTATE_RETPOLINE_SAFE 188 - .Lhere_\@: 189 - .pushsection .discard.retpoline_safe 190 - .long .Lhere_\@ 191 - .popsection 192 - .endm 193 - 194 - /* 195 183 * (ab)use RETPOLINE_SAFE on RET to annotate away 'bare' RET instructions 196 184 * vs RETBleed validation. 197 185 */ ··· 337 349 #endif 338 350 339 351 #else /* __ASSEMBLY__ */ 340 - 341 - #define ANNOTATE_RETPOLINE_SAFE \ 342 - "999:\n\t" \ 343 - ".pushsection .discard.retpoline_safe\n\t" \ 344 - ".long 999b\n\t" \ 345 - ".popsection\n\t" 346 352 347 353 typedef u8 retpoline_thunk_t[RETPOLINE_THUNK_SIZE]; 348 354 extern retpoline_thunk_t __x86_indirect_thunk_array[];
+1 -1
arch/x86/kernel/process.c
··· 839 839 #ifdef CONFIG_SMP 840 840 if (smp_ops.stop_this_cpu) { 841 841 smp_ops.stop_this_cpu(); 842 - unreachable(); 842 + BUG(); 843 843 } 844 844 #endif 845 845
+1 -1
arch/x86/kernel/reboot.c
··· 883 883 884 884 if (smp_ops.stop_this_cpu) { 885 885 smp_ops.stop_this_cpu(); 886 - unreachable(); 886 + BUG(); 887 887 } 888 888 889 889 /* Assume hlt works */
+1 -1
arch/x86/kvm/svm/sev.c
··· 3820 3820 goto next_range; 3821 3821 } 3822 3822 3823 - unreachable(); 3823 + BUG(); 3824 3824 } 3825 3825 3826 3826 static int __sev_snp_update_protected_guest_state(struct kvm_vcpu *vcpu)
+1 -1
arch/x86/mm/fault.c
··· 678 678 ASM_CALL_ARG3, 679 679 , [arg1] "r" (regs), [arg2] "r" (address), [arg3] "r" (&info)); 680 680 681 - unreachable(); 681 + BUG(); 682 682 } 683 683 #endif 684 684
-12
include/linux/compiler-gcc.h
··· 52 52 */ 53 53 #define barrier_before_unreachable() asm volatile("") 54 54 55 - /* 56 - * Mark a position in code as unreachable. This can be used to 57 - * suppress control flow warnings after asm blocks that transfer 58 - * control elsewhere. 59 - */ 60 - #define unreachable() \ 61 - do { \ 62 - annotate_unreachable(); \ 63 - barrier_before_unreachable(); \ 64 - __builtin_unreachable(); \ 65 - } while (0) 66 - 67 55 #if defined(CONFIG_ARCH_USE_BUILTIN_BSWAP) 68 56 #define __HAVE_BUILTIN_BSWAP32__ 69 57 #define __HAVE_BUILTIN_BSWAP64__
+7 -30
include/linux/compiler.h
··· 109 109 110 110 /* Unreachable code */ 111 111 #ifdef CONFIG_OBJTOOL 112 - /* 113 - * These macros help objtool understand GCC code flow for unreachable code. 114 - * The __COUNTER__ based labels are a hack to make each instance of the macros 115 - * unique, to convince GCC not to merge duplicate inline asm statements. 116 - */ 117 - #define __stringify_label(n) #n 118 - 119 - #define __annotate_reachable(c) ({ \ 120 - asm volatile(__stringify_label(c) ":\n\t" \ 121 - ".pushsection .discard.reachable\n\t" \ 122 - ".long " __stringify_label(c) "b - .\n\t" \ 123 - ".popsection\n\t"); \ 124 - }) 125 - #define annotate_reachable() __annotate_reachable(__COUNTER__) 126 - 127 - #define __annotate_unreachable(c) ({ \ 128 - asm volatile(__stringify_label(c) ":\n\t" \ 129 - ".pushsection .discard.unreachable\n\t" \ 130 - ".long " __stringify_label(c) "b - .\n\t" \ 131 - ".popsection\n\t" : : "i" (c)); \ 132 - }) 133 - #define annotate_unreachable() __annotate_unreachable(__COUNTER__) 134 - 135 112 /* Annotate a C jump table to allow objtool to follow the code flow */ 136 113 #define __annotate_jump_table __section(".rodata..c_jump_table,\"a\",@progbits #") 137 - 138 114 #else /* !CONFIG_OBJTOOL */ 139 - #define annotate_reachable() 140 - #define annotate_unreachable() 141 115 #define __annotate_jump_table 142 116 #endif /* CONFIG_OBJTOOL */ 143 117 144 - #ifndef unreachable 145 - # define unreachable() do { \ 146 - annotate_unreachable(); \ 118 + /* 119 + * Mark a position in code as unreachable. This can be used to 120 + * suppress control flow warnings after asm blocks that transfer 121 + * control elsewhere. 122 + */ 123 + #define unreachable() do { \ 124 + barrier_before_unreachable(); \ 147 125 __builtin_unreachable(); \ 148 126 } while (0) 149 - #endif 150 127 151 128 /* 152 129 * KENTRY - kernel entry point
+5 -6
include/linux/instrumentation.h
··· 4 4 5 5 #ifdef CONFIG_NOINSTR_VALIDATION 6 6 7 + #include <linux/objtool.h> 7 8 #include <linux/stringify.h> 8 9 9 10 /* Begin/end of an instrumentation safe region */ 10 11 #define __instrumentation_begin(c) ({ \ 11 12 asm volatile(__stringify(c) ": nop\n\t" \ 12 - ".pushsection .discard.instr_begin\n\t" \ 13 - ".long " __stringify(c) "b - .\n\t" \ 14 - ".popsection\n\t" : : "i" (c)); \ 13 + ANNOTATE_INSTR_BEGIN(__ASM_BREF(c)) \ 14 + : : "i" (c)); \ 15 15 }) 16 16 #define instrumentation_begin() __instrumentation_begin(__COUNTER__) 17 17 ··· 48 48 */ 49 49 #define __instrumentation_end(c) ({ \ 50 50 asm volatile(__stringify(c) ": nop\n\t" \ 51 - ".pushsection .discard.instr_end\n\t" \ 52 - ".long " __stringify(c) "b - .\n\t" \ 53 - ".popsection\n\t" : : "i" (c)); \ 51 + ANNOTATE_INSTR_END(__ASM_BREF(c)) \ 52 + : : "i" (c)); \ 54 53 }) 55 54 #define instrumentation_end() __instrumentation_end(__COUNTER__) 56 55 #else /* !CONFIG_NOINSTR_VALIDATION */
+78 -48
include/linux/objtool.h
··· 45 45 #define STACK_FRAME_NON_STANDARD_FP(func) 46 46 #endif 47 47 48 - #define ANNOTATE_NOENDBR \ 49 - "986: \n\t" \ 50 - ".pushsection .discard.noendbr\n\t" \ 51 - ".long 986b\n\t" \ 52 - ".popsection\n\t" 53 - 54 48 #define ASM_REACHABLE \ 55 49 "998:\n\t" \ 56 50 ".pushsection .discard.reachable\n\t" \ 57 51 ".long 998b\n\t" \ 58 52 ".popsection\n\t" 59 53 60 - #else /* __ASSEMBLY__ */ 54 + #define __ASM_BREF(label) label ## b 61 55 62 - /* 63 - * This macro indicates that the following intra-function call is valid. 64 - * Any non-annotated intra-function call will cause objtool to issue a warning. 65 - */ 66 - #define ANNOTATE_INTRA_FUNCTION_CALL \ 67 - 999: \ 68 - .pushsection .discard.intra_function_calls; \ 69 - .long 999b; \ 70 - .popsection; 56 + #define __ASM_ANNOTATE(label, type) \ 57 + ".pushsection .discard.annotate_insn,\"M\",@progbits,8\n\t" \ 58 + ".long " __stringify(label) " - .\n\t" \ 59 + ".long " __stringify(type) "\n\t" \ 60 + ".popsection\n\t" 61 + 62 + #define ASM_ANNOTATE(type) \ 63 + "911:\n\t" \ 64 + __ASM_ANNOTATE(911b, type) 65 + 66 + #else /* __ASSEMBLY__ */ 71 67 72 68 /* 73 69 * In asm, there are two kinds of code: normal C-type callable functions and ··· 111 115 #endif 112 116 .endm 113 117 114 - .macro ANNOTATE_NOENDBR 118 + .macro ANNOTATE type:req 115 119 .Lhere_\@: 116 - .pushsection .discard.noendbr 117 - .long .Lhere_\@ 118 - .popsection 119 - .endm 120 - 121 - /* 122 - * Use objtool to validate the entry requirement that all code paths do 123 - * VALIDATE_UNRET_END before RET. 124 - * 125 - * NOTE: The macro must be used at the beginning of a global symbol, otherwise 126 - * it will be ignored. 127 - */ 128 - .macro VALIDATE_UNRET_BEGIN 129 - #if defined(CONFIG_NOINSTR_VALIDATION) && \ 130 - (defined(CONFIG_MITIGATION_UNRET_ENTRY) || defined(CONFIG_MITIGATION_SRSO)) 131 - .Lhere_\@: 132 - .pushsection .discard.validate_unret 120 + .pushsection .discard.annotate_insn,"M",@progbits,8 133 121 .long .Lhere_\@ - . 134 - .popsection 135 - #endif 136 - .endm 137 - 138 - .macro REACHABLE 139 - .Lhere_\@: 140 - .pushsection .discard.reachable 141 - .long .Lhere_\@ 122 + .long \type 142 123 .popsection 143 124 .endm 144 125 ··· 128 155 #define UNWIND_HINT(type, sp_reg, sp_offset, signal) "\n\t" 129 156 #define STACK_FRAME_NON_STANDARD(func) 130 157 #define STACK_FRAME_NON_STANDARD_FP(func) 131 - #define ANNOTATE_NOENDBR 132 - #define ASM_REACHABLE 158 + #define __ASM_ANNOTATE(label, type) 159 + #define ASM_ANNOTATE(type) 133 160 #else 134 - #define ANNOTATE_INTRA_FUNCTION_CALL 135 161 .macro UNWIND_HINT type:req sp_reg=0 sp_offset=0 signal=0 136 162 .endm 137 163 .macro STACK_FRAME_NON_STANDARD func:req 138 164 .endm 139 - .macro ANNOTATE_NOENDBR 140 - .endm 141 - .macro REACHABLE 165 + .macro ANNOTATE type:req 142 166 .endm 143 167 #endif 144 168 145 169 #endif /* CONFIG_OBJTOOL */ 170 + 171 + #ifndef __ASSEMBLY__ 172 + /* 173 + * Annotate away the various 'relocation to !ENDBR` complaints; knowing that 174 + * these relocations will never be used for indirect calls. 175 + */ 176 + #define ANNOTATE_NOENDBR ASM_ANNOTATE(ANNOTYPE_NOENDBR) 177 + /* 178 + * This should be used immediately before an indirect jump/call. It tells 179 + * objtool the subsequent indirect jump/call is vouched safe for retpoline 180 + * builds. 181 + */ 182 + #define ANNOTATE_RETPOLINE_SAFE ASM_ANNOTATE(ANNOTYPE_RETPOLINE_SAFE) 183 + /* 184 + * See linux/instrumentation.h 185 + */ 186 + #define ANNOTATE_INSTR_BEGIN(label) __ASM_ANNOTATE(label, ANNOTYPE_INSTR_BEGIN) 187 + #define ANNOTATE_INSTR_END(label) __ASM_ANNOTATE(label, ANNOTYPE_INSTR_END) 188 + /* 189 + * objtool annotation to ignore the alternatives and only consider the original 190 + * instruction(s). 191 + */ 192 + #define ANNOTATE_IGNORE_ALTERNATIVE ASM_ANNOTATE(ANNOTYPE_IGNORE_ALTS) 193 + /* 194 + * This macro indicates that the following intra-function call is valid. 195 + * Any non-annotated intra-function call will cause objtool to issue a warning. 196 + */ 197 + #define ANNOTATE_INTRA_FUNCTION_CALL ASM_ANNOTATE(ANNOTYPE_INTRA_FUNCTION_CALL) 198 + /* 199 + * Use objtool to validate the entry requirement that all code paths do 200 + * VALIDATE_UNRET_END before RET. 201 + * 202 + * NOTE: The macro must be used at the beginning of a global symbol, otherwise 203 + * it will be ignored. 204 + */ 205 + #define ANNOTATE_UNRET_BEGIN ASM_ANNOTATE(ANNOTYPE_UNRET_BEGIN) 206 + /* 207 + * This should be used to refer to an instruction that is considered 208 + * terminating, like a noreturn CALL or UD2 when we know they are not -- eg 209 + * WARN using UD2. 210 + */ 211 + #define ANNOTATE_REACHABLE(label) __ASM_ANNOTATE(label, ANNOTYPE_REACHABLE) 212 + 213 + #else 214 + #define ANNOTATE_NOENDBR ANNOTATE type=ANNOTYPE_NOENDBR 215 + #define ANNOTATE_RETPOLINE_SAFE ANNOTATE type=ANNOTYPE_RETPOLINE_SAFE 216 + /* ANNOTATE_INSTR_BEGIN ANNOTATE type=ANNOTYPE_INSTR_BEGIN */ 217 + /* ANNOTATE_INSTR_END ANNOTATE type=ANNOTYPE_INSTR_END */ 218 + #define ANNOTATE_IGNORE_ALTERNATIVE ANNOTATE type=ANNOTYPE_IGNORE_ALTS 219 + #define ANNOTATE_INTRA_FUNCTION_CALL ANNOTATE type=ANNOTYPE_INTRA_FUNCTION_CALL 220 + #define ANNOTATE_UNRET_BEGIN ANNOTATE type=ANNOTYPE_UNRET_BEGIN 221 + #define ANNOTATE_REACHABLE ANNOTATE type=ANNOTYPE_REACHABLE 222 + #endif 223 + 224 + #if defined(CONFIG_NOINSTR_VALIDATION) && \ 225 + (defined(CONFIG_MITIGATION_UNRET_ENTRY) || defined(CONFIG_MITIGATION_SRSO)) 226 + #define VALIDATE_UNRET_BEGIN ANNOTATE_UNRET_BEGIN 227 + #else 228 + #define VALIDATE_UNRET_BEGIN 229 + #endif 146 230 147 231 #endif /* _LINUX_OBJTOOL_H */
+12
include/linux/objtool_types.h
··· 54 54 #define UNWIND_HINT_TYPE_SAVE 6 55 55 #define UNWIND_HINT_TYPE_RESTORE 7 56 56 57 + /* 58 + * Annotate types 59 + */ 60 + #define ANNOTYPE_NOENDBR 1 61 + #define ANNOTYPE_RETPOLINE_SAFE 2 62 + #define ANNOTYPE_INSTR_BEGIN 3 63 + #define ANNOTYPE_INSTR_END 4 64 + #define ANNOTYPE_UNRET_BEGIN 5 65 + #define ANNOTYPE_IGNORE_ALTS 6 66 + #define ANNOTYPE_INTRA_FUNCTION_CALL 7 67 + #define ANNOTYPE_REACHABLE 8 68 + 57 69 #endif /* _LINUX_OBJTOOL_TYPES_H */
+12
tools/include/linux/objtool_types.h
··· 54 54 #define UNWIND_HINT_TYPE_SAVE 6 55 55 #define UNWIND_HINT_TYPE_RESTORE 7 56 56 57 + /* 58 + * Annotate types 59 + */ 60 + #define ANNOTYPE_NOENDBR 1 61 + #define ANNOTYPE_RETPOLINE_SAFE 2 62 + #define ANNOTYPE_INSTR_BEGIN 3 63 + #define ANNOTYPE_INSTR_END 4 64 + #define ANNOTYPE_UNRET_BEGIN 5 65 + #define ANNOTYPE_IGNORE_ALTS 6 66 + #define ANNOTYPE_INTRA_FUNCTION_CALL 7 67 + #define ANNOTYPE_REACHABLE 8 68 + 57 69 #endif /* _LINUX_OBJTOOL_TYPES_H */
+2 -1
tools/objtool/arch/loongarch/special.c
··· 9 9 } 10 10 11 11 struct reloc *arch_find_switch_table(struct objtool_file *file, 12 - struct instruction *insn) 12 + struct instruction *insn, 13 + unsigned long *table_size) 13 14 { 14 15 return NULL; 15 16 }
+2 -1
tools/objtool/arch/powerpc/special.c
··· 13 13 } 14 14 15 15 struct reloc *arch_find_switch_table(struct objtool_file *file, 16 - struct instruction *insn) 16 + struct instruction *insn, 17 + unsigned long *table_size) 17 18 { 18 19 exit(-1); 19 20 }
+3 -1
tools/objtool/arch/x86/special.c
··· 109 109 * NOTE: MITIGATION_RETPOLINE made it harder still to decode dynamic jumps. 110 110 */ 111 111 struct reloc *arch_find_switch_table(struct objtool_file *file, 112 - struct instruction *insn) 112 + struct instruction *insn, 113 + unsigned long *table_size) 113 114 { 114 115 struct reloc *text_reloc, *rodata_reloc; 115 116 struct section *table_sec; ··· 159 158 if (reloc_type(text_reloc) == R_X86_64_PC32) 160 159 file->ignore_unreachables = true; 161 160 161 + *table_size = 0; 162 162 return rodata_reloc; 163 163 }
+129 -313
tools/objtool/check.c
··· 150 150 return NULL; 151 151 } 152 152 153 + static inline unsigned long insn_jump_table_size(struct instruction *insn) 154 + { 155 + if (insn->type == INSN_JUMP_DYNAMIC || 156 + insn->type == INSN_CALL_DYNAMIC) 157 + return insn->_jump_table_size; 158 + 159 + return 0; 160 + } 161 + 153 162 static bool is_jump_table_jump(struct instruction *insn) 154 163 { 155 164 struct alt_group *alt_group = insn->alt_group; ··· 619 610 620 611 for (idx = 0; (pv_ops = pv_ops_tables[idx]); idx++) 621 612 add_pv_ops(file, pv_ops); 622 - 623 - return 0; 624 - } 625 - 626 - static struct instruction *find_last_insn(struct objtool_file *file, 627 - struct section *sec) 628 - { 629 - struct instruction *insn = NULL; 630 - unsigned int offset; 631 - unsigned int end = (sec->sh.sh_size > 10) ? sec->sh.sh_size - 10 : 0; 632 - 633 - for (offset = sec->sh.sh_size - 1; offset >= end && !insn; offset--) 634 - insn = find_insn(file, sec, offset); 635 - 636 - return insn; 637 - } 638 - 639 - /* 640 - * Mark "ud2" instructions and manually annotated dead ends. 641 - */ 642 - static int add_dead_ends(struct objtool_file *file) 643 - { 644 - struct section *rsec; 645 - struct reloc *reloc; 646 - struct instruction *insn; 647 - uint64_t offset; 648 - 649 - /* 650 - * Check for manually annotated dead ends. 651 - */ 652 - rsec = find_section_by_name(file->elf, ".rela.discard.unreachable"); 653 - if (!rsec) 654 - goto reachable; 655 - 656 - for_each_reloc(rsec, reloc) { 657 - if (reloc->sym->type == STT_SECTION) { 658 - offset = reloc_addend(reloc); 659 - } else if (reloc->sym->local_label) { 660 - offset = reloc->sym->offset; 661 - } else { 662 - WARN("unexpected relocation symbol type in %s", rsec->name); 663 - return -1; 664 - } 665 - 666 - insn = find_insn(file, reloc->sym->sec, offset); 667 - if (insn) 668 - insn = prev_insn_same_sec(file, insn); 669 - else if (offset == reloc->sym->sec->sh.sh_size) { 670 - insn = find_last_insn(file, reloc->sym->sec); 671 - if (!insn) { 672 - WARN("can't find unreachable insn at %s+0x%" PRIx64, 673 - reloc->sym->sec->name, offset); 674 - return -1; 675 - } 676 - } else { 677 - WARN("can't find unreachable insn at %s+0x%" PRIx64, 678 - reloc->sym->sec->name, offset); 679 - return -1; 680 - } 681 - 682 - insn->dead_end = true; 683 - } 684 - 685 - reachable: 686 - /* 687 - * These manually annotated reachable checks are needed for GCC 4.4, 688 - * where the Linux unreachable() macro isn't supported. In that case 689 - * GCC doesn't know the "ud2" is fatal, so it generates code as if it's 690 - * not a dead end. 691 - */ 692 - rsec = find_section_by_name(file->elf, ".rela.discard.reachable"); 693 - if (!rsec) 694 - return 0; 695 - 696 - for_each_reloc(rsec, reloc) { 697 - if (reloc->sym->type == STT_SECTION) { 698 - offset = reloc_addend(reloc); 699 - } else if (reloc->sym->local_label) { 700 - offset = reloc->sym->offset; 701 - } else { 702 - WARN("unexpected relocation symbol type in %s", rsec->name); 703 - return -1; 704 - } 705 - 706 - insn = find_insn(file, reloc->sym->sec, offset); 707 - if (insn) 708 - insn = prev_insn_same_sec(file, insn); 709 - else if (offset == reloc->sym->sec->sh.sh_size) { 710 - insn = find_last_insn(file, reloc->sym->sec); 711 - if (!insn) { 712 - WARN("can't find reachable insn at %s+0x%" PRIx64, 713 - reloc->sym->sec->name, offset); 714 - return -1; 715 - } 716 - } else { 717 - WARN("can't find reachable insn at %s+0x%" PRIx64, 718 - reloc->sym->sec->name, offset); 719 - return -1; 720 - } 721 - 722 - insn->dead_end = false; 723 - } 724 613 725 614 return 0; 726 615 } ··· 1214 1307 1215 1308 func->uaccess_safe = true; 1216 1309 } 1217 - } 1218 - 1219 - /* 1220 - * FIXME: For now, just ignore any alternatives which add retpolines. This is 1221 - * a temporary hack, as it doesn't allow ORC to unwind from inside a retpoline. 1222 - * But it at least allows objtool to understand the control flow *around* the 1223 - * retpoline. 1224 - */ 1225 - static int add_ignore_alternatives(struct objtool_file *file) 1226 - { 1227 - struct section *rsec; 1228 - struct reloc *reloc; 1229 - struct instruction *insn; 1230 - 1231 - rsec = find_section_by_name(file->elf, ".rela.discard.ignore_alts"); 1232 - if (!rsec) 1233 - return 0; 1234 - 1235 - for_each_reloc(rsec, reloc) { 1236 - if (reloc->sym->type != STT_SECTION) { 1237 - WARN("unexpected relocation symbol type in %s", rsec->name); 1238 - return -1; 1239 - } 1240 - 1241 - insn = find_insn(file, reloc->sym->sec, reloc_addend(reloc)); 1242 - if (!insn) { 1243 - WARN("bad .discard.ignore_alts entry"); 1244 - return -1; 1245 - } 1246 - 1247 - insn->ignore_alts = true; 1248 - } 1249 - 1250 - return 0; 1251 1310 } 1252 1311 1253 1312 /* ··· 1946 2073 static int add_jump_table(struct objtool_file *file, struct instruction *insn, 1947 2074 struct reloc *next_table) 1948 2075 { 2076 + unsigned long table_size = insn_jump_table_size(insn); 1949 2077 struct symbol *pfunc = insn_func(insn)->pfunc; 1950 2078 struct reloc *table = insn_jump_table(insn); 1951 2079 struct instruction *dest_insn; ··· 1961 2087 for_each_reloc_from(table->sec, reloc) { 1962 2088 1963 2089 /* Check for the end of the table: */ 2090 + if (table_size && reloc_offset(reloc) - reloc_offset(table) >= table_size) 2091 + break; 1964 2092 if (reloc != table && reloc == next_table) 1965 2093 break; 1966 2094 ··· 2007 2131 * find_jump_table() - Given a dynamic jump, find the switch jump table 2008 2132 * associated with it. 2009 2133 */ 2010 - static struct reloc *find_jump_table(struct objtool_file *file, 2011 - struct symbol *func, 2012 - struct instruction *insn) 2134 + static void find_jump_table(struct objtool_file *file, struct symbol *func, 2135 + struct instruction *insn) 2013 2136 { 2014 2137 struct reloc *table_reloc; 2015 2138 struct instruction *dest_insn, *orig_insn = insn; 2139 + unsigned long table_size; 2016 2140 2017 2141 /* 2018 2142 * Backward search using the @first_jump_src links, these help avoid ··· 2033 2157 insn->jump_dest->offset > orig_insn->offset)) 2034 2158 break; 2035 2159 2036 - table_reloc = arch_find_switch_table(file, insn); 2160 + table_reloc = arch_find_switch_table(file, insn, &table_size); 2037 2161 if (!table_reloc) 2038 2162 continue; 2039 2163 dest_insn = find_insn(file, table_reloc->sym->sec, reloc_addend(table_reloc)); 2040 2164 if (!dest_insn || !insn_func(dest_insn) || insn_func(dest_insn)->pfunc != func) 2041 2165 continue; 2042 2166 2043 - return table_reloc; 2167 + orig_insn->_jump_table = table_reloc; 2168 + orig_insn->_jump_table_size = table_size; 2169 + break; 2044 2170 } 2045 - 2046 - return NULL; 2047 2171 } 2048 2172 2049 2173 /* ··· 2054 2178 struct symbol *func) 2055 2179 { 2056 2180 struct instruction *insn, *last = NULL; 2057 - struct reloc *reloc; 2058 2181 2059 2182 func_for_each_insn(file, func, insn) { 2060 2183 if (!last) ··· 2076 2201 if (insn->type != INSN_JUMP_DYNAMIC) 2077 2202 continue; 2078 2203 2079 - reloc = find_jump_table(file, func, insn); 2080 - if (reloc) 2081 - insn->_jump_table = reloc; 2204 + find_jump_table(file, func, insn); 2082 2205 } 2083 2206 } 2084 2207 ··· 2246 2373 return 0; 2247 2374 } 2248 2375 2249 - static int read_noendbr_hints(struct objtool_file *file) 2376 + static int read_annotate(struct objtool_file *file, 2377 + int (*func)(struct objtool_file *file, int type, struct instruction *insn)) 2250 2378 { 2379 + struct section *sec; 2251 2380 struct instruction *insn; 2252 - struct section *rsec; 2253 2381 struct reloc *reloc; 2382 + uint64_t offset; 2383 + int type, ret; 2254 2384 2255 - rsec = find_section_by_name(file->elf, ".rela.discard.noendbr"); 2256 - if (!rsec) 2385 + sec = find_section_by_name(file->elf, ".discard.annotate_insn"); 2386 + if (!sec) 2257 2387 return 0; 2258 2388 2259 - for_each_reloc(rsec, reloc) { 2260 - insn = find_insn(file, reloc->sym->sec, 2261 - reloc->sym->offset + reloc_addend(reloc)); 2389 + if (!sec->rsec) 2390 + return 0; 2391 + 2392 + if (sec->sh.sh_entsize != 8) { 2393 + static bool warned = false; 2394 + if (!warned) { 2395 + WARN("%s: dodgy linker, sh_entsize != 8", sec->name); 2396 + warned = true; 2397 + } 2398 + sec->sh.sh_entsize = 8; 2399 + } 2400 + 2401 + for_each_reloc(sec->rsec, reloc) { 2402 + type = *(u32 *)(sec->data->d_buf + (reloc_idx(reloc) * sec->sh.sh_entsize) + 4); 2403 + 2404 + offset = reloc->sym->offset + reloc_addend(reloc); 2405 + insn = find_insn(file, reloc->sym->sec, offset); 2406 + 2262 2407 if (!insn) { 2263 - WARN("bad .discard.noendbr entry"); 2408 + WARN("bad .discard.annotate_insn entry: %d of type %d", reloc_idx(reloc), type); 2264 2409 return -1; 2265 2410 } 2266 2411 2267 - insn->noendbr = 1; 2412 + ret = func(file, type, insn); 2413 + if (ret < 0) 2414 + return ret; 2268 2415 } 2269 2416 2270 2417 return 0; 2271 2418 } 2272 2419 2273 - static int read_retpoline_hints(struct objtool_file *file) 2420 + static int __annotate_early(struct objtool_file *file, int type, struct instruction *insn) 2274 2421 { 2275 - struct section *rsec; 2276 - struct instruction *insn; 2277 - struct reloc *reloc; 2422 + switch (type) { 2423 + case ANNOTYPE_IGNORE_ALTS: 2424 + insn->ignore_alts = true; 2425 + break; 2278 2426 2279 - rsec = find_section_by_name(file->elf, ".rela.discard.retpoline_safe"); 2280 - if (!rsec) 2427 + /* 2428 + * Must be before read_unwind_hints() since that needs insn->noendbr. 2429 + */ 2430 + case ANNOTYPE_NOENDBR: 2431 + insn->noendbr = 1; 2432 + break; 2433 + 2434 + default: 2435 + break; 2436 + } 2437 + 2438 + return 0; 2439 + } 2440 + 2441 + static int __annotate_ifc(struct objtool_file *file, int type, struct instruction *insn) 2442 + { 2443 + unsigned long dest_off; 2444 + 2445 + if (type != ANNOTYPE_INTRA_FUNCTION_CALL) 2281 2446 return 0; 2282 2447 2283 - for_each_reloc(rsec, reloc) { 2284 - if (reloc->sym->type != STT_SECTION) { 2285 - WARN("unexpected relocation symbol type in %s", rsec->name); 2286 - return -1; 2287 - } 2448 + if (insn->type != INSN_CALL) { 2449 + WARN_INSN(insn, "intra_function_call not a direct call"); 2450 + return -1; 2451 + } 2288 2452 2289 - insn = find_insn(file, reloc->sym->sec, reloc_addend(reloc)); 2290 - if (!insn) { 2291 - WARN("bad .discard.retpoline_safe entry"); 2292 - return -1; 2293 - } 2453 + /* 2454 + * Treat intra-function CALLs as JMPs, but with a stack_op. 2455 + * See add_call_destinations(), which strips stack_ops from 2456 + * normal CALLs. 2457 + */ 2458 + insn->type = INSN_JUMP_UNCONDITIONAL; 2294 2459 2460 + dest_off = arch_jump_destination(insn); 2461 + insn->jump_dest = find_insn(file, insn->sec, dest_off); 2462 + if (!insn->jump_dest) { 2463 + WARN_INSN(insn, "can't find call dest at %s+0x%lx", 2464 + insn->sec->name, dest_off); 2465 + return -1; 2466 + } 2467 + 2468 + return 0; 2469 + } 2470 + 2471 + static int __annotate_late(struct objtool_file *file, int type, struct instruction *insn) 2472 + { 2473 + switch (type) { 2474 + case ANNOTYPE_NOENDBR: 2475 + /* early */ 2476 + break; 2477 + 2478 + case ANNOTYPE_RETPOLINE_SAFE: 2295 2479 if (insn->type != INSN_JUMP_DYNAMIC && 2296 2480 insn->type != INSN_CALL_DYNAMIC && 2297 2481 insn->type != INSN_RETURN && ··· 2358 2428 } 2359 2429 2360 2430 insn->retpoline_safe = true; 2361 - } 2431 + break; 2362 2432 2363 - return 0; 2364 - } 2365 - 2366 - static int read_instr_hints(struct objtool_file *file) 2367 - { 2368 - struct section *rsec; 2369 - struct instruction *insn; 2370 - struct reloc *reloc; 2371 - 2372 - rsec = find_section_by_name(file->elf, ".rela.discard.instr_end"); 2373 - if (!rsec) 2374 - return 0; 2375 - 2376 - for_each_reloc(rsec, reloc) { 2377 - if (reloc->sym->type != STT_SECTION) { 2378 - WARN("unexpected relocation symbol type in %s", rsec->name); 2379 - return -1; 2380 - } 2381 - 2382 - insn = find_insn(file, reloc->sym->sec, reloc_addend(reloc)); 2383 - if (!insn) { 2384 - WARN("bad .discard.instr_end entry"); 2385 - return -1; 2386 - } 2387 - 2388 - insn->instr--; 2389 - } 2390 - 2391 - rsec = find_section_by_name(file->elf, ".rela.discard.instr_begin"); 2392 - if (!rsec) 2393 - return 0; 2394 - 2395 - for_each_reloc(rsec, reloc) { 2396 - if (reloc->sym->type != STT_SECTION) { 2397 - WARN("unexpected relocation symbol type in %s", rsec->name); 2398 - return -1; 2399 - } 2400 - 2401 - insn = find_insn(file, reloc->sym->sec, reloc_addend(reloc)); 2402 - if (!insn) { 2403 - WARN("bad .discard.instr_begin entry"); 2404 - return -1; 2405 - } 2406 - 2433 + case ANNOTYPE_INSTR_BEGIN: 2407 2434 insn->instr++; 2408 - } 2435 + break; 2409 2436 2410 - return 0; 2411 - } 2437 + case ANNOTYPE_INSTR_END: 2438 + insn->instr--; 2439 + break; 2412 2440 2413 - static int read_validate_unret_hints(struct objtool_file *file) 2414 - { 2415 - struct section *rsec; 2416 - struct instruction *insn; 2417 - struct reloc *reloc; 2418 - 2419 - rsec = find_section_by_name(file->elf, ".rela.discard.validate_unret"); 2420 - if (!rsec) 2421 - return 0; 2422 - 2423 - for_each_reloc(rsec, reloc) { 2424 - if (reloc->sym->type != STT_SECTION) { 2425 - WARN("unexpected relocation symbol type in %s", rsec->name); 2426 - return -1; 2427 - } 2428 - 2429 - insn = find_insn(file, reloc->sym->sec, reloc_addend(reloc)); 2430 - if (!insn) { 2431 - WARN("bad .discard.instr_end entry"); 2432 - return -1; 2433 - } 2441 + case ANNOTYPE_UNRET_BEGIN: 2434 2442 insn->unret = 1; 2435 - } 2443 + break; 2436 2444 2437 - return 0; 2438 - } 2445 + case ANNOTYPE_IGNORE_ALTS: 2446 + /* early */ 2447 + break; 2439 2448 2449 + case ANNOTYPE_INTRA_FUNCTION_CALL: 2450 + /* ifc */ 2451 + break; 2440 2452 2441 - static int read_intra_function_calls(struct objtool_file *file) 2442 - { 2443 - struct instruction *insn; 2444 - struct section *rsec; 2445 - struct reloc *reloc; 2453 + case ANNOTYPE_REACHABLE: 2454 + insn->dead_end = false; 2455 + break; 2446 2456 2447 - rsec = find_section_by_name(file->elf, ".rela.discard.intra_function_calls"); 2448 - if (!rsec) 2449 - return 0; 2450 - 2451 - for_each_reloc(rsec, reloc) { 2452 - unsigned long dest_off; 2453 - 2454 - if (reloc->sym->type != STT_SECTION) { 2455 - WARN("unexpected relocation symbol type in %s", 2456 - rsec->name); 2457 - return -1; 2458 - } 2459 - 2460 - insn = find_insn(file, reloc->sym->sec, reloc_addend(reloc)); 2461 - if (!insn) { 2462 - WARN("bad .discard.intra_function_call entry"); 2463 - return -1; 2464 - } 2465 - 2466 - if (insn->type != INSN_CALL) { 2467 - WARN_INSN(insn, "intra_function_call not a direct call"); 2468 - return -1; 2469 - } 2470 - 2471 - /* 2472 - * Treat intra-function CALLs as JMPs, but with a stack_op. 2473 - * See add_call_destinations(), which strips stack_ops from 2474 - * normal CALLs. 2475 - */ 2476 - insn->type = INSN_JUMP_UNCONDITIONAL; 2477 - 2478 - dest_off = arch_jump_destination(insn); 2479 - insn->jump_dest = find_insn(file, insn->sec, dest_off); 2480 - if (!insn->jump_dest) { 2481 - WARN_INSN(insn, "can't find call dest at %s+0x%lx", 2482 - insn->sec->name, dest_off); 2483 - return -1; 2484 - } 2457 + default: 2458 + WARN_INSN(insn, "Unknown annotation type: %d", type); 2459 + break; 2485 2460 } 2486 2461 2487 2462 return 0; ··· 2501 2666 add_ignores(file); 2502 2667 add_uaccess_safe(file); 2503 2668 2504 - ret = add_ignore_alternatives(file); 2505 - if (ret) 2506 - return ret; 2507 - 2508 - /* 2509 - * Must be before read_unwind_hints() since that needs insn->noendbr. 2510 - */ 2511 - ret = read_noendbr_hints(file); 2669 + ret = read_annotate(file, __annotate_early); 2512 2670 if (ret) 2513 2671 return ret; 2514 2672 ··· 2523 2695 * Must be before add_call_destination(); it changes INSN_CALL to 2524 2696 * INSN_JUMP. 2525 2697 */ 2526 - ret = read_intra_function_calls(file); 2698 + ret = read_annotate(file, __annotate_ifc); 2527 2699 if (ret) 2528 2700 return ret; 2529 2701 2530 2702 ret = add_call_destinations(file); 2531 - if (ret) 2532 - return ret; 2533 - 2534 - /* 2535 - * Must be after add_call_destinations() such that it can override 2536 - * dead_end_function() marks. 2537 - */ 2538 - ret = add_dead_ends(file); 2539 2703 if (ret) 2540 2704 return ret; 2541 2705 ··· 2539 2719 if (ret) 2540 2720 return ret; 2541 2721 2542 - ret = read_retpoline_hints(file); 2543 - if (ret) 2544 - return ret; 2545 - 2546 - ret = read_instr_hints(file); 2547 - if (ret) 2548 - return ret; 2549 - 2550 - ret = read_validate_unret_hints(file); 2722 + /* 2723 + * Must be after add_call_destinations() such that it can override 2724 + * dead_end_function() marks. 2725 + */ 2726 + ret = read_annotate(file, __annotate_late); 2551 2727 if (ret) 2552 2728 return ret; 2553 2729
+4 -1
tools/objtool/include/objtool/check.h
··· 71 71 struct instruction *first_jump_src; 72 72 union { 73 73 struct symbol *_call_dest; 74 - struct reloc *_jump_table; 74 + struct { 75 + struct reloc *_jump_table; 76 + unsigned long _jump_table_size; 77 + }; 75 78 }; 76 79 struct alternative *alts; 77 80 struct symbol *sym;
+2 -1
tools/objtool/include/objtool/special.h
··· 38 38 struct instruction *insn, 39 39 struct reloc *reloc); 40 40 struct reloc *arch_find_switch_table(struct objtool_file *file, 41 - struct instruction *insn); 41 + struct instruction *insn, 42 + unsigned long *table_size); 42 43 #endif /* _SPECIAL_H */