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

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

Pull objtool updates from Thomas Gleixner:

- Make objtool work for big-endian cross compiles

- Make stack tracking via stack pointer memory operations match
push/pop semantics to prepare for architectures w/o PUSH/POP
instructions.

- Add support for analyzing alternatives

- Improve retpoline detection and handling

- Improve assembly code coverage on x86

- Provide support for inlined stack switching

* tag 'objtool-core-2021-02-23' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: (33 commits)
objtool: Support stack-swizzle
objtool,x86: Additionally decode: mov %rsp, (%reg)
x86/unwind/orc: Change REG_SP_INDIRECT
x86/power: Support objtool validation in hibernate_asm_64.S
x86/power: Move restore_registers() to top of the file
x86/power: Annotate indirect branches as safe
x86/acpi: Support objtool validation in wakeup_64.S
x86/acpi: Annotate indirect branch as safe
x86/ftrace: Support objtool vmlinux.o validation in ftrace_64.S
x86/xen/pvh: Annotate indirect branch as safe
x86/xen: Support objtool vmlinux.o validation in xen-head.S
x86/xen: Support objtool validation in xen-asm.S
objtool: Add xen_start_kernel() to noreturn list
objtool: Combine UNWIND_HINT_RET_OFFSET and UNWIND_HINT_FUNC
objtool: Add asm version of STACK_FRAME_NON_STANDARD
objtool: Assume only ELF functions do sibling calls
x86/ftrace: Add UNWIND_HINT_FUNC annotation for ftrace_stub
objtool: Support retpoline jump detection for vmlinux.o
objtool: Fix ".cold" section suffix check for newer versions of GCC
objtool: Fix retpoline detection in asm code
...

+997 -632
+45
arch/x86/include/asm/insn.h
··· 7 7 * Copyright (C) IBM Corporation, 2009 8 8 */ 9 9 10 + #include <asm/byteorder.h> 10 11 /* insn_attr_t is defined in inat.h */ 11 12 #include <asm/inat.h> 13 + 14 + #if defined(__BYTE_ORDER) ? __BYTE_ORDER == __LITTLE_ENDIAN : defined(__LITTLE_ENDIAN) 12 15 13 16 struct insn_field { 14 17 union { ··· 22 19 unsigned char got; 23 20 unsigned char nbytes; 24 21 }; 22 + 23 + static inline void insn_field_set(struct insn_field *p, insn_value_t v, 24 + unsigned char n) 25 + { 26 + p->value = v; 27 + p->nbytes = n; 28 + } 29 + 30 + static inline void insn_set_byte(struct insn_field *p, unsigned char n, 31 + insn_byte_t v) 32 + { 33 + p->bytes[n] = v; 34 + } 35 + 36 + #else 37 + 38 + struct insn_field { 39 + insn_value_t value; 40 + union { 41 + insn_value_t little; 42 + insn_byte_t bytes[4]; 43 + }; 44 + /* !0 if we've run insn_get_xxx() for this field */ 45 + unsigned char got; 46 + unsigned char nbytes; 47 + }; 48 + 49 + static inline void insn_field_set(struct insn_field *p, insn_value_t v, 50 + unsigned char n) 51 + { 52 + p->value = v; 53 + p->little = __cpu_to_le32(v); 54 + p->nbytes = n; 55 + } 56 + 57 + static inline void insn_set_byte(struct insn_field *p, unsigned char n, 58 + insn_byte_t v) 59 + { 60 + p->bytes[n] = v; 61 + p->value = __le32_to_cpu(p->little); 62 + } 63 + #endif 25 64 26 65 struct insn { 27 66 struct insn_field prefixes; /*
+10
arch/x86/include/asm/orc_types.h
··· 40 40 #define ORC_REG_MAX 15 41 41 42 42 #ifndef __ASSEMBLY__ 43 + #include <asm/byteorder.h> 44 + 43 45 /* 44 46 * This struct is more or less a vastly simplified version of the DWARF Call 45 47 * Frame Information standard. It contains only the necessary parts of DWARF ··· 53 51 struct orc_entry { 54 52 s16 sp_offset; 55 53 s16 bp_offset; 54 + #if defined(__LITTLE_ENDIAN_BITFIELD) 56 55 unsigned sp_reg:4; 57 56 unsigned bp_reg:4; 58 57 unsigned type:2; 59 58 unsigned end:1; 59 + #elif defined(__BIG_ENDIAN_BITFIELD) 60 + unsigned bp_reg:4; 61 + unsigned sp_reg:4; 62 + unsigned unused:5; 63 + unsigned end:1; 64 + unsigned type:2; 65 + #endif 60 66 } __packed; 61 67 62 68 #endif /* __ASSEMBLY__ */
+2 -11
arch/x86/include/asm/unwind_hints.h
··· 48 48 UNWIND_HINT_REGS base=\base offset=\offset partial=1 49 49 .endm 50 50 51 - .macro UNWIND_HINT_FUNC sp_offset=8 52 - UNWIND_HINT sp_reg=ORC_REG_SP sp_offset=\sp_offset type=UNWIND_HINT_TYPE_CALL 53 - .endm 54 - 55 - /* 56 - * RET_OFFSET: Used on instructions that terminate a function; mostly RETURN 57 - * and sibling calls. On these, sp_offset denotes the expected offset from 58 - * initial_func_cfi. 59 - */ 60 - .macro UNWIND_HINT_RET_OFFSET sp_offset=8 61 - UNWIND_HINT sp_reg=ORC_REG_SP type=UNWIND_HINT_TYPE_RET_OFFSET sp_offset=\sp_offset 51 + .macro UNWIND_HINT_FUNC 52 + UNWIND_HINT sp_reg=ORC_REG_SP sp_offset=8 type=UNWIND_HINT_TYPE_FUNC 62 53 .endm 63 54 64 55 #endif /* __ASSEMBLY__ */
-1
arch/x86/kernel/acpi/Makefile
··· 1 1 # SPDX-License-Identifier: GPL-2.0 2 - OBJECT_FILES_NON_STANDARD_wakeup_$(BITS).o := y 3 2 4 3 obj-$(CONFIG_ACPI) += boot.o 5 4 obj-$(CONFIG_ACPI_SLEEP) += sleep.o wakeup_$(BITS).o
+4
arch/x86/kernel/acpi/wakeup_64.S
··· 1 1 /* SPDX-License-Identifier: GPL-2.0-only */ 2 2 .text 3 3 #include <linux/linkage.h> 4 + #include <linux/objtool.h> 4 5 #include <asm/segment.h> 5 6 #include <asm/pgtable_types.h> 6 7 #include <asm/page_types.h> 7 8 #include <asm/msr.h> 8 9 #include <asm/asm-offsets.h> 9 10 #include <asm/frame.h> 11 + #include <asm/nospec-branch.h> 10 12 11 13 # Copyright 2003 Pavel Machek <pavel@suse.cz 12 14 ··· 41 39 movq saved_rbp, %rbp 42 40 43 41 movq saved_rip, %rax 42 + ANNOTATE_RETPOLINE_SAFE 44 43 jmp *%rax 45 44 SYM_FUNC_END(wakeup_long64) 46 45 ··· 129 126 FRAME_END 130 127 jmp restore_processor_state 131 128 SYM_FUNC_END(do_suspend_lowlevel) 129 + STACK_FRAME_NON_STANDARD do_suspend_lowlevel 132 130 133 131 .data 134 132 saved_rbp: .quad 0
+4 -4
arch/x86/kernel/ftrace_64.S
··· 184 184 * It is also used to copy the retq for trampolines. 185 185 */ 186 186 SYM_INNER_LABEL_ALIGN(ftrace_stub, SYM_L_WEAK) 187 + UNWIND_HINT_FUNC 187 188 retq 188 189 SYM_FUNC_END(ftrace_epilogue) 189 190 ··· 277 276 restore_mcount_regs 8 278 277 /* Restore flags */ 279 278 popfq 280 - UNWIND_HINT_RET_OFFSET 279 + UNWIND_HINT_FUNC 281 280 jmp ftrace_epilogue 282 281 283 282 SYM_FUNC_END(ftrace_regs_caller) ··· 334 333 retq 335 334 SYM_FUNC_END(ftrace_graph_caller) 336 335 337 - SYM_CODE_START(return_to_handler) 338 - UNWIND_HINT_EMPTY 336 + SYM_FUNC_START(return_to_handler) 339 337 subq $24, %rsp 340 338 341 339 /* Save the return values */ ··· 349 349 movq (%rsp), %rax 350 350 addq $24, %rsp 351 351 JMP_NOSPEC rdi 352 - SYM_CODE_END(return_to_handler) 352 + SYM_FUNC_END(return_to_handler) 353 353 #endif
+4 -1
arch/x86/kernel/unwind_orc.c
··· 471 471 break; 472 472 473 473 case ORC_REG_SP_INDIRECT: 474 - sp = state->sp + orc->sp_offset; 474 + sp = state->sp; 475 475 indirect = true; 476 476 break; 477 477 ··· 521 521 if (indirect) { 522 522 if (!deref_stack_reg(state, sp, &sp)) 523 523 goto err; 524 + 525 + if (orc->sp_reg == ORC_REG_SP_INDIRECT) 526 + sp += orc->sp_offset; 524 527 } 525 528 526 529 /* Find IP, SP and possibly regs: */
+56 -63
arch/x86/lib/insn.c
··· 5 5 * Copyright (C) IBM Corporation, 2002, 2004, 2009 6 6 */ 7 7 8 + #include <linux/kernel.h> 8 9 #ifdef __KERNEL__ 9 10 #include <linux/string.h> 10 11 #else ··· 16 15 17 16 #include <asm/emulate_prefix.h> 18 17 18 + #define leXX_to_cpu(t, r) \ 19 + ({ \ 20 + __typeof__(t) v; \ 21 + switch (sizeof(t)) { \ 22 + case 4: v = le32_to_cpu(r); break; \ 23 + case 2: v = le16_to_cpu(r); break; \ 24 + case 1: v = r; break; \ 25 + default: \ 26 + BUILD_BUG(); break; \ 27 + } \ 28 + v; \ 29 + }) 30 + 19 31 /* Verify next sizeof(t) bytes can be on the same instruction */ 20 32 #define validate_next(t, insn, n) \ 21 33 ((insn)->next_byte + sizeof(t) + n <= (insn)->end_kaddr) 22 34 23 35 #define __get_next(t, insn) \ 24 - ({ t r = *(t*)insn->next_byte; insn->next_byte += sizeof(t); r; }) 36 + ({ t r = *(t*)insn->next_byte; insn->next_byte += sizeof(t); leXX_to_cpu(t, r); }) 25 37 26 38 #define __peek_nbyte_next(t, insn, n) \ 27 - ({ t r = *(t*)((insn)->next_byte + n); r; }) 39 + ({ t r = *(t*)((insn)->next_byte + n); leXX_to_cpu(t, r); }) 28 40 29 41 #define get_next(t, insn) \ 30 42 ({ if (unlikely(!validate_next(t, insn, 0))) goto err_out; __get_next(t, insn); }) ··· 161 147 b = insn->prefixes.bytes[3]; 162 148 for (i = 0; i < nb; i++) 163 149 if (prefixes->bytes[i] == lb) 164 - prefixes->bytes[i] = b; 150 + insn_set_byte(prefixes, i, b); 165 151 } 166 - insn->prefixes.bytes[3] = lb; 152 + insn_set_byte(&insn->prefixes, 3, lb); 167 153 } 168 154 169 155 /* Decode REX prefix */ ··· 171 157 b = peek_next(insn_byte_t, insn); 172 158 attr = inat_get_opcode_attribute(b); 173 159 if (inat_is_rex_prefix(attr)) { 174 - insn->rex_prefix.value = b; 175 - insn->rex_prefix.nbytes = 1; 160 + insn_field_set(&insn->rex_prefix, b, 1); 176 161 insn->next_byte++; 177 162 if (X86_REX_W(b)) 178 163 /* REX.W overrides opnd_size */ ··· 194 181 if (X86_MODRM_MOD(b2) != 3) 195 182 goto vex_end; 196 183 } 197 - insn->vex_prefix.bytes[0] = b; 198 - insn->vex_prefix.bytes[1] = b2; 184 + insn_set_byte(&insn->vex_prefix, 0, b); 185 + insn_set_byte(&insn->vex_prefix, 1, b2); 199 186 if (inat_is_evex_prefix(attr)) { 200 187 b2 = peek_nbyte_next(insn_byte_t, insn, 2); 201 - insn->vex_prefix.bytes[2] = b2; 188 + insn_set_byte(&insn->vex_prefix, 2, b2); 202 189 b2 = peek_nbyte_next(insn_byte_t, insn, 3); 203 - insn->vex_prefix.bytes[3] = b2; 190 + insn_set_byte(&insn->vex_prefix, 3, b2); 204 191 insn->vex_prefix.nbytes = 4; 205 192 insn->next_byte += 4; 206 193 if (insn->x86_64 && X86_VEX_W(b2)) ··· 208 195 insn->opnd_bytes = 8; 209 196 } else if (inat_is_vex3_prefix(attr)) { 210 197 b2 = peek_nbyte_next(insn_byte_t, insn, 2); 211 - insn->vex_prefix.bytes[2] = b2; 198 + insn_set_byte(&insn->vex_prefix, 2, b2); 212 199 insn->vex_prefix.nbytes = 3; 213 200 insn->next_byte += 3; 214 201 if (insn->x86_64 && X86_VEX_W(b2)) ··· 220 207 * Makes it easier to decode vex.W, vex.vvvv, 221 208 * vex.L and vex.pp. Masking with 0x7f sets vex.W == 0. 222 209 */ 223 - insn->vex_prefix.bytes[2] = b2 & 0x7f; 210 + insn_set_byte(&insn->vex_prefix, 2, b2 & 0x7f); 224 211 insn->vex_prefix.nbytes = 2; 225 212 insn->next_byte += 2; 226 213 } ··· 256 243 257 244 /* Get first opcode */ 258 245 op = get_next(insn_byte_t, insn); 259 - opcode->bytes[0] = op; 246 + insn_set_byte(opcode, 0, op); 260 247 opcode->nbytes = 1; 261 248 262 249 /* Check if there is VEX prefix or not */ ··· 308 295 309 296 if (inat_has_modrm(insn->attr)) { 310 297 mod = get_next(insn_byte_t, insn); 311 - modrm->value = mod; 312 - modrm->nbytes = 1; 298 + insn_field_set(modrm, mod, 1); 313 299 if (inat_is_group(insn->attr)) { 314 300 pfx_id = insn_last_prefix_id(insn); 315 301 insn->attr = inat_get_group_attribute(mod, pfx_id, ··· 346 334 * For rip-relative instructions, the mod field (top 2 bits) 347 335 * is zero and the r/m field (bottom 3 bits) is 0x5. 348 336 */ 349 - return (modrm->nbytes && (modrm->value & 0xc7) == 0x5); 337 + return (modrm->nbytes && (modrm->bytes[0] & 0xc7) == 0x5); 350 338 } 351 339 352 340 /** ··· 365 353 if (!insn->modrm.got) 366 354 insn_get_modrm(insn); 367 355 if (insn->modrm.nbytes) { 368 - modrm = (insn_byte_t)insn->modrm.value; 356 + modrm = insn->modrm.bytes[0]; 369 357 if (insn->addr_bytes != 2 && 370 358 X86_MODRM_MOD(modrm) != 3 && X86_MODRM_RM(modrm) == 4) { 371 - insn->sib.value = get_next(insn_byte_t, insn); 372 - insn->sib.nbytes = 1; 359 + insn_field_set(&insn->sib, 360 + get_next(insn_byte_t, insn), 1); 373 361 } 374 362 } 375 363 insn->sib.got = 1; ··· 419 407 if (mod == 3) 420 408 goto out; 421 409 if (mod == 1) { 422 - insn->displacement.value = get_next(signed char, insn); 423 - insn->displacement.nbytes = 1; 410 + insn_field_set(&insn->displacement, 411 + get_next(signed char, insn), 1); 424 412 } else if (insn->addr_bytes == 2) { 425 413 if ((mod == 0 && rm == 6) || mod == 2) { 426 - insn->displacement.value = 427 - get_next(short, insn); 428 - insn->displacement.nbytes = 2; 414 + insn_field_set(&insn->displacement, 415 + get_next(short, insn), 2); 429 416 } 430 417 } else { 431 418 if ((mod == 0 && rm == 5) || mod == 2 || 432 419 (mod == 0 && base == 5)) { 433 - insn->displacement.value = get_next(int, insn); 434 - insn->displacement.nbytes = 4; 420 + insn_field_set(&insn->displacement, 421 + get_next(int, insn), 4); 435 422 } 436 423 } 437 424 } ··· 446 435 { 447 436 switch (insn->addr_bytes) { 448 437 case 2: 449 - insn->moffset1.value = get_next(short, insn); 450 - insn->moffset1.nbytes = 2; 438 + insn_field_set(&insn->moffset1, get_next(short, insn), 2); 451 439 break; 452 440 case 4: 453 - insn->moffset1.value = get_next(int, insn); 454 - insn->moffset1.nbytes = 4; 441 + insn_field_set(&insn->moffset1, get_next(int, insn), 4); 455 442 break; 456 443 case 8: 457 - insn->moffset1.value = get_next(int, insn); 458 - insn->moffset1.nbytes = 4; 459 - insn->moffset2.value = get_next(int, insn); 460 - insn->moffset2.nbytes = 4; 444 + insn_field_set(&insn->moffset1, get_next(int, insn), 4); 445 + insn_field_set(&insn->moffset2, get_next(int, insn), 4); 461 446 break; 462 447 default: /* opnd_bytes must be modified manually */ 463 448 goto err_out; ··· 471 464 { 472 465 switch (insn->opnd_bytes) { 473 466 case 2: 474 - insn->immediate.value = get_next(short, insn); 475 - insn->immediate.nbytes = 2; 467 + insn_field_set(&insn->immediate, get_next(short, insn), 2); 476 468 break; 477 469 case 4: 478 470 case 8: 479 - insn->immediate.value = get_next(int, insn); 480 - insn->immediate.nbytes = 4; 471 + insn_field_set(&insn->immediate, get_next(int, insn), 4); 481 472 break; 482 473 default: /* opnd_bytes must be modified manually */ 483 474 goto err_out; ··· 492 487 { 493 488 switch (insn->opnd_bytes) { 494 489 case 2: 495 - insn->immediate1.value = get_next(short, insn); 496 - insn->immediate1.nbytes = 2; 490 + insn_field_set(&insn->immediate1, get_next(short, insn), 2); 497 491 break; 498 492 case 4: 499 - insn->immediate1.value = get_next(int, insn); 493 + insn_field_set(&insn->immediate1, get_next(int, insn), 4); 500 494 insn->immediate1.nbytes = 4; 501 495 break; 502 496 case 8: 503 - insn->immediate1.value = get_next(int, insn); 504 - insn->immediate1.nbytes = 4; 505 - insn->immediate2.value = get_next(int, insn); 506 - insn->immediate2.nbytes = 4; 497 + insn_field_set(&insn->immediate1, get_next(int, insn), 4); 498 + insn_field_set(&insn->immediate2, get_next(int, insn), 4); 507 499 break; 508 500 default: /* opnd_bytes must be modified manually */ 509 501 goto err_out; ··· 517 515 { 518 516 switch (insn->opnd_bytes) { 519 517 case 2: 520 - insn->immediate1.value = get_next(short, insn); 521 - insn->immediate1.nbytes = 2; 518 + insn_field_set(&insn->immediate1, get_next(short, insn), 2); 522 519 break; 523 520 case 4: 524 - insn->immediate1.value = get_next(int, insn); 525 - insn->immediate1.nbytes = 4; 521 + insn_field_set(&insn->immediate1, get_next(int, insn), 4); 526 522 break; 527 523 case 8: 528 524 /* ptr16:64 is not exist (no segment) */ ··· 528 528 default: /* opnd_bytes must be modified manually */ 529 529 goto err_out; 530 530 } 531 - insn->immediate2.value = get_next(unsigned short, insn); 532 - insn->immediate2.nbytes = 2; 531 + insn_field_set(&insn->immediate2, get_next(unsigned short, insn), 2); 533 532 insn->immediate1.got = insn->immediate2.got = 1; 534 533 535 534 return 1; ··· 564 565 565 566 switch (inat_immediate_size(insn->attr)) { 566 567 case INAT_IMM_BYTE: 567 - insn->immediate.value = get_next(signed char, insn); 568 - insn->immediate.nbytes = 1; 568 + insn_field_set(&insn->immediate, get_next(signed char, insn), 1); 569 569 break; 570 570 case INAT_IMM_WORD: 571 - insn->immediate.value = get_next(short, insn); 572 - insn->immediate.nbytes = 2; 571 + insn_field_set(&insn->immediate, get_next(short, insn), 2); 573 572 break; 574 573 case INAT_IMM_DWORD: 575 - insn->immediate.value = get_next(int, insn); 576 - insn->immediate.nbytes = 4; 574 + insn_field_set(&insn->immediate, get_next(int, insn), 4); 577 575 break; 578 576 case INAT_IMM_QWORD: 579 - insn->immediate1.value = get_next(int, insn); 580 - insn->immediate1.nbytes = 4; 581 - insn->immediate2.value = get_next(int, insn); 582 - insn->immediate2.nbytes = 4; 577 + insn_field_set(&insn->immediate1, get_next(int, insn), 4); 578 + insn_field_set(&insn->immediate2, get_next(int, insn), 4); 583 579 break; 584 580 case INAT_IMM_PTR: 585 581 if (!__get_immptr(insn)) ··· 593 599 goto err_out; 594 600 } 595 601 if (inat_has_second_immediate(insn->attr)) { 596 - insn->immediate2.value = get_next(signed char, insn); 597 - insn->immediate2.nbytes = 1; 602 + insn_field_set(&insn->immediate2, get_next(signed char, insn), 1); 598 603 } 599 604 done: 600 605 insn->immediate.got = 1;
+1 -1
arch/x86/lib/retpoline.S
··· 28 28 jmp .Lspec_trap_\@ 29 29 .Ldo_rop_\@: 30 30 mov %\reg, (%_ASM_SP) 31 - UNWIND_HINT_RET_OFFSET 31 + UNWIND_HINT_FUNC 32 32 ret 33 33 SYM_FUNC_END(__x86_retpoline_\reg) 34 34
+2
arch/x86/platform/pvh/head.S
··· 16 16 #include <asm/boot.h> 17 17 #include <asm/processor-flags.h> 18 18 #include <asm/msr.h> 19 + #include <asm/nospec-branch.h> 19 20 #include <xen/interface/elfnote.h> 20 21 21 22 __HEAD ··· 106 105 /* startup_64 expects boot_params in %rsi. */ 107 106 mov $_pa(pvh_bootparams), %rsi 108 107 mov $_pa(startup_64), %rax 108 + ANNOTATE_RETPOLINE_SAFE 109 109 jmp *%rax 110 110 111 111 #else /* CONFIG_X86_64 */
-1
arch/x86/power/Makefile
··· 1 1 # SPDX-License-Identifier: GPL-2.0 2 - OBJECT_FILES_NON_STANDARD_hibernate_asm_$(BITS).o := y 3 2 4 3 # __restore_processor_state() restores %gs after S3 resume and so should not 5 4 # itself be stack-protected
+81 -78
arch/x86/power/hibernate_asm_64.S
··· 21 21 #include <asm/asm-offsets.h> 22 22 #include <asm/processor-flags.h> 23 23 #include <asm/frame.h> 24 - 25 - SYM_FUNC_START(swsusp_arch_suspend) 26 - movq $saved_context, %rax 27 - movq %rsp, pt_regs_sp(%rax) 28 - movq %rbp, pt_regs_bp(%rax) 29 - movq %rsi, pt_regs_si(%rax) 30 - movq %rdi, pt_regs_di(%rax) 31 - movq %rbx, pt_regs_bx(%rax) 32 - movq %rcx, pt_regs_cx(%rax) 33 - movq %rdx, pt_regs_dx(%rax) 34 - movq %r8, pt_regs_r8(%rax) 35 - movq %r9, pt_regs_r9(%rax) 36 - movq %r10, pt_regs_r10(%rax) 37 - movq %r11, pt_regs_r11(%rax) 38 - movq %r12, pt_regs_r12(%rax) 39 - movq %r13, pt_regs_r13(%rax) 40 - movq %r14, pt_regs_r14(%rax) 41 - movq %r15, pt_regs_r15(%rax) 42 - pushfq 43 - popq pt_regs_flags(%rax) 44 - 45 - /* save cr3 */ 46 - movq %cr3, %rax 47 - movq %rax, restore_cr3(%rip) 48 - 49 - FRAME_BEGIN 50 - call swsusp_save 51 - FRAME_END 52 - ret 53 - SYM_FUNC_END(swsusp_arch_suspend) 54 - 55 - SYM_CODE_START(restore_image) 56 - /* prepare to jump to the image kernel */ 57 - movq restore_jump_address(%rip), %r8 58 - movq restore_cr3(%rip), %r9 59 - 60 - /* prepare to switch to temporary page tables */ 61 - movq temp_pgt(%rip), %rax 62 - movq mmu_cr4_features(%rip), %rbx 63 - 64 - /* prepare to copy image data to their original locations */ 65 - movq restore_pblist(%rip), %rdx 66 - 67 - /* jump to relocated restore code */ 68 - movq relocated_restore_code(%rip), %rcx 69 - jmpq *%rcx 70 - SYM_CODE_END(restore_image) 71 - 72 - /* code below has been relocated to a safe page */ 73 - SYM_CODE_START(core_restore_code) 74 - /* switch to temporary page tables */ 75 - movq %rax, %cr3 76 - /* flush TLB */ 77 - movq %rbx, %rcx 78 - andq $~(X86_CR4_PGE), %rcx 79 - movq %rcx, %cr4; # turn off PGE 80 - movq %cr3, %rcx; # flush TLB 81 - movq %rcx, %cr3; 82 - movq %rbx, %cr4; # turn PGE back on 83 - .Lloop: 84 - testq %rdx, %rdx 85 - jz .Ldone 86 - 87 - /* get addresses from the pbe and copy the page */ 88 - movq pbe_address(%rdx), %rsi 89 - movq pbe_orig_address(%rdx), %rdi 90 - movq $(PAGE_SIZE >> 3), %rcx 91 - rep 92 - movsq 93 - 94 - /* progress to the next pbe */ 95 - movq pbe_next(%rdx), %rdx 96 - jmp .Lloop 97 - 98 - .Ldone: 99 - /* jump to the restore_registers address from the image header */ 100 - jmpq *%r8 101 - SYM_CODE_END(core_restore_code) 24 + #include <asm/nospec-branch.h> 102 25 103 26 /* code below belongs to the image kernel */ 104 27 .align PAGE_SIZE ··· 68 145 69 146 ret 70 147 SYM_FUNC_END(restore_registers) 148 + 149 + SYM_FUNC_START(swsusp_arch_suspend) 150 + movq $saved_context, %rax 151 + movq %rsp, pt_regs_sp(%rax) 152 + movq %rbp, pt_regs_bp(%rax) 153 + movq %rsi, pt_regs_si(%rax) 154 + movq %rdi, pt_regs_di(%rax) 155 + movq %rbx, pt_regs_bx(%rax) 156 + movq %rcx, pt_regs_cx(%rax) 157 + movq %rdx, pt_regs_dx(%rax) 158 + movq %r8, pt_regs_r8(%rax) 159 + movq %r9, pt_regs_r9(%rax) 160 + movq %r10, pt_regs_r10(%rax) 161 + movq %r11, pt_regs_r11(%rax) 162 + movq %r12, pt_regs_r12(%rax) 163 + movq %r13, pt_regs_r13(%rax) 164 + movq %r14, pt_regs_r14(%rax) 165 + movq %r15, pt_regs_r15(%rax) 166 + pushfq 167 + popq pt_regs_flags(%rax) 168 + 169 + /* save cr3 */ 170 + movq %cr3, %rax 171 + movq %rax, restore_cr3(%rip) 172 + 173 + FRAME_BEGIN 174 + call swsusp_save 175 + FRAME_END 176 + ret 177 + SYM_FUNC_END(swsusp_arch_suspend) 178 + 179 + SYM_FUNC_START(restore_image) 180 + /* prepare to jump to the image kernel */ 181 + movq restore_jump_address(%rip), %r8 182 + movq restore_cr3(%rip), %r9 183 + 184 + /* prepare to switch to temporary page tables */ 185 + movq temp_pgt(%rip), %rax 186 + movq mmu_cr4_features(%rip), %rbx 187 + 188 + /* prepare to copy image data to their original locations */ 189 + movq restore_pblist(%rip), %rdx 190 + 191 + /* jump to relocated restore code */ 192 + movq relocated_restore_code(%rip), %rcx 193 + ANNOTATE_RETPOLINE_SAFE 194 + jmpq *%rcx 195 + SYM_FUNC_END(restore_image) 196 + 197 + /* code below has been relocated to a safe page */ 198 + SYM_FUNC_START(core_restore_code) 199 + /* switch to temporary page tables */ 200 + movq %rax, %cr3 201 + /* flush TLB */ 202 + movq %rbx, %rcx 203 + andq $~(X86_CR4_PGE), %rcx 204 + movq %rcx, %cr4; # turn off PGE 205 + movq %cr3, %rcx; # flush TLB 206 + movq %rcx, %cr3; 207 + movq %rbx, %cr4; # turn PGE back on 208 + .Lloop: 209 + testq %rdx, %rdx 210 + jz .Ldone 211 + 212 + /* get addresses from the pbe and copy the page */ 213 + movq pbe_address(%rdx), %rsi 214 + movq pbe_orig_address(%rdx), %rdi 215 + movq $(PAGE_SIZE >> 3), %rcx 216 + rep 217 + movsq 218 + 219 + /* progress to the next pbe */ 220 + movq pbe_next(%rdx), %rdx 221 + jmp .Lloop 222 + 223 + .Ldone: 224 + /* jump to the restore_registers address from the image header */ 225 + ANNOTATE_RETPOLINE_SAFE 226 + jmpq *%r8 227 + SYM_FUNC_END(core_restore_code)
+4 -4
arch/x86/tools/Makefile
··· 29 29 hostprogs += insn_decoder_test insn_sanity 30 30 31 31 # -I needed for generated C source and C source which in the kernel tree. 32 - HOSTCFLAGS_insn_decoder_test.o := -Wall -I$(objtree)/arch/x86/lib/ -I$(srctree)/arch/x86/include/uapi/ -I$(srctree)/arch/x86/include/ -I$(srctree)/arch/x86/lib/ -I$(srctree)/include/uapi/ 32 + HOSTCFLAGS_insn_decoder_test.o := -Wall -I$(srctree)/tools/arch/x86/lib/ -I$(srctree)/tools/arch/x86/include/ -I$(objtree)/arch/x86/lib/ 33 33 34 - HOSTCFLAGS_insn_sanity.o := -Wall -I$(objtree)/arch/x86/lib/ -I$(srctree)/arch/x86/include/ -I$(srctree)/arch/x86/lib/ -I$(srctree)/include/ 34 + HOSTCFLAGS_insn_sanity.o := -Wall -I$(srctree)/tools/arch/x86/lib/ -I$(srctree)/tools/arch/x86/include/ -I$(objtree)/arch/x86/lib/ 35 35 36 36 # Dependencies are also needed. 37 - $(obj)/insn_decoder_test.o: $(srctree)/arch/x86/lib/insn.c $(srctree)/arch/x86/lib/inat.c $(srctree)/arch/x86/include/asm/inat_types.h $(srctree)/arch/x86/include/asm/inat.h $(srctree)/arch/x86/include/asm/insn.h $(objtree)/arch/x86/lib/inat-tables.c 37 + $(obj)/insn_decoder_test.o: $(srctree)/tools/arch/x86/lib/insn.c $(srctree)/tools/arch/x86/lib/inat.c $(srctree)/tools/arch/x86/include/asm/inat_types.h $(srctree)/tools/arch/x86/include/asm/inat.h $(srctree)/tools/arch/x86/include/asm/insn.h $(objtree)/arch/x86/lib/inat-tables.c 38 38 39 - $(obj)/insn_sanity.o: $(srctree)/arch/x86/lib/insn.c $(srctree)/arch/x86/lib/inat.c $(srctree)/arch/x86/include/asm/inat_types.h $(srctree)/arch/x86/include/asm/inat.h $(srctree)/arch/x86/include/asm/insn.h $(objtree)/arch/x86/lib/inat-tables.c 39 + $(obj)/insn_sanity.o: $(srctree)/tools/arch/x86/lib/insn.c $(srctree)/tools/arch/x86/lib/inat.c $(srctree)/tools/arch/x86/include/asm/inat_types.h $(srctree)/tools/arch/x86/include/asm/inat.h $(srctree)/tools/arch/x86/include/asm/insn.h $(objtree)/arch/x86/lib/inat-tables.c 40 40 41 41 HOST_EXTRACFLAGS += -I$(srctree)/tools/include 42 42 hostprogs += relocs
-4
arch/x86/tools/insn_sanity.c
··· 14 14 #include <sys/types.h> 15 15 #include <sys/stat.h> 16 16 #include <fcntl.h> 17 - 18 - #define unlikely(cond) (cond) 19 - #define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0])) 20 - 21 17 #include <asm/insn.h> 22 18 #include <inat.c> 23 19 #include <insn.c>
-1
arch/x86/xen/Makefile
··· 1 1 # SPDX-License-Identifier: GPL-2.0 2 - OBJECT_FILES_NON_STANDARD_xen-asm.o := y 3 2 4 3 ifdef CONFIG_FUNCTION_TRACER 5 4 # Do not profile debug and lowlevel utilities
+18 -10
arch/x86/xen/xen-asm.S
··· 14 14 #include <asm/thread_info.h> 15 15 #include <asm/asm.h> 16 16 #include <asm/frame.h> 17 + #include <asm/unwind_hints.h> 17 18 18 19 #include <xen/interface/xen.h> 19 20 ··· 119 118 120 119 .macro xen_pv_trap name 121 120 SYM_CODE_START(xen_\name) 121 + UNWIND_HINT_EMPTY 122 122 pop %rcx 123 123 pop %r11 124 124 jmp \name ··· 159 157 SYM_CODE_START(xen_early_idt_handler_array) 160 158 i = 0 161 159 .rept NUM_EXCEPTION_VECTORS 160 + UNWIND_HINT_EMPTY 162 161 pop %rcx 163 162 pop %r11 164 163 jmp early_idt_handler_array + i*EARLY_IDT_HANDLER_SIZE ··· 186 183 * rsp->rax } 187 184 */ 188 185 SYM_CODE_START(xen_iret) 186 + UNWIND_HINT_EMPTY 189 187 pushq $0 190 188 jmp hypercall_iret 191 189 SYM_CODE_END(xen_iret) ··· 207 203 */ 208 204 209 205 /* Normal 64-bit system call target */ 210 - SYM_FUNC_START(xen_syscall_target) 206 + SYM_CODE_START(xen_syscall_target) 207 + UNWIND_HINT_EMPTY 211 208 popq %rcx 212 209 popq %r11 213 210 ··· 221 216 movq $__USER_CS, 1*8(%rsp) 222 217 223 218 jmp entry_SYSCALL_64_after_hwframe 224 - SYM_FUNC_END(xen_syscall_target) 219 + SYM_CODE_END(xen_syscall_target) 225 220 226 221 #ifdef CONFIG_IA32_EMULATION 227 222 228 223 /* 32-bit compat syscall target */ 229 - SYM_FUNC_START(xen_syscall32_target) 224 + SYM_CODE_START(xen_syscall32_target) 225 + UNWIND_HINT_EMPTY 230 226 popq %rcx 231 227 popq %r11 232 228 ··· 240 234 movq $__USER32_CS, 1*8(%rsp) 241 235 242 236 jmp entry_SYSCALL_compat_after_hwframe 243 - SYM_FUNC_END(xen_syscall32_target) 237 + SYM_CODE_END(xen_syscall32_target) 244 238 245 239 /* 32-bit compat sysenter target */ 246 - SYM_FUNC_START(xen_sysenter_target) 240 + SYM_CODE_START(xen_sysenter_target) 241 + UNWIND_HINT_EMPTY 247 242 /* 248 243 * NB: Xen is polite and clears TF from EFLAGS for us. This means 249 244 * that we don't need to guard against single step exceptions here. ··· 261 254 movq $__USER32_CS, 1*8(%rsp) 262 255 263 256 jmp entry_SYSENTER_compat_after_hwframe 264 - SYM_FUNC_END(xen_sysenter_target) 257 + SYM_CODE_END(xen_sysenter_target) 265 258 266 259 #else /* !CONFIG_IA32_EMULATION */ 267 260 268 - SYM_FUNC_START_ALIAS(xen_syscall32_target) 269 - SYM_FUNC_START(xen_sysenter_target) 261 + SYM_CODE_START(xen_syscall32_target) 262 + SYM_CODE_START(xen_sysenter_target) 263 + UNWIND_HINT_EMPTY 270 264 lea 16(%rsp), %rsp /* strip %rcx, %r11 */ 271 265 mov $-ENOSYS, %rax 272 266 pushq $0 273 267 jmp hypercall_iret 274 - SYM_FUNC_END(xen_sysenter_target) 275 - SYM_FUNC_END_ALIAS(xen_syscall32_target) 268 + SYM_CODE_END(xen_sysenter_target) 269 + SYM_CODE_END(xen_syscall32_target) 276 270 277 271 #endif /* CONFIG_IA32_EMULATION */
+3 -2
arch/x86/xen/xen-head.S
··· 68 68 .balign PAGE_SIZE 69 69 SYM_CODE_START(hypercall_page) 70 70 .rept (PAGE_SIZE / 32) 71 - UNWIND_HINT_EMPTY 72 - .skip 32 71 + UNWIND_HINT_FUNC 72 + .skip 31, 0x90 73 + ret 73 74 .endr 74 75 75 76 #define HYPERCALL(n) \
+12 -1
include/linux/objtool.h
··· 29 29 * 30 30 * UNWIND_HINT_TYPE_REGS_PARTIAL: Used in entry code to indicate that 31 31 * sp_reg+sp_offset points to the iret return frame. 32 + * 33 + * UNWIND_HINT_FUNC: Generate the unwind metadata of a callable function. 34 + * Useful for code which doesn't have an ELF function annotation. 32 35 */ 33 36 #define UNWIND_HINT_TYPE_CALL 0 34 37 #define UNWIND_HINT_TYPE_REGS 1 35 38 #define UNWIND_HINT_TYPE_REGS_PARTIAL 2 36 - #define UNWIND_HINT_TYPE_RET_OFFSET 3 39 + #define UNWIND_HINT_TYPE_FUNC 3 37 40 38 41 #ifdef CONFIG_STACK_VALIDATION 39 42 ··· 112 109 .popsection 113 110 .endm 114 111 112 + .macro STACK_FRAME_NON_STANDARD func:req 113 + .pushsection .discard.func_stack_frame_non_standard, "aw" 114 + .long \func - . 115 + .popsection 116 + .endm 117 + 115 118 #endif /* __ASSEMBLY__ */ 116 119 117 120 #else /* !CONFIG_STACK_VALIDATION */ ··· 130 121 #else 131 122 #define ANNOTATE_INTRA_FUNCTION_CALL 132 123 .macro UNWIND_HINT sp_reg:req sp_offset=0 type:req end=0 124 + .endm 125 + .macro STACK_FRAME_NON_STANDARD func:req 133 126 .endm 134 127 #endif 135 128
+45
tools/arch/x86/include/asm/insn.h
··· 7 7 * Copyright (C) IBM Corporation, 2009 8 8 */ 9 9 10 + #include <asm/byteorder.h> 10 11 /* insn_attr_t is defined in inat.h */ 11 12 #include "inat.h" 13 + 14 + #if defined(__BYTE_ORDER) ? __BYTE_ORDER == __LITTLE_ENDIAN : defined(__LITTLE_ENDIAN) 12 15 13 16 struct insn_field { 14 17 union { ··· 22 19 unsigned char got; 23 20 unsigned char nbytes; 24 21 }; 22 + 23 + static inline void insn_field_set(struct insn_field *p, insn_value_t v, 24 + unsigned char n) 25 + { 26 + p->value = v; 27 + p->nbytes = n; 28 + } 29 + 30 + static inline void insn_set_byte(struct insn_field *p, unsigned char n, 31 + insn_byte_t v) 32 + { 33 + p->bytes[n] = v; 34 + } 35 + 36 + #else 37 + 38 + struct insn_field { 39 + insn_value_t value; 40 + union { 41 + insn_value_t little; 42 + insn_byte_t bytes[4]; 43 + }; 44 + /* !0 if we've run insn_get_xxx() for this field */ 45 + unsigned char got; 46 + unsigned char nbytes; 47 + }; 48 + 49 + static inline void insn_field_set(struct insn_field *p, insn_value_t v, 50 + unsigned char n) 51 + { 52 + p->value = v; 53 + p->little = __cpu_to_le32(v); 54 + p->nbytes = n; 55 + } 56 + 57 + static inline void insn_set_byte(struct insn_field *p, unsigned char n, 58 + insn_byte_t v) 59 + { 60 + p->bytes[n] = v; 61 + p->value = __le32_to_cpu(p->little); 62 + } 63 + #endif 25 64 26 65 struct insn { 27 66 struct insn_field prefixes; /*
+10
tools/arch/x86/include/asm/orc_types.h
··· 40 40 #define ORC_REG_MAX 15 41 41 42 42 #ifndef __ASSEMBLY__ 43 + #include <asm/byteorder.h> 44 + 43 45 /* 44 46 * This struct is more or less a vastly simplified version of the DWARF Call 45 47 * Frame Information standard. It contains only the necessary parts of DWARF ··· 53 51 struct orc_entry { 54 52 s16 sp_offset; 55 53 s16 bp_offset; 54 + #if defined(__LITTLE_ENDIAN_BITFIELD) 56 55 unsigned sp_reg:4; 57 56 unsigned bp_reg:4; 58 57 unsigned type:2; 59 58 unsigned end:1; 59 + #elif defined(__BIG_ENDIAN_BITFIELD) 60 + unsigned bp_reg:4; 61 + unsigned sp_reg:4; 62 + unsigned unused:5; 63 + unsigned end:1; 64 + unsigned type:2; 65 + #endif 60 66 } __packed; 61 67 62 68 #endif /* __ASSEMBLY__ */
+56 -63
tools/arch/x86/lib/insn.c
··· 5 5 * Copyright (C) IBM Corporation, 2002, 2004, 2009 6 6 */ 7 7 8 + #include <linux/kernel.h> 8 9 #ifdef __KERNEL__ 9 10 #include <linux/string.h> 10 11 #else ··· 16 15 17 16 #include "../include/asm/emulate_prefix.h" 18 17 18 + #define leXX_to_cpu(t, r) \ 19 + ({ \ 20 + __typeof__(t) v; \ 21 + switch (sizeof(t)) { \ 22 + case 4: v = le32_to_cpu(r); break; \ 23 + case 2: v = le16_to_cpu(r); break; \ 24 + case 1: v = r; break; \ 25 + default: \ 26 + BUILD_BUG(); break; \ 27 + } \ 28 + v; \ 29 + }) 30 + 19 31 /* Verify next sizeof(t) bytes can be on the same instruction */ 20 32 #define validate_next(t, insn, n) \ 21 33 ((insn)->next_byte + sizeof(t) + n <= (insn)->end_kaddr) 22 34 23 35 #define __get_next(t, insn) \ 24 - ({ t r = *(t*)insn->next_byte; insn->next_byte += sizeof(t); r; }) 36 + ({ t r = *(t*)insn->next_byte; insn->next_byte += sizeof(t); leXX_to_cpu(t, r); }) 25 37 26 38 #define __peek_nbyte_next(t, insn, n) \ 27 - ({ t r = *(t*)((insn)->next_byte + n); r; }) 39 + ({ t r = *(t*)((insn)->next_byte + n); leXX_to_cpu(t, r); }) 28 40 29 41 #define get_next(t, insn) \ 30 42 ({ if (unlikely(!validate_next(t, insn, 0))) goto err_out; __get_next(t, insn); }) ··· 161 147 b = insn->prefixes.bytes[3]; 162 148 for (i = 0; i < nb; i++) 163 149 if (prefixes->bytes[i] == lb) 164 - prefixes->bytes[i] = b; 150 + insn_set_byte(prefixes, i, b); 165 151 } 166 - insn->prefixes.bytes[3] = lb; 152 + insn_set_byte(&insn->prefixes, 3, lb); 167 153 } 168 154 169 155 /* Decode REX prefix */ ··· 171 157 b = peek_next(insn_byte_t, insn); 172 158 attr = inat_get_opcode_attribute(b); 173 159 if (inat_is_rex_prefix(attr)) { 174 - insn->rex_prefix.value = b; 175 - insn->rex_prefix.nbytes = 1; 160 + insn_field_set(&insn->rex_prefix, b, 1); 176 161 insn->next_byte++; 177 162 if (X86_REX_W(b)) 178 163 /* REX.W overrides opnd_size */ ··· 194 181 if (X86_MODRM_MOD(b2) != 3) 195 182 goto vex_end; 196 183 } 197 - insn->vex_prefix.bytes[0] = b; 198 - insn->vex_prefix.bytes[1] = b2; 184 + insn_set_byte(&insn->vex_prefix, 0, b); 185 + insn_set_byte(&insn->vex_prefix, 1, b2); 199 186 if (inat_is_evex_prefix(attr)) { 200 187 b2 = peek_nbyte_next(insn_byte_t, insn, 2); 201 - insn->vex_prefix.bytes[2] = b2; 188 + insn_set_byte(&insn->vex_prefix, 2, b2); 202 189 b2 = peek_nbyte_next(insn_byte_t, insn, 3); 203 - insn->vex_prefix.bytes[3] = b2; 190 + insn_set_byte(&insn->vex_prefix, 3, b2); 204 191 insn->vex_prefix.nbytes = 4; 205 192 insn->next_byte += 4; 206 193 if (insn->x86_64 && X86_VEX_W(b2)) ··· 208 195 insn->opnd_bytes = 8; 209 196 } else if (inat_is_vex3_prefix(attr)) { 210 197 b2 = peek_nbyte_next(insn_byte_t, insn, 2); 211 - insn->vex_prefix.bytes[2] = b2; 198 + insn_set_byte(&insn->vex_prefix, 2, b2); 212 199 insn->vex_prefix.nbytes = 3; 213 200 insn->next_byte += 3; 214 201 if (insn->x86_64 && X86_VEX_W(b2)) ··· 220 207 * Makes it easier to decode vex.W, vex.vvvv, 221 208 * vex.L and vex.pp. Masking with 0x7f sets vex.W == 0. 222 209 */ 223 - insn->vex_prefix.bytes[2] = b2 & 0x7f; 210 + insn_set_byte(&insn->vex_prefix, 2, b2 & 0x7f); 224 211 insn->vex_prefix.nbytes = 2; 225 212 insn->next_byte += 2; 226 213 } ··· 256 243 257 244 /* Get first opcode */ 258 245 op = get_next(insn_byte_t, insn); 259 - opcode->bytes[0] = op; 246 + insn_set_byte(opcode, 0, op); 260 247 opcode->nbytes = 1; 261 248 262 249 /* Check if there is VEX prefix or not */ ··· 308 295 309 296 if (inat_has_modrm(insn->attr)) { 310 297 mod = get_next(insn_byte_t, insn); 311 - modrm->value = mod; 312 - modrm->nbytes = 1; 298 + insn_field_set(modrm, mod, 1); 313 299 if (inat_is_group(insn->attr)) { 314 300 pfx_id = insn_last_prefix_id(insn); 315 301 insn->attr = inat_get_group_attribute(mod, pfx_id, ··· 346 334 * For rip-relative instructions, the mod field (top 2 bits) 347 335 * is zero and the r/m field (bottom 3 bits) is 0x5. 348 336 */ 349 - return (modrm->nbytes && (modrm->value & 0xc7) == 0x5); 337 + return (modrm->nbytes && (modrm->bytes[0] & 0xc7) == 0x5); 350 338 } 351 339 352 340 /** ··· 365 353 if (!insn->modrm.got) 366 354 insn_get_modrm(insn); 367 355 if (insn->modrm.nbytes) { 368 - modrm = (insn_byte_t)insn->modrm.value; 356 + modrm = insn->modrm.bytes[0]; 369 357 if (insn->addr_bytes != 2 && 370 358 X86_MODRM_MOD(modrm) != 3 && X86_MODRM_RM(modrm) == 4) { 371 - insn->sib.value = get_next(insn_byte_t, insn); 372 - insn->sib.nbytes = 1; 359 + insn_field_set(&insn->sib, 360 + get_next(insn_byte_t, insn), 1); 373 361 } 374 362 } 375 363 insn->sib.got = 1; ··· 419 407 if (mod == 3) 420 408 goto out; 421 409 if (mod == 1) { 422 - insn->displacement.value = get_next(signed char, insn); 423 - insn->displacement.nbytes = 1; 410 + insn_field_set(&insn->displacement, 411 + get_next(signed char, insn), 1); 424 412 } else if (insn->addr_bytes == 2) { 425 413 if ((mod == 0 && rm == 6) || mod == 2) { 426 - insn->displacement.value = 427 - get_next(short, insn); 428 - insn->displacement.nbytes = 2; 414 + insn_field_set(&insn->displacement, 415 + get_next(short, insn), 2); 429 416 } 430 417 } else { 431 418 if ((mod == 0 && rm == 5) || mod == 2 || 432 419 (mod == 0 && base == 5)) { 433 - insn->displacement.value = get_next(int, insn); 434 - insn->displacement.nbytes = 4; 420 + insn_field_set(&insn->displacement, 421 + get_next(int, insn), 4); 435 422 } 436 423 } 437 424 } ··· 446 435 { 447 436 switch (insn->addr_bytes) { 448 437 case 2: 449 - insn->moffset1.value = get_next(short, insn); 450 - insn->moffset1.nbytes = 2; 438 + insn_field_set(&insn->moffset1, get_next(short, insn), 2); 451 439 break; 452 440 case 4: 453 - insn->moffset1.value = get_next(int, insn); 454 - insn->moffset1.nbytes = 4; 441 + insn_field_set(&insn->moffset1, get_next(int, insn), 4); 455 442 break; 456 443 case 8: 457 - insn->moffset1.value = get_next(int, insn); 458 - insn->moffset1.nbytes = 4; 459 - insn->moffset2.value = get_next(int, insn); 460 - insn->moffset2.nbytes = 4; 444 + insn_field_set(&insn->moffset1, get_next(int, insn), 4); 445 + insn_field_set(&insn->moffset2, get_next(int, insn), 4); 461 446 break; 462 447 default: /* opnd_bytes must be modified manually */ 463 448 goto err_out; ··· 471 464 { 472 465 switch (insn->opnd_bytes) { 473 466 case 2: 474 - insn->immediate.value = get_next(short, insn); 475 - insn->immediate.nbytes = 2; 467 + insn_field_set(&insn->immediate, get_next(short, insn), 2); 476 468 break; 477 469 case 4: 478 470 case 8: 479 - insn->immediate.value = get_next(int, insn); 480 - insn->immediate.nbytes = 4; 471 + insn_field_set(&insn->immediate, get_next(int, insn), 4); 481 472 break; 482 473 default: /* opnd_bytes must be modified manually */ 483 474 goto err_out; ··· 492 487 { 493 488 switch (insn->opnd_bytes) { 494 489 case 2: 495 - insn->immediate1.value = get_next(short, insn); 496 - insn->immediate1.nbytes = 2; 490 + insn_field_set(&insn->immediate1, get_next(short, insn), 2); 497 491 break; 498 492 case 4: 499 - insn->immediate1.value = get_next(int, insn); 493 + insn_field_set(&insn->immediate1, get_next(int, insn), 4); 500 494 insn->immediate1.nbytes = 4; 501 495 break; 502 496 case 8: 503 - insn->immediate1.value = get_next(int, insn); 504 - insn->immediate1.nbytes = 4; 505 - insn->immediate2.value = get_next(int, insn); 506 - insn->immediate2.nbytes = 4; 497 + insn_field_set(&insn->immediate1, get_next(int, insn), 4); 498 + insn_field_set(&insn->immediate2, get_next(int, insn), 4); 507 499 break; 508 500 default: /* opnd_bytes must be modified manually */ 509 501 goto err_out; ··· 517 515 { 518 516 switch (insn->opnd_bytes) { 519 517 case 2: 520 - insn->immediate1.value = get_next(short, insn); 521 - insn->immediate1.nbytes = 2; 518 + insn_field_set(&insn->immediate1, get_next(short, insn), 2); 522 519 break; 523 520 case 4: 524 - insn->immediate1.value = get_next(int, insn); 525 - insn->immediate1.nbytes = 4; 521 + insn_field_set(&insn->immediate1, get_next(int, insn), 4); 526 522 break; 527 523 case 8: 528 524 /* ptr16:64 is not exist (no segment) */ ··· 528 528 default: /* opnd_bytes must be modified manually */ 529 529 goto err_out; 530 530 } 531 - insn->immediate2.value = get_next(unsigned short, insn); 532 - insn->immediate2.nbytes = 2; 531 + insn_field_set(&insn->immediate2, get_next(unsigned short, insn), 2); 533 532 insn->immediate1.got = insn->immediate2.got = 1; 534 533 535 534 return 1; ··· 564 565 565 566 switch (inat_immediate_size(insn->attr)) { 566 567 case INAT_IMM_BYTE: 567 - insn->immediate.value = get_next(signed char, insn); 568 - insn->immediate.nbytes = 1; 568 + insn_field_set(&insn->immediate, get_next(signed char, insn), 1); 569 569 break; 570 570 case INAT_IMM_WORD: 571 - insn->immediate.value = get_next(short, insn); 572 - insn->immediate.nbytes = 2; 571 + insn_field_set(&insn->immediate, get_next(short, insn), 2); 573 572 break; 574 573 case INAT_IMM_DWORD: 575 - insn->immediate.value = get_next(int, insn); 576 - insn->immediate.nbytes = 4; 574 + insn_field_set(&insn->immediate, get_next(int, insn), 4); 577 575 break; 578 576 case INAT_IMM_QWORD: 579 - insn->immediate1.value = get_next(int, insn); 580 - insn->immediate1.nbytes = 4; 581 - insn->immediate2.value = get_next(int, insn); 582 - insn->immediate2.nbytes = 4; 577 + insn_field_set(&insn->immediate1, get_next(int, insn), 4); 578 + insn_field_set(&insn->immediate2, get_next(int, insn), 4); 583 579 break; 584 580 case INAT_IMM_PTR: 585 581 if (!__get_immptr(insn)) ··· 593 599 goto err_out; 594 600 } 595 601 if (inat_has_second_immediate(insn->attr)) { 596 - insn->immediate2.value = get_next(signed char, insn); 597 - insn->immediate2.nbytes = 1; 602 + insn_field_set(&insn->immediate2, get_next(signed char, insn), 1); 598 603 } 599 604 done: 600 605 insn->immediate.got = 1;
+12 -1
tools/include/linux/objtool.h
··· 29 29 * 30 30 * UNWIND_HINT_TYPE_REGS_PARTIAL: Used in entry code to indicate that 31 31 * sp_reg+sp_offset points to the iret return frame. 32 + * 33 + * UNWIND_HINT_FUNC: Generate the unwind metadata of a callable function. 34 + * Useful for code which doesn't have an ELF function annotation. 32 35 */ 33 36 #define UNWIND_HINT_TYPE_CALL 0 34 37 #define UNWIND_HINT_TYPE_REGS 1 35 38 #define UNWIND_HINT_TYPE_REGS_PARTIAL 2 36 - #define UNWIND_HINT_TYPE_RET_OFFSET 3 39 + #define UNWIND_HINT_TYPE_FUNC 3 37 40 38 41 #ifdef CONFIG_STACK_VALIDATION 39 42 ··· 112 109 .popsection 113 110 .endm 114 111 112 + .macro STACK_FRAME_NON_STANDARD func:req 113 + .pushsection .discard.func_stack_frame_non_standard, "aw" 114 + .long \func - . 115 + .popsection 116 + .endm 117 + 115 118 #endif /* __ASSEMBLY__ */ 116 119 117 120 #else /* !CONFIG_STACK_VALIDATION */ ··· 130 121 #else 131 122 #define ANNOTATE_INTRA_FUNCTION_CALL 132 123 .macro UNWIND_HINT sp_reg:req sp_offset=0 type:req end=0 124 + .endm 125 + .macro STACK_FRAME_NON_STANDARD func:req 133 126 .endm 134 127 #endif 135 128
+1 -1
tools/objtool/.gitignore
··· 1 1 # SPDX-License-Identifier: GPL-2.0-only 2 2 arch/x86/lib/inat-tables.c 3 - objtool 3 + /objtool 4 4 fixdep
+8 -6
tools/objtool/Documentation/stack-validation.txt
··· 315 315 function tracing inserts additional calls, which is not obvious from the 316 316 sources). 317 317 318 - 10. file.o: warning: func()+0x5c: alternative modifies stack 318 + 10. file.o: warning: func()+0x5c: stack layout conflict in alternatives 319 319 320 - This means that an alternative includes instructions that modify the 321 - stack. The problem is that there is only one ORC unwind table, this means 322 - that the ORC unwind entries must be valid for each of the alternatives. 323 - The easiest way to enforce this is to ensure alternatives do not contain 324 - any ORC entries, which in turn implies the above constraint. 320 + This means that in the use of the alternative() or ALTERNATIVE() 321 + macro, the code paths have conflicting modifications to the stack. 322 + The problem is that there is only one ORC unwind table, which means 323 + that the ORC unwind entries must be consistent for all possible 324 + instruction boundaries regardless of which code has been patched. 325 + This limitation can be overcome by massaging the alternatives with 326 + NOPs to shift the stack changes around so they no longer conflict. 325 327 326 328 11. file.o: warning: unannotated intra-function call 327 329
+1 -4
tools/objtool/Makefile
··· 27 27 INCLUDES := -I$(srctree)/tools/include \ 28 28 -I$(srctree)/tools/arch/$(HOSTARCH)/include/uapi \ 29 29 -I$(srctree)/tools/arch/$(SRCARCH)/include \ 30 + -I$(srctree)/tools/objtool/include \ 30 31 -I$(srctree)/tools/objtool/arch/$(SRCARCH)/include 31 32 WARNINGS := $(EXTRA_WARNINGS) -Wno-switch-default -Wno-switch-enum -Wno-packed -Wno-nested-externs 32 33 CFLAGS := -Werror $(WARNINGS) $(KBUILD_HOSTCFLAGS) -g $(INCLUDES) $(LIBELF_FLAGS) ··· 45 44 ifeq ($(SRCARCH),x86) 46 45 SUBCMD_CHECK := y 47 46 SUBCMD_ORC := y 48 - endif 49 - 50 - ifeq ($(SUBCMD_ORC),y) 51 - CFLAGS += -DINSN_USE_ORC 52 47 endif 53 48 54 49 export SUBCMD_CHECK SUBCMD_ORC
+2 -6
tools/objtool/arch.h tools/objtool/include/objtool/arch.h
··· 8 8 9 9 #include <stdbool.h> 10 10 #include <linux/list.h> 11 - #include "objtool.h" 12 - #include "cfi.h" 13 - 14 - #ifdef INSN_USE_ORC 15 - #include <asm/orc_types.h> 16 - #endif 11 + #include <objtool/objtool.h> 12 + #include <objtool/cfi.h> 17 13 18 14 enum insn_type { 19 15 INSN_JUMP_CONDITIONAL,
+40 -14
tools/objtool/arch/x86/decode.c
··· 11 11 #include "../../../arch/x86/lib/inat.c" 12 12 #include "../../../arch/x86/lib/insn.c" 13 13 14 - #include "../../check.h" 15 - #include "../../elf.h" 16 - #include "../../arch.h" 17 - #include "../../warn.h" 18 14 #include <asm/orc_types.h> 15 + #include <objtool/check.h> 16 + #include <objtool/elf.h> 17 + #include <objtool/arch.h> 18 + #include <objtool/warn.h> 19 19 20 20 static unsigned char op_to_cfi_reg[][2] = { 21 21 {CFI_AX, CFI_R8}, ··· 222 222 break; 223 223 224 224 case 0x89: 225 - if (rex_w && !rex_r && modrm_mod == 3 && modrm_reg == 4) { 225 + if (rex_w && !rex_r && modrm_reg == 4) { 226 226 227 - /* mov %rsp, reg */ 228 - ADD_OP(op) { 229 - op->src.type = OP_SRC_REG; 230 - op->src.reg = CFI_SP; 231 - op->dest.type = OP_DEST_REG; 232 - op->dest.reg = op_to_cfi_reg[modrm_rm][rex_b]; 227 + if (modrm_mod == 3) { 228 + /* mov %rsp, reg */ 229 + ADD_OP(op) { 230 + op->src.type = OP_SRC_REG; 231 + op->src.reg = CFI_SP; 232 + op->dest.type = OP_DEST_REG; 233 + op->dest.reg = op_to_cfi_reg[modrm_rm][rex_b]; 234 + } 235 + break; 236 + 237 + } else { 238 + /* skip nontrivial SIB */ 239 + if (modrm_rm == 4 && !(sib == 0x24 && rex_b == rex_x)) 240 + break; 241 + 242 + /* skip RIP relative displacement */ 243 + if (modrm_rm == 5 && modrm_mod == 0) 244 + break; 245 + 246 + /* mov %rsp, disp(%reg) */ 247 + ADD_OP(op) { 248 + op->src.type = OP_SRC_REG; 249 + op->src.reg = CFI_SP; 250 + op->dest.type = OP_DEST_REG_INDIRECT; 251 + op->dest.reg = op_to_cfi_reg[modrm_rm][rex_b]; 252 + op->dest.offset = insn.displacement.value; 253 + } 254 + break; 233 255 } 256 + 234 257 break; 235 258 } 236 259 ··· 282 259 op->dest.reg = CFI_BP; 283 260 op->dest.offset = insn.displacement.value; 284 261 } 262 + break; 263 + } 285 264 286 - } else if (rex_w && !rex_b && modrm_rm == 4 && sib == 0x24) { 265 + if (rex_w && !rex_b && modrm_rm == 4 && sib == 0x24) { 287 266 288 267 /* mov reg, disp(%rsp) */ 289 268 ADD_OP(op) { ··· 295 270 op->dest.reg = CFI_SP; 296 271 op->dest.offset = insn.displacement.value; 297 272 } 273 + break; 298 274 } 299 275 300 276 break; ··· 589 563 state->cfa.offset = 8; 590 564 591 565 /* initial RA (return address) */ 592 - state->regs[16].base = CFI_CFA; 593 - state->regs[16].offset = -8; 566 + state->regs[CFI_RA].base = CFI_CFA; 567 + state->regs[CFI_RA].offset = -8; 594 568 } 595 569 596 570 const char *arch_nop_insn(int len)
+9
tools/objtool/arch/x86/include/arch/endianness.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0-or-later */ 2 + #ifndef _ARCH_ENDIANNESS_H 3 + #define _ARCH_ENDIANNESS_H 4 + 5 + #include <endian.h> 6 + 7 + #define __TARGET_BYTE_ORDER __LITTLE_ENDIAN 8 + 9 + #endif /* _ARCH_ENDIANNESS_H */
tools/objtool/arch/x86/include/arch_elf.h tools/objtool/arch/x86/include/arch/elf.h
tools/objtool/arch/x86/include/arch_special.h tools/objtool/arch/x86/include/arch/special.h
tools/objtool/arch/x86/include/cfi_regs.h tools/objtool/arch/x86/include/arch/cfi_regs.h
+3 -3
tools/objtool/arch/x86/special.c
··· 1 1 // SPDX-License-Identifier: GPL-2.0-or-later 2 2 #include <string.h> 3 3 4 - #include "../../special.h" 5 - #include "../../builtin.h" 4 + #include <objtool/special.h> 5 + #include <objtool/builtin.h> 6 6 7 7 #define X86_FEATURE_POPCNT (4 * 32 + 23) 8 8 #define X86_FEATURE_SMAP (9 * 32 + 20) ··· 48 48 * replacement group. 49 49 */ 50 50 return insn->offset == special_alt->new_off && 51 - (insn->type == INSN_CALL || is_static_jump(insn)); 51 + (insn->type == INSN_CALL || is_jump(insn)); 52 52 } 53 53 54 54 /*
+2 -2
tools/objtool/builtin-check.c
··· 15 15 16 16 #include <subcmd/parse-options.h> 17 17 #include <string.h> 18 - #include "builtin.h" 19 - #include "objtool.h" 18 + #include <objtool/builtin.h> 19 + #include <objtool/objtool.h> 20 20 21 21 bool no_fp, no_unreachable, retpoline, module, backtrace, uaccess, stats, validate_dup, vmlinux; 22 22
+3 -7
tools/objtool/builtin-orc.c
··· 13 13 */ 14 14 15 15 #include <string.h> 16 - #include "builtin.h" 17 - #include "objtool.h" 16 + #include <objtool/builtin.h> 17 + #include <objtool/objtool.h> 18 18 19 19 static const char *orc_usage[] = { 20 20 "objtool orc generate [<options>] file.o", ··· 51 51 if (list_empty(&file->insn_list)) 52 52 return 0; 53 53 54 - ret = create_orc(file); 55 - if (ret) 56 - return ret; 57 - 58 - ret = create_orc_sections(file); 54 + ret = orc_create(file); 59 55 if (ret) 60 56 return ret; 61 57
tools/objtool/builtin.h tools/objtool/include/objtool/builtin.h
+1 -1
tools/objtool/cfi.h tools/objtool/include/objtool/cfi.h
··· 6 6 #ifndef _OBJTOOL_CFI_H 7 7 #define _OBJTOOL_CFI_H 8 8 9 - #include "cfi_regs.h" 9 + #include <arch/cfi_regs.h> 10 10 11 11 #define CFI_UNDEFINED -1 12 12 #define CFI_CFA -2
+255 -147
tools/objtool/check.c
··· 6 6 #include <string.h> 7 7 #include <stdlib.h> 8 8 9 - #include "builtin.h" 10 - #include "cfi.h" 11 - #include "arch.h" 12 - #include "check.h" 13 - #include "special.h" 14 - #include "warn.h" 15 - #include "arch_elf.h" 9 + #include <arch/elf.h> 10 + #include <objtool/builtin.h> 11 + #include <objtool/cfi.h> 12 + #include <objtool/arch.h> 13 + #include <objtool/check.h> 14 + #include <objtool/special.h> 15 + #include <objtool/warn.h> 16 + #include <objtool/endianness.h> 16 17 17 18 #include <linux/objtool.h> 18 19 #include <linux/hashtable.h> 19 20 #include <linux/kernel.h> 20 21 #include <linux/static_call_types.h> 21 - 22 - #define FAKE_JUMP_OFFSET -1 23 22 24 23 struct alternative { 25 24 struct list_head list; ··· 110 111 111 112 static bool is_sibling_call(struct instruction *insn) 112 113 { 114 + /* 115 + * Assume only ELF functions can make sibling calls. This ensures 116 + * sibling call detection consistency between vmlinux.o and individual 117 + * objects. 118 + */ 119 + if (!insn->func) 120 + return false; 121 + 113 122 /* An indirect jump is either a sibling call or a jump to a table. */ 114 123 if (insn->type == INSN_JUMP_DYNAMIC) 115 124 return list_empty(&insn->alts); 116 125 117 - if (!is_static_jump(insn)) 118 - return false; 119 - 120 126 /* add_jump_destinations() sets insn->call_dest for sibling calls. */ 121 - return !!insn->call_dest; 127 + return (is_static_jump(insn) && insn->call_dest); 122 128 } 123 129 124 130 /* ··· 160 156 "machine_real_restart", 161 157 "rewind_stack_do_exit", 162 158 "kunit_try_catch_throw", 159 + "xen_start_kernel", 163 160 }; 164 161 165 162 if (!func) ··· 792 787 if (!is_static_jump(insn)) 793 788 continue; 794 789 795 - if (insn->offset == FAKE_JUMP_OFFSET) 796 - continue; 797 - 798 790 reloc = find_reloc_by_dest_range(file->elf, insn->sec, 799 - insn->offset, insn->len); 791 + insn->offset, insn->len); 800 792 if (!reloc) { 801 793 dest_sec = insn->sec; 802 794 dest_off = arch_jump_destination(insn); 803 795 } else if (reloc->sym->type == STT_SECTION) { 804 796 dest_sec = reloc->sym->sec; 805 797 dest_off = arch_dest_reloc_offset(reloc->addend); 806 - } else if (reloc->sym->sec->idx) { 807 - dest_sec = reloc->sym->sec; 808 - dest_off = reloc->sym->sym.st_value + 809 - arch_dest_reloc_offset(reloc->addend); 810 - } else if (strstr(reloc->sym->name, "_indirect_thunk_")) { 798 + } else if (!strncmp(reloc->sym->name, "__x86_indirect_thunk_", 21) || 799 + !strncmp(reloc->sym->name, "__x86_retpoline_", 16)) { 811 800 /* 812 801 * Retpoline jumps are really dynamic jumps in 813 802 * disguise, so convert them accordingly. ··· 813 814 814 815 insn->retpoline_safe = true; 815 816 continue; 816 - } else { 817 - /* external sibling call */ 817 + } else if (insn->func) { 818 + /* internal or external sibling call (with reloc) */ 818 819 insn->call_dest = reloc->sym; 819 820 if (insn->call_dest->static_call_tramp) { 820 821 list_add_tail(&insn->static_call_node, 821 822 &file->static_call_list); 822 823 } 824 + continue; 825 + } else if (reloc->sym->sec->idx) { 826 + dest_sec = reloc->sym->sec; 827 + dest_off = reloc->sym->sym.st_value + 828 + arch_dest_reloc_offset(reloc->addend); 829 + } else { 830 + /* non-func asm code jumping to another file */ 823 831 continue; 824 832 } 825 833 ··· 868 862 * case where the parent function's only reference to a 869 863 * subfunction is through a jump table. 870 864 */ 871 - if (!strstr(insn->func->name, ".cold.") && 872 - strstr(insn->jump_dest->func->name, ".cold.")) { 865 + if (!strstr(insn->func->name, ".cold") && 866 + strstr(insn->jump_dest->func->name, ".cold")) { 873 867 insn->func->cfunc = insn->jump_dest->func; 874 868 insn->jump_dest->func->pfunc = insn->func; 875 869 876 870 } else if (insn->jump_dest->func->pfunc != insn->func->pfunc && 877 871 insn->jump_dest->offset == insn->jump_dest->func->offset) { 878 872 879 - /* internal sibling call */ 873 + /* internal sibling call (without reloc) */ 880 874 insn->call_dest = insn->jump_dest->func; 881 875 if (insn->call_dest->static_call_tramp) { 882 876 list_add_tail(&insn->static_call_node, ··· 989 983 } 990 984 991 985 /* 992 - * The .alternatives section requires some extra special care, over and above 993 - * what other special sections require: 994 - * 995 - * 1. Because alternatives are patched in-place, we need to insert a fake jump 996 - * instruction at the end so that validate_branch() skips all the original 997 - * replaced instructions when validating the new instruction path. 998 - * 999 - * 2. An added wrinkle is that the new instruction length might be zero. In 1000 - * that case the old instructions are replaced with noops. We simulate that 1001 - * by creating a fake jump as the only new instruction. 1002 - * 1003 - * 3. In some cases, the alternative section includes an instruction which 1004 - * conditionally jumps to the _end_ of the entry. We have to modify these 1005 - * jumps' destinations to point back to .text rather than the end of the 1006 - * entry in .altinstr_replacement. 986 + * The .alternatives section requires some extra special care over and above 987 + * other special sections because alternatives are patched in place. 1007 988 */ 1008 989 static int handle_group_alt(struct objtool_file *file, 1009 990 struct special_alt *special_alt, 1010 991 struct instruction *orig_insn, 1011 992 struct instruction **new_insn) 1012 993 { 1013 - static unsigned int alt_group_next_index = 1; 1014 - struct instruction *last_orig_insn, *last_new_insn, *insn, *fake_jump = NULL; 1015 - unsigned int alt_group = alt_group_next_index++; 994 + struct instruction *last_orig_insn, *last_new_insn = NULL, *insn, *nop = NULL; 995 + struct alt_group *orig_alt_group, *new_alt_group; 1016 996 unsigned long dest_off; 997 + 998 + 999 + orig_alt_group = malloc(sizeof(*orig_alt_group)); 1000 + if (!orig_alt_group) { 1001 + WARN("malloc failed"); 1002 + return -1; 1003 + } 1004 + orig_alt_group->cfi = calloc(special_alt->orig_len, 1005 + sizeof(struct cfi_state *)); 1006 + if (!orig_alt_group->cfi) { 1007 + WARN("calloc failed"); 1008 + return -1; 1009 + } 1017 1010 1018 1011 last_orig_insn = NULL; 1019 1012 insn = orig_insn; ··· 1020 1015 if (insn->offset >= special_alt->orig_off + special_alt->orig_len) 1021 1016 break; 1022 1017 1023 - insn->alt_group = alt_group; 1018 + insn->alt_group = orig_alt_group; 1024 1019 last_orig_insn = insn; 1025 1020 } 1021 + orig_alt_group->orig_group = NULL; 1022 + orig_alt_group->first_insn = orig_insn; 1023 + orig_alt_group->last_insn = last_orig_insn; 1026 1024 1027 - if (next_insn_same_sec(file, last_orig_insn)) { 1028 - fake_jump = malloc(sizeof(*fake_jump)); 1029 - if (!fake_jump) { 1025 + 1026 + new_alt_group = malloc(sizeof(*new_alt_group)); 1027 + if (!new_alt_group) { 1028 + WARN("malloc failed"); 1029 + return -1; 1030 + } 1031 + 1032 + if (special_alt->new_len < special_alt->orig_len) { 1033 + /* 1034 + * Insert a fake nop at the end to make the replacement 1035 + * alt_group the same size as the original. This is needed to 1036 + * allow propagate_alt_cfi() to do its magic. When the last 1037 + * instruction affects the stack, the instruction after it (the 1038 + * nop) will propagate the new state to the shared CFI array. 1039 + */ 1040 + nop = malloc(sizeof(*nop)); 1041 + if (!nop) { 1030 1042 WARN("malloc failed"); 1031 1043 return -1; 1032 1044 } 1033 - memset(fake_jump, 0, sizeof(*fake_jump)); 1034 - INIT_LIST_HEAD(&fake_jump->alts); 1035 - INIT_LIST_HEAD(&fake_jump->stack_ops); 1036 - init_cfi_state(&fake_jump->cfi); 1045 + memset(nop, 0, sizeof(*nop)); 1046 + INIT_LIST_HEAD(&nop->alts); 1047 + INIT_LIST_HEAD(&nop->stack_ops); 1048 + init_cfi_state(&nop->cfi); 1037 1049 1038 - fake_jump->sec = special_alt->new_sec; 1039 - fake_jump->offset = FAKE_JUMP_OFFSET; 1040 - fake_jump->type = INSN_JUMP_UNCONDITIONAL; 1041 - fake_jump->jump_dest = list_next_entry(last_orig_insn, list); 1042 - fake_jump->func = orig_insn->func; 1050 + nop->sec = special_alt->new_sec; 1051 + nop->offset = special_alt->new_off + special_alt->new_len; 1052 + nop->len = special_alt->orig_len - special_alt->new_len; 1053 + nop->type = INSN_NOP; 1054 + nop->func = orig_insn->func; 1055 + nop->alt_group = new_alt_group; 1056 + nop->ignore = orig_insn->ignore_alts; 1043 1057 } 1044 1058 1045 1059 if (!special_alt->new_len) { 1046 - if (!fake_jump) { 1047 - WARN("%s: empty alternative at end of section", 1048 - special_alt->orig_sec->name); 1049 - return -1; 1050 - } 1051 - 1052 - *new_insn = fake_jump; 1053 - return 0; 1060 + *new_insn = nop; 1061 + goto end; 1054 1062 } 1055 1063 1056 - last_new_insn = NULL; 1057 - alt_group = alt_group_next_index++; 1058 1064 insn = *new_insn; 1059 1065 sec_for_each_insn_from(file, insn) { 1060 1066 struct reloc *alt_reloc; ··· 1077 1061 1078 1062 insn->ignore = orig_insn->ignore_alts; 1079 1063 insn->func = orig_insn->func; 1080 - insn->alt_group = alt_group; 1064 + insn->alt_group = new_alt_group; 1081 1065 1082 1066 /* 1083 1067 * Since alternative replacement code is copy/pasted by the ··· 1104 1088 continue; 1105 1089 1106 1090 dest_off = arch_jump_destination(insn); 1107 - if (dest_off == special_alt->new_off + special_alt->new_len) { 1108 - if (!fake_jump) { 1109 - WARN("%s: alternative jump to end of section", 1110 - special_alt->orig_sec->name); 1111 - return -1; 1112 - } 1113 - insn->jump_dest = fake_jump; 1114 - } 1091 + if (dest_off == special_alt->new_off + special_alt->new_len) 1092 + insn->jump_dest = next_insn_same_sec(file, last_orig_insn); 1115 1093 1116 1094 if (!insn->jump_dest) { 1117 1095 WARN_FUNC("can't find alternative jump destination", ··· 1120 1110 return -1; 1121 1111 } 1122 1112 1123 - if (fake_jump) 1124 - list_add(&fake_jump->list, &last_new_insn->list); 1125 - 1113 + if (nop) 1114 + list_add(&nop->list, &last_new_insn->list); 1115 + end: 1116 + new_alt_group->orig_group = orig_alt_group; 1117 + new_alt_group->first_insn = *new_insn; 1118 + new_alt_group->last_insn = nop ? : last_new_insn; 1119 + new_alt_group->cfi = orig_alt_group->cfi; 1126 1120 return 0; 1127 1121 } 1128 1122 ··· 1418 1404 return 0; 1419 1405 } 1420 1406 1407 + static void set_func_state(struct cfi_state *state) 1408 + { 1409 + state->cfa = initial_func_cfi.cfa; 1410 + memcpy(&state->regs, &initial_func_cfi.regs, 1411 + CFI_NUM_REGS * sizeof(struct cfi_reg)); 1412 + state->stack_size = initial_func_cfi.cfa.offset; 1413 + } 1414 + 1421 1415 static int read_unwind_hints(struct objtool_file *file) 1422 1416 { 1423 1417 struct section *sec, *relocsec; 1424 1418 struct reloc *reloc; 1425 1419 struct unwind_hint *hint; 1426 1420 struct instruction *insn; 1427 - struct cfi_reg *cfa; 1428 1421 int i; 1429 1422 1430 1423 sec = find_section_by_name(file->elf, ".discard.unwind_hints"); ··· 1466 1445 return -1; 1467 1446 } 1468 1447 1469 - cfa = &insn->cfi.cfa; 1448 + insn->hint = true; 1470 1449 1471 - if (hint->type == UNWIND_HINT_TYPE_RET_OFFSET) { 1472 - insn->ret_offset = hint->sp_offset; 1450 + if (hint->type == UNWIND_HINT_TYPE_FUNC) { 1451 + set_func_state(&insn->cfi); 1473 1452 continue; 1474 1453 } 1475 - 1476 - insn->hint = true; 1477 1454 1478 1455 if (arch_decode_hint_reg(insn, hint->sp_reg)) { 1479 1456 WARN_FUNC("unsupported unwind_hint sp base reg %d", ··· 1479 1460 return -1; 1480 1461 } 1481 1462 1482 - cfa->offset = hint->sp_offset; 1463 + insn->cfi.cfa.offset = bswap_if_needed(hint->sp_offset); 1483 1464 insn->cfi.type = hint->type; 1484 1465 insn->cfi.end = hint->end; 1485 1466 } ··· 1735 1716 1736 1717 static bool has_modified_stack_frame(struct instruction *insn, struct insn_state *state) 1737 1718 { 1738 - u8 ret_offset = insn->ret_offset; 1739 1719 struct cfi_state *cfi = &state->cfi; 1740 1720 int i; 1741 1721 1742 1722 if (cfi->cfa.base != initial_func_cfi.cfa.base || cfi->drap) 1743 1723 return true; 1744 1724 1745 - if (cfi->cfa.offset != initial_func_cfi.cfa.offset + ret_offset) 1725 + if (cfi->cfa.offset != initial_func_cfi.cfa.offset) 1746 1726 return true; 1747 1727 1748 - if (cfi->stack_size != initial_func_cfi.cfa.offset + ret_offset) 1728 + if (cfi->stack_size != initial_func_cfi.cfa.offset) 1749 1729 return true; 1750 - 1751 - /* 1752 - * If there is a ret offset hint then don't check registers 1753 - * because a callee-saved register might have been pushed on 1754 - * the stack. 1755 - */ 1756 - if (ret_offset) 1757 - return false; 1758 1730 1759 1731 for (i = 0; i < CFI_NUM_REGS; i++) { 1760 1732 if (cfi->regs[i].base != initial_func_cfi.regs[i].base || ··· 1756 1746 return false; 1757 1747 } 1758 1748 1749 + static bool check_reg_frame_pos(const struct cfi_reg *reg, 1750 + int expected_offset) 1751 + { 1752 + return reg->base == CFI_CFA && 1753 + reg->offset == expected_offset; 1754 + } 1755 + 1759 1756 static bool has_valid_stack_frame(struct insn_state *state) 1760 1757 { 1761 1758 struct cfi_state *cfi = &state->cfi; 1762 1759 1763 - if (cfi->cfa.base == CFI_BP && cfi->regs[CFI_BP].base == CFI_CFA && 1764 - cfi->regs[CFI_BP].offset == -16) 1760 + if (cfi->cfa.base == CFI_BP && 1761 + check_reg_frame_pos(&cfi->regs[CFI_BP], -cfi->cfa.offset) && 1762 + check_reg_frame_pos(&cfi->regs[CFI_RA], -cfi->cfa.offset + 8)) 1765 1763 return true; 1766 1764 1767 1765 if (cfi->drap && cfi->regs[CFI_BP].base == CFI_BP) ··· 1898 1880 case OP_SRC_REG: 1899 1881 if (op->src.reg == CFI_SP && op->dest.reg == CFI_BP && 1900 1882 cfa->base == CFI_SP && 1901 - regs[CFI_BP].base == CFI_CFA && 1902 - regs[CFI_BP].offset == -cfa->offset) { 1883 + check_reg_frame_pos(&regs[CFI_BP], -cfa->offset)) { 1903 1884 1904 1885 /* mov %rsp, %rbp */ 1905 1886 cfa->base = op->dest.reg; ··· 1958 1941 cfa->offset = -cfi->vals[op->src.reg].offset; 1959 1942 cfi->stack_size = cfa->offset; 1960 1943 1944 + } else if (cfa->base == CFI_SP && 1945 + cfi->vals[op->src.reg].base == CFI_SP_INDIRECT && 1946 + cfi->vals[op->src.reg].offset == cfa->offset) { 1947 + 1948 + /* 1949 + * Stack swizzle: 1950 + * 1951 + * 1: mov %rsp, (%[tos]) 1952 + * 2: mov %[tos], %rsp 1953 + * ... 1954 + * 3: pop %rsp 1955 + * 1956 + * Where: 1957 + * 1958 + * 1 - places a pointer to the previous 1959 + * stack at the Top-of-Stack of the 1960 + * new stack. 1961 + * 1962 + * 2 - switches to the new stack. 1963 + * 1964 + * 3 - pops the Top-of-Stack to restore 1965 + * the original stack. 1966 + * 1967 + * Note: we set base to SP_INDIRECT 1968 + * here and preserve offset. Therefore 1969 + * when the unwinder reaches ToS it 1970 + * will dereference SP and then add the 1971 + * offset to find the next frame, IOW: 1972 + * (%rsp) + offset. 1973 + */ 1974 + cfa->base = CFI_SP_INDIRECT; 1975 + 1961 1976 } else { 1962 1977 cfa->base = CFI_UNDEFINED; 1963 1978 cfa->offset = 0; ··· 2012 1963 2013 1964 /* lea disp(%rbp), %rsp */ 2014 1965 cfi->stack_size = -(op->src.offset + regs[CFI_BP].offset); 1966 + break; 1967 + } 1968 + 1969 + if (!cfi->drap && op->src.reg == CFI_SP && 1970 + op->dest.reg == CFI_BP && cfa->base == CFI_SP && 1971 + check_reg_frame_pos(&regs[CFI_BP], -cfa->offset + op->src.offset)) { 1972 + 1973 + /* lea disp(%rsp), %rbp */ 1974 + cfa->base = CFI_BP; 1975 + cfa->offset -= op->src.offset; 1976 + cfi->bp_scratch = false; 2015 1977 break; 2016 1978 } 2017 1979 ··· 2092 2032 2093 2033 case OP_SRC_POP: 2094 2034 case OP_SRC_POPF: 2035 + if (op->dest.reg == CFI_SP && cfa->base == CFI_SP_INDIRECT) { 2036 + 2037 + /* pop %rsp; # restore from a stack swizzle */ 2038 + cfa->base = CFI_SP; 2039 + break; 2040 + } 2041 + 2095 2042 if (!cfi->drap && op->dest.reg == cfa->base) { 2096 2043 2097 2044 /* pop %rbp */ ··· 2127 2060 break; 2128 2061 2129 2062 case OP_SRC_REG_INDIRECT: 2063 + if (!cfi->drap && op->dest.reg == cfa->base && 2064 + op->dest.reg == CFI_BP) { 2065 + 2066 + /* mov disp(%rsp), %rbp */ 2067 + cfa->base = CFI_SP; 2068 + cfa->offset = cfi->stack_size; 2069 + } 2070 + 2130 2071 if (cfi->drap && op->src.reg == CFI_BP && 2131 2072 op->src.offset == cfi->drap_offset) { 2132 2073 ··· 2154 2079 op->src.offset == regs[op->dest.reg].offset + cfa->offset) { 2155 2080 2156 2081 /* mov disp(%rbp), %reg */ 2082 + /* mov disp(%rsp), %reg */ 2083 + restore_reg(cfi, op->dest.reg); 2084 + 2085 + } else if (op->src.reg == CFI_SP && 2086 + op->src.offset == regs[op->dest.reg].offset + cfi->stack_size) { 2087 + 2157 2088 /* mov disp(%rsp), %reg */ 2158 2089 restore_reg(cfi, op->dest.reg); 2159 2090 } ··· 2239 2158 /* mov reg, disp(%rsp) */ 2240 2159 save_reg(cfi, op->src.reg, CFI_CFA, 2241 2160 op->dest.offset - cfi->cfa.offset); 2161 + 2162 + } else if (op->dest.reg == CFI_SP) { 2163 + 2164 + /* mov reg, disp(%rsp) */ 2165 + save_reg(cfi, op->src.reg, CFI_CFA, 2166 + op->dest.offset - cfi->stack_size); 2167 + 2168 + } else if (op->src.reg == CFI_SP && op->dest.offset == 0) { 2169 + 2170 + /* mov %rsp, (%reg); # setup a stack swizzle. */ 2171 + cfi->vals[op->dest.reg].base = CFI_SP_INDIRECT; 2172 + cfi->vals[op->dest.reg].offset = cfa->offset; 2242 2173 } 2243 2174 2244 2175 break; ··· 2298 2205 return 0; 2299 2206 } 2300 2207 2208 + /* 2209 + * The stack layouts of alternatives instructions can sometimes diverge when 2210 + * they have stack modifications. That's fine as long as the potential stack 2211 + * layouts don't conflict at any given potential instruction boundary. 2212 + * 2213 + * Flatten the CFIs of the different alternative code streams (both original 2214 + * and replacement) into a single shared CFI array which can be used to detect 2215 + * conflicts and nicely feed a linear array of ORC entries to the unwinder. 2216 + */ 2217 + static int propagate_alt_cfi(struct objtool_file *file, struct instruction *insn) 2218 + { 2219 + struct cfi_state **alt_cfi; 2220 + int group_off; 2221 + 2222 + if (!insn->alt_group) 2223 + return 0; 2224 + 2225 + alt_cfi = insn->alt_group->cfi; 2226 + group_off = insn->offset - insn->alt_group->first_insn->offset; 2227 + 2228 + if (!alt_cfi[group_off]) { 2229 + alt_cfi[group_off] = &insn->cfi; 2230 + } else { 2231 + if (memcmp(alt_cfi[group_off], &insn->cfi, sizeof(struct cfi_state))) { 2232 + WARN_FUNC("stack layout conflict in alternatives", 2233 + insn->sec, insn->offset); 2234 + return -1; 2235 + } 2236 + } 2237 + 2238 + return 0; 2239 + } 2240 + 2301 2241 static int handle_insn_ops(struct instruction *insn, struct insn_state *state) 2302 2242 { 2303 2243 struct stack_op *op; 2304 2244 2305 2245 list_for_each_entry(op, &insn->stack_ops, list) { 2306 - struct cfi_state old_cfi = state->cfi; 2307 - int res; 2308 2246 2309 - res = update_cfi_state(insn, &state->cfi, op); 2310 - if (res) 2311 - return res; 2312 - 2313 - if (insn->alt_group && memcmp(&state->cfi, &old_cfi, sizeof(struct cfi_state))) { 2314 - WARN_FUNC("alternative modifies stack", insn->sec, insn->offset); 2315 - return -1; 2316 - } 2247 + if (update_cfi_state(insn, &state->cfi, op)) 2248 + return 1; 2317 2249 2318 2250 if (op->dest.type == OP_DEST_PUSHF) { 2319 2251 if (!state->uaccess_stack) { ··· 2528 2410 return 0; 2529 2411 } 2530 2412 2531 - /* 2532 - * Alternatives should not contain any ORC entries, this in turn means they 2533 - * should not contain any CFI ops, which implies all instructions should have 2534 - * the same same CFI state. 2535 - * 2536 - * It is possible to constuct alternatives that have unreachable holes that go 2537 - * unreported (because they're NOPs), such holes would result in CFI_UNDEFINED 2538 - * states which then results in ORC entries, which we just said we didn't want. 2539 - * 2540 - * Avoid them by copying the CFI entry of the first instruction into the whole 2541 - * alternative. 2542 - */ 2543 - static void fill_alternative_cfi(struct objtool_file *file, struct instruction *insn) 2413 + static struct instruction *next_insn_to_validate(struct objtool_file *file, 2414 + struct instruction *insn) 2544 2415 { 2545 - struct instruction *first_insn = insn; 2546 - int alt_group = insn->alt_group; 2416 + struct alt_group *alt_group = insn->alt_group; 2547 2417 2548 - sec_for_each_insn_continue(file, insn) { 2549 - if (insn->alt_group != alt_group) 2550 - break; 2551 - insn->cfi = first_insn->cfi; 2552 - } 2418 + /* 2419 + * Simulate the fact that alternatives are patched in-place. When the 2420 + * end of a replacement alt_group is reached, redirect objtool flow to 2421 + * the end of the original alt_group. 2422 + */ 2423 + if (alt_group && insn == alt_group->last_insn && alt_group->orig_group) 2424 + return next_insn_same_sec(file, alt_group->orig_group->last_insn); 2425 + 2426 + return next_insn_same_sec(file, insn); 2553 2427 } 2554 2428 2555 2429 /* ··· 2562 2452 sec = insn->sec; 2563 2453 2564 2454 while (1) { 2565 - next_insn = next_insn_same_sec(file, insn); 2455 + next_insn = next_insn_to_validate(file, insn); 2566 2456 2567 2457 if (file->c_file && func && insn->func && func != insn->func->pfunc) { 2568 2458 WARN("%s() falls through to next function %s()", ··· 2595 2485 2596 2486 insn->visited |= visited; 2597 2487 2488 + if (propagate_alt_cfi(file, insn)) 2489 + return 1; 2490 + 2598 2491 if (!insn->ignore_alts && !list_empty(&insn->alts)) { 2599 2492 bool skip_orig = false; 2600 2493 ··· 2612 2499 return ret; 2613 2500 } 2614 2501 } 2615 - 2616 - if (insn->alt_group) 2617 - fill_alternative_cfi(file, insn); 2618 2502 2619 2503 if (skip_orig) 2620 2504 return 0; ··· 2650 2540 2651 2541 case INSN_JUMP_CONDITIONAL: 2652 2542 case INSN_JUMP_UNCONDITIONAL: 2653 - if (func && is_sibling_call(insn)) { 2543 + if (is_sibling_call(insn)) { 2654 2544 ret = validate_sibling_call(insn, &state); 2655 2545 if (ret) 2656 2546 return ret; ··· 2672 2562 2673 2563 case INSN_JUMP_DYNAMIC: 2674 2564 case INSN_JUMP_DYNAMIC_CONDITIONAL: 2675 - if (func && is_sibling_call(insn)) { 2565 + if (is_sibling_call(insn)) { 2676 2566 ret = validate_sibling_call(insn, &state); 2677 2567 if (ret) 2678 2568 return ret; ··· 2715 2605 break; 2716 2606 2717 2607 case INSN_STD: 2718 - if (state.df) 2608 + if (state.df) { 2719 2609 WARN_FUNC("recursive STD", sec, insn->offset); 2610 + return 1; 2611 + } 2720 2612 2721 2613 state.df = true; 2722 2614 break; 2723 2615 2724 2616 case INSN_CLD: 2725 - if (!state.df && func) 2617 + if (!state.df && func) { 2726 2618 WARN_FUNC("redundant CLD", sec, insn->offset); 2619 + return 1; 2620 + } 2727 2621 2728 2622 state.df = false; 2729 2623 break; ··· 2850 2736 !strcmp(insn->sec->name, ".altinstr_aux")) 2851 2737 return true; 2852 2738 2853 - if (insn->type == INSN_JUMP_UNCONDITIONAL && insn->offset == FAKE_JUMP_OFFSET) 2854 - return true; 2855 - 2856 2739 if (!insn->func) 2857 2740 return false; 2858 2741 ··· 2935 2824 continue; 2936 2825 2937 2826 init_insn_state(&state, sec); 2938 - state.cfi.cfa = initial_func_cfi.cfa; 2939 - memcpy(&state.cfi.regs, &initial_func_cfi.regs, 2940 - CFI_NUM_REGS * sizeof(struct cfi_reg)); 2941 - state.cfi.stack_size = initial_func_cfi.cfa.offset; 2827 + set_func_state(&state.cfi); 2942 2828 2943 2829 warnings += validate_symbol(file, sec, func, &state); 2944 2830 }
+31 -7
tools/objtool/check.h tools/objtool/include/objtool/check.h
··· 7 7 #define _CHECK_H 8 8 9 9 #include <stdbool.h> 10 - #include "cfi.h" 11 - #include "arch.h" 10 + #include <objtool/cfi.h> 11 + #include <objtool/arch.h> 12 12 13 13 struct insn_state { 14 14 struct cfi_state cfi; ··· 17 17 bool df; 18 18 bool noinstr; 19 19 s8 instr; 20 + }; 21 + 22 + struct alt_group { 23 + /* 24 + * Pointer from a replacement group to the original group. NULL if it 25 + * *is* the original group. 26 + */ 27 + struct alt_group *orig_group; 28 + 29 + /* First and last instructions in the group */ 30 + struct instruction *first_insn, *last_insn; 31 + 32 + /* 33 + * Byte-offset-addressed len-sized array of pointers to CFI structs. 34 + * This is shared with the other alt_groups in the same alternative. 35 + */ 36 + struct cfi_state **cfi; 20 37 }; 21 38 22 39 struct instruction { ··· 50 33 bool retpoline_safe; 51 34 s8 instr; 52 35 u8 visited; 53 - u8 ret_offset; 54 - int alt_group; 36 + struct alt_group *alt_group; 55 37 struct symbol *call_dest; 56 38 struct instruction *jump_dest; 57 39 struct instruction *first_jump_src; ··· 59 43 struct symbol *func; 60 44 struct list_head stack_ops; 61 45 struct cfi_state cfi; 62 - #ifdef INSN_USE_ORC 63 - struct orc_entry orc; 64 - #endif 65 46 }; 66 47 67 48 static inline bool is_static_jump(struct instruction *insn) 68 49 { 69 50 return insn->type == INSN_JUMP_CONDITIONAL || 70 51 insn->type == INSN_JUMP_UNCONDITIONAL; 52 + } 53 + 54 + static inline bool is_dynamic_jump(struct instruction *insn) 55 + { 56 + return insn->type == INSN_JUMP_DYNAMIC || 57 + insn->type == INSN_JUMP_DYNAMIC_CONDITIONAL; 58 + } 59 + 60 + static inline bool is_jump(struct instruction *insn) 61 + { 62 + return is_static_jump(insn) || is_dynamic_jump(insn); 71 63 } 72 64 73 65 struct instruction *find_insn(struct objtool_file *file,
+22 -18
tools/objtool/elf.c
··· 15 15 #include <string.h> 16 16 #include <unistd.h> 17 17 #include <errno.h> 18 - #include "builtin.h" 18 + #include <objtool/builtin.h> 19 19 20 - #include "elf.h" 21 - #include "warn.h" 20 + #include <objtool/elf.h> 21 + #include <objtool/warn.h> 22 22 23 23 #define MAX_NAME_LEN 128 24 24 ··· 814 814 { 815 815 struct reloc *reloc; 816 816 int idx = 0, size; 817 - GElf_Rel *relocs; 817 + void *buf; 818 818 819 819 /* Allocate a buffer for relocations */ 820 - size = nr * sizeof(*relocs); 821 - relocs = malloc(size); 822 - if (!relocs) { 820 + size = nr * sizeof(GElf_Rel); 821 + buf = malloc(size); 822 + if (!buf) { 823 823 perror("malloc"); 824 824 return -1; 825 825 } 826 826 827 - sec->data->d_buf = relocs; 827 + sec->data->d_buf = buf; 828 828 sec->data->d_size = size; 829 + sec->data->d_type = ELF_T_REL; 829 830 830 831 sec->sh.sh_size = size; 831 832 832 833 idx = 0; 833 834 list_for_each_entry(reloc, &sec->reloc_list, list) { 834 - relocs[idx].r_offset = reloc->offset; 835 - relocs[idx].r_info = GELF_R_INFO(reloc->sym->idx, reloc->type); 835 + reloc->rel.r_offset = reloc->offset; 836 + reloc->rel.r_info = GELF_R_INFO(reloc->sym->idx, reloc->type); 837 + gelf_update_rel(sec->data, idx, &reloc->rel); 836 838 idx++; 837 839 } 838 840 ··· 845 843 { 846 844 struct reloc *reloc; 847 845 int idx = 0, size; 848 - GElf_Rela *relocs; 846 + void *buf; 849 847 850 848 /* Allocate a buffer for relocations with addends */ 851 - size = nr * sizeof(*relocs); 852 - relocs = malloc(size); 853 - if (!relocs) { 849 + size = nr * sizeof(GElf_Rela); 850 + buf = malloc(size); 851 + if (!buf) { 854 852 perror("malloc"); 855 853 return -1; 856 854 } 857 855 858 - sec->data->d_buf = relocs; 856 + sec->data->d_buf = buf; 859 857 sec->data->d_size = size; 858 + sec->data->d_type = ELF_T_RELA; 860 859 861 860 sec->sh.sh_size = size; 862 861 863 862 idx = 0; 864 863 list_for_each_entry(reloc, &sec->reloc_list, list) { 865 - relocs[idx].r_offset = reloc->offset; 866 - relocs[idx].r_addend = reloc->addend; 867 - relocs[idx].r_info = GELF_R_INFO(reloc->sym->idx, reloc->type); 864 + reloc->rela.r_offset = reloc->offset; 865 + reloc->rela.r_addend = reloc->addend; 866 + reloc->rela.r_info = GELF_R_INFO(reloc->sym->idx, reloc->type); 867 + gelf_update_rela(sec->data, idx, &reloc->rela); 868 868 idx++; 869 869 } 870 870
tools/objtool/elf.h tools/objtool/include/objtool/elf.h
+38
tools/objtool/include/objtool/endianness.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0-or-later */ 2 + #ifndef _OBJTOOL_ENDIANNESS_H 3 + #define _OBJTOOL_ENDIANNESS_H 4 + 5 + #include <arch/endianness.h> 6 + #include <linux/kernel.h> 7 + #include <endian.h> 8 + 9 + #ifndef __TARGET_BYTE_ORDER 10 + #error undefined arch __TARGET_BYTE_ORDER 11 + #endif 12 + 13 + #if __BYTE_ORDER != __TARGET_BYTE_ORDER 14 + #define __NEED_BSWAP 1 15 + #else 16 + #define __NEED_BSWAP 0 17 + #endif 18 + 19 + /* 20 + * Does a byte swap if target endianness doesn't match the host, i.e. cross 21 + * compilation for little endian on big endian and vice versa. 22 + * To be used for multi-byte values conversion, which are read from / about 23 + * to be written to a target native endianness ELF file. 24 + */ 25 + #define bswap_if_needed(val) \ 26 + ({ \ 27 + __typeof__(val) __ret; \ 28 + switch (sizeof(val)) { \ 29 + case 8: __ret = __NEED_BSWAP ? bswap_64(val) : (val); break; \ 30 + case 4: __ret = __NEED_BSWAP ? bswap_32(val) : (val); break; \ 31 + case 2: __ret = __NEED_BSWAP ? bswap_16(val) : (val); break; \ 32 + default: \ 33 + BUILD_BUG(); break; \ 34 + } \ 35 + __ret; \ 36 + }) 37 + 38 + #endif /* _OBJTOOL_ENDIANNESS_H */
+3 -3
tools/objtool/objtool.c
··· 21 21 #include <subcmd/pager.h> 22 22 #include <linux/kernel.h> 23 23 24 - #include "builtin.h" 25 - #include "objtool.h" 26 - #include "warn.h" 24 + #include <objtool/builtin.h> 25 + #include <objtool/objtool.h> 26 + #include <objtool/warn.h> 27 27 28 28 struct cmd_struct { 29 29 const char *name;
+2 -3
tools/objtool/objtool.h tools/objtool/include/objtool/objtool.h
··· 10 10 #include <linux/list.h> 11 11 #include <linux/hashtable.h> 12 12 13 - #include "elf.h" 13 + #include <objtool/elf.h> 14 14 15 15 #define __weak __attribute__((weak)) 16 16 ··· 26 26 27 27 int check(struct objtool_file *file); 28 28 int orc_dump(const char *objname); 29 - int create_orc(struct objtool_file *file); 30 - int create_orc_sections(struct objtool_file *file); 29 + int orc_create(struct objtool_file *file); 31 30 32 31 #endif /* _OBJTOOL_H */
+6 -5
tools/objtool/orc_dump.c
··· 6 6 #include <unistd.h> 7 7 #include <linux/objtool.h> 8 8 #include <asm/orc_types.h> 9 - #include "objtool.h" 10 - #include "warn.h" 9 + #include <objtool/objtool.h> 10 + #include <objtool/warn.h> 11 + #include <objtool/endianness.h> 11 12 12 13 static const char *reg_name(unsigned int reg) 13 14 { ··· 55 54 if (reg == ORC_REG_BP_INDIRECT) 56 55 printf("(bp%+d)", offset); 57 56 else if (reg == ORC_REG_SP_INDIRECT) 58 - printf("(sp%+d)", offset); 57 + printf("(sp)%+d", offset); 59 58 else if (reg == ORC_REG_UNDEFINED) 60 59 printf("(und)"); 61 60 else ··· 198 197 199 198 printf(" sp:"); 200 199 201 - print_reg(orc[i].sp_reg, orc[i].sp_offset); 200 + print_reg(orc[i].sp_reg, bswap_if_needed(orc[i].sp_offset)); 202 201 203 202 printf(" bp:"); 204 203 205 - print_reg(orc[i].bp_reg, orc[i].bp_offset); 204 + print_reg(orc[i].bp_reg, bswap_if_needed(orc[i].bp_offset)); 206 205 207 206 printf(" type:%s end:%d\n", 208 207 orc_type_name(orc[i].type), orc[i].end);
+188 -143
tools/objtool/orc_gen.c
··· 9 9 #include <linux/objtool.h> 10 10 #include <asm/orc_types.h> 11 11 12 - #include "check.h" 13 - #include "warn.h" 12 + #include <objtool/check.h> 13 + #include <objtool/warn.h> 14 + #include <objtool/endianness.h> 14 15 15 - int create_orc(struct objtool_file *file) 16 + static int init_orc_entry(struct orc_entry *orc, struct cfi_state *cfi) 16 17 { 17 - struct instruction *insn; 18 + struct instruction *insn = container_of(cfi, struct instruction, cfi); 19 + struct cfi_reg *bp = &cfi->regs[CFI_BP]; 18 20 19 - for_each_insn(file, insn) { 20 - struct orc_entry *orc = &insn->orc; 21 - struct cfi_reg *cfa = &insn->cfi.cfa; 22 - struct cfi_reg *bp = &insn->cfi.regs[CFI_BP]; 21 + memset(orc, 0, sizeof(*orc)); 23 22 24 - if (!insn->sec->text) 25 - continue; 23 + orc->end = cfi->end; 26 24 27 - orc->end = insn->cfi.end; 28 - 29 - if (cfa->base == CFI_UNDEFINED) { 30 - orc->sp_reg = ORC_REG_UNDEFINED; 31 - continue; 32 - } 33 - 34 - switch (cfa->base) { 35 - case CFI_SP: 36 - orc->sp_reg = ORC_REG_SP; 37 - break; 38 - case CFI_SP_INDIRECT: 39 - orc->sp_reg = ORC_REG_SP_INDIRECT; 40 - break; 41 - case CFI_BP: 42 - orc->sp_reg = ORC_REG_BP; 43 - break; 44 - case CFI_BP_INDIRECT: 45 - orc->sp_reg = ORC_REG_BP_INDIRECT; 46 - break; 47 - case CFI_R10: 48 - orc->sp_reg = ORC_REG_R10; 49 - break; 50 - case CFI_R13: 51 - orc->sp_reg = ORC_REG_R13; 52 - break; 53 - case CFI_DI: 54 - orc->sp_reg = ORC_REG_DI; 55 - break; 56 - case CFI_DX: 57 - orc->sp_reg = ORC_REG_DX; 58 - break; 59 - default: 60 - WARN_FUNC("unknown CFA base reg %d", 61 - insn->sec, insn->offset, cfa->base); 62 - return -1; 63 - } 64 - 65 - switch(bp->base) { 66 - case CFI_UNDEFINED: 67 - orc->bp_reg = ORC_REG_UNDEFINED; 68 - break; 69 - case CFI_CFA: 70 - orc->bp_reg = ORC_REG_PREV_SP; 71 - break; 72 - case CFI_BP: 73 - orc->bp_reg = ORC_REG_BP; 74 - break; 75 - default: 76 - WARN_FUNC("unknown BP base reg %d", 77 - insn->sec, insn->offset, bp->base); 78 - return -1; 79 - } 80 - 81 - orc->sp_offset = cfa->offset; 82 - orc->bp_offset = bp->offset; 83 - orc->type = insn->cfi.type; 25 + if (cfi->cfa.base == CFI_UNDEFINED) { 26 + orc->sp_reg = ORC_REG_UNDEFINED; 27 + return 0; 84 28 } 29 + 30 + switch (cfi->cfa.base) { 31 + case CFI_SP: 32 + orc->sp_reg = ORC_REG_SP; 33 + break; 34 + case CFI_SP_INDIRECT: 35 + orc->sp_reg = ORC_REG_SP_INDIRECT; 36 + break; 37 + case CFI_BP: 38 + orc->sp_reg = ORC_REG_BP; 39 + break; 40 + case CFI_BP_INDIRECT: 41 + orc->sp_reg = ORC_REG_BP_INDIRECT; 42 + break; 43 + case CFI_R10: 44 + orc->sp_reg = ORC_REG_R10; 45 + break; 46 + case CFI_R13: 47 + orc->sp_reg = ORC_REG_R13; 48 + break; 49 + case CFI_DI: 50 + orc->sp_reg = ORC_REG_DI; 51 + break; 52 + case CFI_DX: 53 + orc->sp_reg = ORC_REG_DX; 54 + break; 55 + default: 56 + WARN_FUNC("unknown CFA base reg %d", 57 + insn->sec, insn->offset, cfi->cfa.base); 58 + return -1; 59 + } 60 + 61 + switch (bp->base) { 62 + case CFI_UNDEFINED: 63 + orc->bp_reg = ORC_REG_UNDEFINED; 64 + break; 65 + case CFI_CFA: 66 + orc->bp_reg = ORC_REG_PREV_SP; 67 + break; 68 + case CFI_BP: 69 + orc->bp_reg = ORC_REG_BP; 70 + break; 71 + default: 72 + WARN_FUNC("unknown BP base reg %d", 73 + insn->sec, insn->offset, bp->base); 74 + return -1; 75 + } 76 + 77 + orc->sp_offset = cfi->cfa.offset; 78 + orc->bp_offset = bp->offset; 79 + orc->type = cfi->type; 85 80 86 81 return 0; 87 82 } 88 83 89 - static int create_orc_entry(struct elf *elf, struct section *u_sec, struct section *ip_relocsec, 90 - unsigned int idx, struct section *insn_sec, 91 - unsigned long insn_off, struct orc_entry *o) 84 + static int write_orc_entry(struct elf *elf, struct section *orc_sec, 85 + struct section *ip_rsec, unsigned int idx, 86 + struct section *insn_sec, unsigned long insn_off, 87 + struct orc_entry *o) 92 88 { 93 89 struct orc_entry *orc; 94 90 struct reloc *reloc; 95 91 96 92 /* populate ORC data */ 97 - orc = (struct orc_entry *)u_sec->data->d_buf + idx; 93 + orc = (struct orc_entry *)orc_sec->data->d_buf + idx; 98 94 memcpy(orc, o, sizeof(*orc)); 95 + orc->sp_offset = bswap_if_needed(orc->sp_offset); 96 + orc->bp_offset = bswap_if_needed(orc->bp_offset); 99 97 100 98 /* populate reloc for ip */ 101 99 reloc = malloc(sizeof(*reloc)); ··· 112 114 113 115 reloc->type = R_X86_64_PC32; 114 116 reloc->offset = idx * sizeof(int); 115 - reloc->sec = ip_relocsec; 117 + reloc->sec = ip_rsec; 116 118 117 119 elf_add_reloc(elf, reloc); 118 120 119 121 return 0; 120 122 } 121 123 122 - int create_orc_sections(struct objtool_file *file) 123 - { 124 - struct instruction *insn, *prev_insn; 125 - struct section *sec, *u_sec, *ip_relocsec; 126 - unsigned int idx; 124 + struct orc_list_entry { 125 + struct list_head list; 126 + struct orc_entry orc; 127 + struct section *insn_sec; 128 + unsigned long insn_off; 129 + }; 127 130 128 - struct orc_entry empty = { 129 - .sp_reg = ORC_REG_UNDEFINED, 131 + static int orc_list_add(struct list_head *orc_list, struct orc_entry *orc, 132 + struct section *sec, unsigned long offset) 133 + { 134 + struct orc_list_entry *entry = malloc(sizeof(*entry)); 135 + 136 + if (!entry) { 137 + WARN("malloc failed"); 138 + return -1; 139 + } 140 + 141 + entry->orc = *orc; 142 + entry->insn_sec = sec; 143 + entry->insn_off = offset; 144 + 145 + list_add_tail(&entry->list, orc_list); 146 + return 0; 147 + } 148 + 149 + static unsigned long alt_group_len(struct alt_group *alt_group) 150 + { 151 + return alt_group->last_insn->offset + 152 + alt_group->last_insn->len - 153 + alt_group->first_insn->offset; 154 + } 155 + 156 + int orc_create(struct objtool_file *file) 157 + { 158 + struct section *sec, *ip_rsec, *orc_sec; 159 + unsigned int nr = 0, idx = 0; 160 + struct orc_list_entry *entry; 161 + struct list_head orc_list; 162 + 163 + struct orc_entry null = { 164 + .sp_reg = ORC_REG_UNDEFINED, 130 165 .bp_reg = ORC_REG_UNDEFINED, 131 166 .type = UNWIND_HINT_TYPE_CALL, 132 167 }; 133 168 169 + /* Build a deduplicated list of ORC entries: */ 170 + INIT_LIST_HEAD(&orc_list); 171 + for_each_sec(file, sec) { 172 + struct orc_entry orc, prev_orc = {0}; 173 + struct instruction *insn; 174 + bool empty = true; 175 + 176 + if (!sec->text) 177 + continue; 178 + 179 + sec_for_each_insn(file, sec, insn) { 180 + struct alt_group *alt_group = insn->alt_group; 181 + int i; 182 + 183 + if (!alt_group) { 184 + if (init_orc_entry(&orc, &insn->cfi)) 185 + return -1; 186 + if (!memcmp(&prev_orc, &orc, sizeof(orc))) 187 + continue; 188 + if (orc_list_add(&orc_list, &orc, sec, 189 + insn->offset)) 190 + return -1; 191 + nr++; 192 + prev_orc = orc; 193 + empty = false; 194 + continue; 195 + } 196 + 197 + /* 198 + * Alternatives can have different stack layout 199 + * possibilities (but they shouldn't conflict). 200 + * Instead of traversing the instructions, use the 201 + * alt_group's flattened byte-offset-addressed CFI 202 + * array. 203 + */ 204 + for (i = 0; i < alt_group_len(alt_group); i++) { 205 + struct cfi_state *cfi = alt_group->cfi[i]; 206 + if (!cfi) 207 + continue; 208 + if (init_orc_entry(&orc, cfi)) 209 + return -1; 210 + if (!memcmp(&prev_orc, &orc, sizeof(orc))) 211 + continue; 212 + if (orc_list_add(&orc_list, &orc, insn->sec, 213 + insn->offset + i)) 214 + return -1; 215 + nr++; 216 + prev_orc = orc; 217 + empty = false; 218 + } 219 + 220 + /* Skip to the end of the alt_group */ 221 + insn = alt_group->last_insn; 222 + } 223 + 224 + /* Add a section terminator */ 225 + if (!empty) { 226 + orc_list_add(&orc_list, &null, sec, sec->len); 227 + nr++; 228 + } 229 + } 230 + if (!nr) 231 + return 0; 232 + 233 + /* Create .orc_unwind, .orc_unwind_ip and .rela.orc_unwind_ip sections: */ 134 234 sec = find_section_by_name(file->elf, ".orc_unwind"); 135 235 if (sec) { 136 236 WARN("file already has .orc_unwind section, skipping"); 137 237 return -1; 138 238 } 139 - 140 - /* count the number of needed orcs */ 141 - idx = 0; 142 - for_each_sec(file, sec) { 143 - if (!sec->text) 144 - continue; 145 - 146 - prev_insn = NULL; 147 - sec_for_each_insn(file, sec, insn) { 148 - if (!prev_insn || 149 - memcmp(&insn->orc, &prev_insn->orc, 150 - sizeof(struct orc_entry))) { 151 - idx++; 152 - } 153 - prev_insn = insn; 154 - } 155 - 156 - /* section terminator */ 157 - if (prev_insn) 158 - idx++; 159 - } 160 - if (!idx) 239 + orc_sec = elf_create_section(file->elf, ".orc_unwind", 0, 240 + sizeof(struct orc_entry), nr); 241 + if (!orc_sec) 161 242 return -1; 162 243 163 - 164 - /* create .orc_unwind_ip and .rela.orc_unwind_ip sections */ 165 - sec = elf_create_section(file->elf, ".orc_unwind_ip", 0, sizeof(int), idx); 244 + sec = elf_create_section(file->elf, ".orc_unwind_ip", 0, sizeof(int), nr); 166 245 if (!sec) 167 246 return -1; 168 - 169 - ip_relocsec = elf_create_reloc_section(file->elf, sec, SHT_RELA); 170 - if (!ip_relocsec) 247 + ip_rsec = elf_create_reloc_section(file->elf, sec, SHT_RELA); 248 + if (!ip_rsec) 171 249 return -1; 172 250 173 - /* create .orc_unwind section */ 174 - u_sec = elf_create_section(file->elf, ".orc_unwind", 0, 175 - sizeof(struct orc_entry), idx); 176 - 177 - /* populate sections */ 178 - idx = 0; 179 - for_each_sec(file, sec) { 180 - if (!sec->text) 181 - continue; 182 - 183 - prev_insn = NULL; 184 - sec_for_each_insn(file, sec, insn) { 185 - if (!prev_insn || memcmp(&insn->orc, &prev_insn->orc, 186 - sizeof(struct orc_entry))) { 187 - 188 - if (create_orc_entry(file->elf, u_sec, ip_relocsec, idx, 189 - insn->sec, insn->offset, 190 - &insn->orc)) 191 - return -1; 192 - 193 - idx++; 194 - } 195 - prev_insn = insn; 196 - } 197 - 198 - /* section terminator */ 199 - if (prev_insn) { 200 - if (create_orc_entry(file->elf, u_sec, ip_relocsec, idx, 201 - prev_insn->sec, 202 - prev_insn->offset + prev_insn->len, 203 - &empty)) 204 - return -1; 205 - 206 - idx++; 207 - } 251 + /* Write ORC entries to sections: */ 252 + list_for_each_entry(entry, &orc_list, list) { 253 + if (write_orc_entry(file->elf, orc_sec, ip_rsec, idx++, 254 + entry->insn_sec, entry->insn_off, 255 + &entry->orc)) 256 + return -1; 208 257 } 209 258 210 - if (elf_rebuild_reloc_section(file->elf, ip_relocsec)) 259 + if (elf_rebuild_reloc_section(file->elf, ip_rsec)) 211 260 return -1; 212 261 213 262 return 0;
+8 -6
tools/objtool/special.c
··· 11 11 #include <stdlib.h> 12 12 #include <string.h> 13 13 14 - #include "builtin.h" 15 - #include "special.h" 16 - #include "warn.h" 17 - #include "arch_special.h" 14 + #include <arch/special.h> 15 + #include <objtool/builtin.h> 16 + #include <objtool/special.h> 17 + #include <objtool/warn.h> 18 + #include <objtool/endianness.h> 18 19 19 20 struct special_entry { 20 21 const char *sec; ··· 78 77 if (entry->feature) { 79 78 unsigned short feature; 80 79 81 - feature = *(unsigned short *)(sec->data->d_buf + offset + 82 - entry->feature); 80 + feature = bswap_if_needed(*(unsigned short *)(sec->data->d_buf + 81 + offset + 82 + entry->feature)); 83 83 arch_handle_alternative(feature, alt); 84 84 } 85 85
+2 -2
tools/objtool/special.h tools/objtool/include/objtool/special.h
··· 7 7 #define _SPECIAL_H 8 8 9 9 #include <stdbool.h> 10 - #include "check.h" 11 - #include "elf.h" 10 + #include <objtool/check.h> 11 + #include <objtool/elf.h> 12 12 13 13 #define C_JUMP_TABLE_SECTION ".rodata..c_jump_table" 14 14
+1 -1
tools/objtool/warn.h tools/objtool/include/objtool/warn.h
··· 11 11 #include <sys/types.h> 12 12 #include <sys/stat.h> 13 13 #include <fcntl.h> 14 - #include "elf.h" 14 + #include <objtool/elf.h> 15 15 16 16 extern const char *objname; 17 17
+2 -7
tools/objtool/weak.c
··· 7 7 8 8 #include <stdbool.h> 9 9 #include <errno.h> 10 - #include "objtool.h" 10 + #include <objtool/objtool.h> 11 11 12 12 #define UNSUPPORTED(name) \ 13 13 ({ \ ··· 25 25 UNSUPPORTED("orc"); 26 26 } 27 27 28 - int __weak create_orc(struct objtool_file *file) 29 - { 30 - UNSUPPORTED("orc"); 31 - } 32 - 33 - int __weak create_orc_sections(struct objtool_file *file) 28 + int __weak orc_create(struct objtool_file *file) 34 29 { 35 30 UNSUPPORTED("orc"); 36 31 }