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

objtool, x86: Add facility for asm code to provide unwind hints

Some asm (and inline asm) code does special things to the stack which
objtool can't understand. (Nor can GCC or GNU assembler, for that
matter.) In such cases we need a facility for the code to provide
annotations, so the unwinder can unwind through it.

This provides such a facility, in the form of unwind hints. They're
similar to the GNU assembler .cfi* directives, but they give more
information, and are needed in far fewer places, because objtool can
fill in the blanks by following branches and adjusting the stack pointer
for pushes and pops.

Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
Cc: Andy Lutomirski <luto@kernel.org>
Cc: Borislav Petkov <bp@alien8.de>
Cc: Brian Gerst <brgerst@gmail.com>
Cc: Denys Vlasenko <dvlasenk@redhat.com>
Cc: H. Peter Anvin <hpa@zytor.com>
Cc: Jiri Slaby <jslaby@suse.cz>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Mike Galbraith <efault@gmx.de>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: live-patching@vger.kernel.org
Link: http://lkml.kernel.org/r/0f5f3c9104fca559ff4088bece1d14ae3bca52d5.1499786555.git.jpoimboe@redhat.com
Signed-off-by: Ingo Molnar <mingo@kernel.org>

authored by

Josh Poimboeuf and committed by
Ingo Molnar
39358a03 627fce14

+417 -13
+107
arch/x86/include/asm/orc_types.h
··· 1 + /* 2 + * Copyright (C) 2017 Josh Poimboeuf <jpoimboe@redhat.com> 3 + * 4 + * This program is free software; you can redistribute it and/or 5 + * modify it under the terms of the GNU General Public License 6 + * as published by the Free Software Foundation; either version 2 7 + * of the License, or (at your option) any later version. 8 + * 9 + * This program is distributed in the hope that it will be useful, 10 + * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 + * GNU General Public License for more details. 13 + * 14 + * You should have received a copy of the GNU General Public License 15 + * along with this program; if not, see <http://www.gnu.org/licenses/>. 16 + */ 17 + 18 + #ifndef _ORC_TYPES_H 19 + #define _ORC_TYPES_H 20 + 21 + #include <linux/types.h> 22 + #include <linux/compiler.h> 23 + 24 + /* 25 + * The ORC_REG_* registers are base registers which are used to find other 26 + * registers on the stack. 27 + * 28 + * ORC_REG_PREV_SP, also known as DWARF Call Frame Address (CFA), is the 29 + * address of the previous frame: the caller's SP before it called the current 30 + * function. 31 + * 32 + * ORC_REG_UNDEFINED means the corresponding register's value didn't change in 33 + * the current frame. 34 + * 35 + * The most commonly used base registers are SP and BP -- which the previous SP 36 + * is usually based on -- and PREV_SP and UNDEFINED -- which the previous BP is 37 + * usually based on. 38 + * 39 + * The rest of the base registers are needed for special cases like entry code 40 + * and GCC realigned stacks. 41 + */ 42 + #define ORC_REG_UNDEFINED 0 43 + #define ORC_REG_PREV_SP 1 44 + #define ORC_REG_DX 2 45 + #define ORC_REG_DI 3 46 + #define ORC_REG_BP 4 47 + #define ORC_REG_SP 5 48 + #define ORC_REG_R10 6 49 + #define ORC_REG_R13 7 50 + #define ORC_REG_BP_INDIRECT 8 51 + #define ORC_REG_SP_INDIRECT 9 52 + #define ORC_REG_MAX 15 53 + 54 + /* 55 + * ORC_TYPE_CALL: Indicates that sp_reg+sp_offset resolves to PREV_SP (the 56 + * caller's SP right before it made the call). Used for all callable 57 + * functions, i.e. all C code and all callable asm functions. 58 + * 59 + * ORC_TYPE_REGS: Used in entry code to indicate that sp_reg+sp_offset points 60 + * to a fully populated pt_regs from a syscall, interrupt, or exception. 61 + * 62 + * ORC_TYPE_REGS_IRET: Used in entry code to indicate that sp_reg+sp_offset 63 + * points to the iret return frame. 64 + * 65 + * The UNWIND_HINT macros are used only for the unwind_hint struct. They 66 + * aren't used in struct orc_entry due to size and complexity constraints. 67 + * Objtool converts them to real types when it converts the hints to orc 68 + * entries. 69 + */ 70 + #define ORC_TYPE_CALL 0 71 + #define ORC_TYPE_REGS 1 72 + #define ORC_TYPE_REGS_IRET 2 73 + #define UNWIND_HINT_TYPE_SAVE 3 74 + #define UNWIND_HINT_TYPE_RESTORE 4 75 + 76 + #ifndef __ASSEMBLY__ 77 + /* 78 + * This struct is more or less a vastly simplified version of the DWARF Call 79 + * Frame Information standard. It contains only the necessary parts of DWARF 80 + * CFI, simplified for ease of access by the in-kernel unwinder. It tells the 81 + * unwinder how to find the previous SP and BP (and sometimes entry regs) on 82 + * the stack for a given code address. Each instance of the struct corresponds 83 + * to one or more code locations. 84 + */ 85 + struct orc_entry { 86 + s16 sp_offset; 87 + s16 bp_offset; 88 + unsigned sp_reg:4; 89 + unsigned bp_reg:4; 90 + unsigned type:2; 91 + }; 92 + 93 + /* 94 + * This struct is used by asm and inline asm code to manually annotate the 95 + * location of registers on the stack for the ORC unwinder. 96 + * 97 + * Type can be either ORC_TYPE_* or UNWIND_HINT_TYPE_*. 98 + */ 99 + struct unwind_hint { 100 + u32 ip; 101 + s16 sp_offset; 102 + u8 sp_reg; 103 + u8 type; 104 + }; 105 + #endif /* __ASSEMBLY__ */ 106 + 107 + #endif /* _ORC_TYPES_H */
+103
arch/x86/include/asm/unwind_hints.h
··· 1 + #ifndef _ASM_X86_UNWIND_HINTS_H 2 + #define _ASM_X86_UNWIND_HINTS_H 3 + 4 + #include "orc_types.h" 5 + 6 + #ifdef __ASSEMBLY__ 7 + 8 + /* 9 + * In asm, there are two kinds of code: normal C-type callable functions and 10 + * the rest. The normal callable functions can be called by other code, and 11 + * don't do anything unusual with the stack. Such normal callable functions 12 + * are annotated with the ENTRY/ENDPROC macros. Most asm code falls in this 13 + * category. In this case, no special debugging annotations are needed because 14 + * objtool can automatically generate the ORC data for the ORC unwinder to read 15 + * at runtime. 16 + * 17 + * Anything which doesn't fall into the above category, such as syscall and 18 + * interrupt handlers, tends to not be called directly by other functions, and 19 + * often does unusual non-C-function-type things with the stack pointer. Such 20 + * code needs to be annotated such that objtool can understand it. The 21 + * following CFI hint macros are for this type of code. 22 + * 23 + * These macros provide hints to objtool about the state of the stack at each 24 + * instruction. Objtool starts from the hints and follows the code flow, 25 + * making automatic CFI adjustments when it sees pushes and pops, filling out 26 + * the debuginfo as necessary. It will also warn if it sees any 27 + * inconsistencies. 28 + */ 29 + .macro UNWIND_HINT sp_reg=ORC_REG_SP sp_offset=0 type=ORC_TYPE_CALL 30 + #ifdef CONFIG_STACK_VALIDATION 31 + .Lunwind_hint_ip_\@: 32 + .pushsection .discard.unwind_hints 33 + /* struct unwind_hint */ 34 + .long .Lunwind_hint_ip_\@ - . 35 + .short \sp_offset 36 + .byte \sp_reg 37 + .byte \type 38 + .popsection 39 + #endif 40 + .endm 41 + 42 + .macro UNWIND_HINT_EMPTY 43 + UNWIND_HINT sp_reg=ORC_REG_UNDEFINED 44 + .endm 45 + 46 + .macro UNWIND_HINT_REGS base=%rsp offset=0 indirect=0 extra=1 iret=0 47 + .if \base == %rsp && \indirect 48 + .set sp_reg, ORC_REG_SP_INDIRECT 49 + .elseif \base == %rsp 50 + .set sp_reg, ORC_REG_SP 51 + .elseif \base == %rbp 52 + .set sp_reg, ORC_REG_BP 53 + .elseif \base == %rdi 54 + .set sp_reg, ORC_REG_DI 55 + .elseif \base == %rdx 56 + .set sp_reg, ORC_REG_DX 57 + .elseif \base == %r10 58 + .set sp_reg, ORC_REG_R10 59 + .else 60 + .error "UNWIND_HINT_REGS: bad base register" 61 + .endif 62 + 63 + .set sp_offset, \offset 64 + 65 + .if \iret 66 + .set type, ORC_TYPE_REGS_IRET 67 + .elseif \extra == 0 68 + .set type, ORC_TYPE_REGS_IRET 69 + .set sp_offset, \offset + (16*8) 70 + .else 71 + .set type, ORC_TYPE_REGS 72 + .endif 73 + 74 + UNWIND_HINT sp_reg=sp_reg sp_offset=sp_offset type=type 75 + .endm 76 + 77 + .macro UNWIND_HINT_IRET_REGS base=%rsp offset=0 78 + UNWIND_HINT_REGS base=\base offset=\offset iret=1 79 + .endm 80 + 81 + .macro UNWIND_HINT_FUNC sp_offset=8 82 + UNWIND_HINT sp_offset=\sp_offset 83 + .endm 84 + 85 + #else /* !__ASSEMBLY__ */ 86 + 87 + #define UNWIND_HINT(sp_reg, sp_offset, type) \ 88 + "987: \n\t" \ 89 + ".pushsection .discard.unwind_hints\n\t" \ 90 + /* struct unwind_hint */ \ 91 + ".long 987b - .\n\t" \ 92 + ".short " __stringify(sp_offset) "\n\t" \ 93 + ".byte " __stringify(sp_reg) "\n\t" \ 94 + ".byte " __stringify(type) "\n\t" \ 95 + ".popsection\n\t" 96 + 97 + #define UNWIND_HINT_SAVE UNWIND_HINT(0, 0, UNWIND_HINT_TYPE_SAVE) 98 + 99 + #define UNWIND_HINT_RESTORE UNWIND_HINT(0, 0, UNWIND_HINT_TYPE_RESTORE) 100 + 101 + #endif /* __ASSEMBLY__ */ 102 + 103 + #endif /* _ASM_X86_UNWIND_HINTS_H */
+3
tools/objtool/Makefile
··· 52 52 diff -I'^#include' arch/x86/insn/inat.h ../../arch/x86/include/asm/inat.h >/dev/null && \ 53 53 diff -I'^#include' arch/x86/insn/inat_types.h ../../arch/x86/include/asm/inat_types.h >/dev/null) \ 54 54 || echo "warning: objtool: x86 instruction decoder differs from kernel" >&2 )) || true 55 + @(test -d ../../kernel -a -d ../../tools -a -d ../objtool && (( \ 56 + diff ../../arch/x86/include/asm/orc_types.h orc_types.h >/dev/null) \ 57 + || echo "warning: objtool: orc_types.h differs from kernel" >&2 )) || true 55 58 $(QUIET_LINK)$(CC) $(OBJTOOL_IN) $(LDFLAGS) -o $@ 56 59 57 60
+180 -11
tools/objtool/check.c
··· 100 100 static bool ignore_func(struct objtool_file *file, struct symbol *func) 101 101 { 102 102 struct rela *rela; 103 - struct instruction *insn; 104 103 105 104 /* check for STACK_FRAME_NON_STANDARD */ 106 105 if (file->whitelist && file->whitelist->rela) ··· 111 112 if (rela->sym->type == STT_FUNC && rela->sym == func) 112 113 return true; 113 114 } 114 - 115 - /* check if it has a context switching instruction */ 116 - func_for_each_insn(file, func, insn) 117 - if (insn->type == INSN_CONTEXT_SWITCH) 118 - return true; 119 115 120 116 return false; 121 117 } ··· 873 879 return 0; 874 880 } 875 881 882 + static int read_unwind_hints(struct objtool_file *file) 883 + { 884 + struct section *sec, *relasec; 885 + struct rela *rela; 886 + struct unwind_hint *hint; 887 + struct instruction *insn; 888 + struct cfi_reg *cfa; 889 + int i; 890 + 891 + sec = find_section_by_name(file->elf, ".discard.unwind_hints"); 892 + if (!sec) 893 + return 0; 894 + 895 + relasec = sec->rela; 896 + if (!relasec) { 897 + WARN("missing .rela.discard.unwind_hints section"); 898 + return -1; 899 + } 900 + 901 + if (sec->len % sizeof(struct unwind_hint)) { 902 + WARN("struct unwind_hint size mismatch"); 903 + return -1; 904 + } 905 + 906 + file->hints = true; 907 + 908 + for (i = 0; i < sec->len / sizeof(struct unwind_hint); i++) { 909 + hint = (struct unwind_hint *)sec->data->d_buf + i; 910 + 911 + rela = find_rela_by_dest(sec, i * sizeof(*hint)); 912 + if (!rela) { 913 + WARN("can't find rela for unwind_hints[%d]", i); 914 + return -1; 915 + } 916 + 917 + insn = find_insn(file, rela->sym->sec, rela->addend); 918 + if (!insn) { 919 + WARN("can't find insn for unwind_hints[%d]", i); 920 + return -1; 921 + } 922 + 923 + cfa = &insn->state.cfa; 924 + 925 + if (hint->type == UNWIND_HINT_TYPE_SAVE) { 926 + insn->save = true; 927 + continue; 928 + 929 + } else if (hint->type == UNWIND_HINT_TYPE_RESTORE) { 930 + insn->restore = true; 931 + insn->hint = true; 932 + continue; 933 + } 934 + 935 + insn->hint = true; 936 + 937 + switch (hint->sp_reg) { 938 + case ORC_REG_UNDEFINED: 939 + cfa->base = CFI_UNDEFINED; 940 + break; 941 + case ORC_REG_SP: 942 + cfa->base = CFI_SP; 943 + break; 944 + case ORC_REG_BP: 945 + cfa->base = CFI_BP; 946 + break; 947 + case ORC_REG_SP_INDIRECT: 948 + cfa->base = CFI_SP_INDIRECT; 949 + break; 950 + case ORC_REG_R10: 951 + cfa->base = CFI_R10; 952 + break; 953 + case ORC_REG_R13: 954 + cfa->base = CFI_R13; 955 + break; 956 + case ORC_REG_DI: 957 + cfa->base = CFI_DI; 958 + break; 959 + case ORC_REG_DX: 960 + cfa->base = CFI_DX; 961 + break; 962 + default: 963 + WARN_FUNC("unsupported unwind_hint sp base reg %d", 964 + insn->sec, insn->offset, hint->sp_reg); 965 + return -1; 966 + } 967 + 968 + cfa->offset = hint->sp_offset; 969 + insn->state.type = hint->type; 970 + } 971 + 972 + return 0; 973 + } 974 + 876 975 static int decode_sections(struct objtool_file *file) 877 976 { 878 977 int ret; ··· 993 906 return ret; 994 907 995 908 ret = add_switch_table_alts(file); 909 + if (ret) 910 + return ret; 911 + 912 + ret = read_unwind_hints(file); 996 913 if (ret) 997 914 return ret; 998 915 ··· 1473 1382 struct insn_state state) 1474 1383 { 1475 1384 struct alternative *alt; 1476 - struct instruction *insn; 1385 + struct instruction *insn, *next_insn; 1477 1386 struct section *sec; 1478 1387 struct symbol *func = NULL; 1479 1388 int ret; ··· 1488 1397 } 1489 1398 1490 1399 while (1) { 1400 + next_insn = next_insn_same_sec(file, insn); 1401 + 1491 1402 if (file->c_file && insn->func) { 1492 1403 if (func && func != insn->func) { 1493 1404 WARN("%s() falls through to next function %s()", ··· 1507 1414 } 1508 1415 1509 1416 if (insn->visited) { 1510 - if (!!insn_state_match(insn, &state)) 1417 + if (!insn->hint && !insn_state_match(insn, &state)) 1511 1418 return 1; 1512 1419 1513 1420 return 0; 1514 1421 } 1515 1422 1516 - insn->state = state; 1423 + if (insn->hint) { 1424 + if (insn->restore) { 1425 + struct instruction *save_insn, *i; 1426 + 1427 + i = insn; 1428 + save_insn = NULL; 1429 + func_for_each_insn_continue_reverse(file, func, i) { 1430 + if (i->save) { 1431 + save_insn = i; 1432 + break; 1433 + } 1434 + } 1435 + 1436 + if (!save_insn) { 1437 + WARN_FUNC("no corresponding CFI save for CFI restore", 1438 + sec, insn->offset); 1439 + return 1; 1440 + } 1441 + 1442 + if (!save_insn->visited) { 1443 + /* 1444 + * Oops, no state to copy yet. 1445 + * Hopefully we can reach this 1446 + * instruction from another branch 1447 + * after the save insn has been 1448 + * visited. 1449 + */ 1450 + if (insn == first) 1451 + return 0; 1452 + 1453 + WARN_FUNC("objtool isn't smart enough to handle this CFI save/restore combo", 1454 + sec, insn->offset); 1455 + return 1; 1456 + } 1457 + 1458 + insn->state = save_insn->state; 1459 + } 1460 + 1461 + state = insn->state; 1462 + 1463 + } else 1464 + insn->state = state; 1517 1465 1518 1466 insn->visited = true; 1519 1467 ··· 1631 1497 1632 1498 return 0; 1633 1499 1500 + case INSN_CONTEXT_SWITCH: 1501 + if (func && (!next_insn || !next_insn->hint)) { 1502 + WARN_FUNC("unsupported instruction in callable function", 1503 + sec, insn->offset); 1504 + return 1; 1505 + } 1506 + return 0; 1507 + 1634 1508 case INSN_STACK: 1635 1509 if (update_insn_state(insn, &state)) 1636 1510 return -1; ··· 1652 1510 if (insn->dead_end) 1653 1511 return 0; 1654 1512 1655 - insn = next_insn_same_sec(file, insn); 1513 + insn = next_insn; 1656 1514 if (!insn) { 1657 1515 WARN("%s: unexpected end of section", sec->name); 1658 1516 return 1; ··· 1660 1518 } 1661 1519 1662 1520 return 0; 1521 + } 1522 + 1523 + static int validate_unwind_hints(struct objtool_file *file) 1524 + { 1525 + struct instruction *insn; 1526 + int ret, warnings = 0; 1527 + struct insn_state state; 1528 + 1529 + if (!file->hints) 1530 + return 0; 1531 + 1532 + clear_insn_state(&state); 1533 + 1534 + for_each_insn(file, insn) { 1535 + if (insn->hint && !insn->visited) { 1536 + ret = validate_branch(file, insn, state); 1537 + warnings += ret; 1538 + } 1539 + } 1540 + 1541 + return warnings; 1663 1542 } 1664 1543 1665 1544 static bool is_kasan_insn(struct instruction *insn) ··· 1828 1665 hash_init(file.insn_hash); 1829 1666 file.whitelist = find_section_by_name(file.elf, ".discard.func_stack_frame_non_standard"); 1830 1667 file.rodata = find_section_by_name(file.elf, ".rodata"); 1831 - file.ignore_unreachables = false; 1832 1668 file.c_file = find_section_by_name(file.elf, ".comment"); 1669 + file.ignore_unreachables = false; 1670 + file.hints = false; 1833 1671 1834 1672 arch_initial_func_cfi_state(&initial_func_cfi); 1835 1673 ··· 1843 1679 goto out; 1844 1680 1845 1681 ret = validate_functions(&file); 1682 + if (ret < 0) 1683 + goto out; 1684 + warnings += ret; 1685 + 1686 + ret = validate_unwind_hints(&file); 1846 1687 if (ret < 0) 1847 1688 goto out; 1848 1689 warnings += ret;
+2 -2
tools/objtool/check.h
··· 43 43 unsigned int len; 44 44 unsigned char type; 45 45 unsigned long immediate; 46 - bool alt_group, visited, dead_end, ignore; 46 + bool alt_group, visited, dead_end, ignore, hint, save, restore; 47 47 struct symbol *call_dest; 48 48 struct instruction *jump_dest; 49 49 struct list_head alts; ··· 58 58 struct list_head insn_list; 59 59 DECLARE_HASHTABLE(insn_hash, 16); 60 60 struct section *rodata, *whitelist; 61 - bool ignore_unreachables, c_file; 61 + bool ignore_unreachables, c_file, hints; 62 62 }; 63 63 64 64 int check(const char *objname, bool nofp, bool orc);
+22
tools/objtool/orc_types.h
··· 61 61 * 62 62 * ORC_TYPE_REGS_IRET: Used in entry code to indicate that sp_reg+sp_offset 63 63 * points to the iret return frame. 64 + * 65 + * The UNWIND_HINT macros are used only for the unwind_hint struct. They 66 + * aren't used in struct orc_entry due to size and complexity constraints. 67 + * Objtool converts them to real types when it converts the hints to orc 68 + * entries. 64 69 */ 65 70 #define ORC_TYPE_CALL 0 66 71 #define ORC_TYPE_REGS 1 67 72 #define ORC_TYPE_REGS_IRET 2 73 + #define UNWIND_HINT_TYPE_SAVE 3 74 + #define UNWIND_HINT_TYPE_RESTORE 4 68 75 76 + #ifndef __ASSEMBLY__ 69 77 /* 70 78 * This struct is more or less a vastly simplified version of the DWARF Call 71 79 * Frame Information standard. It contains only the necessary parts of DWARF ··· 89 81 unsigned bp_reg:4; 90 82 unsigned type:2; 91 83 } __packed; 84 + 85 + /* 86 + * This struct is used by asm and inline asm code to manually annotate the 87 + * location of registers on the stack for the ORC unwinder. 88 + * 89 + * Type can be either ORC_TYPE_* or UNWIND_HINT_TYPE_*. 90 + */ 91 + struct unwind_hint { 92 + u32 ip; 93 + s16 sp_offset; 94 + u8 sp_reg; 95 + u8 type; 96 + }; 97 + #endif /* __ASSEMBLY__ */ 92 98 93 99 #endif /* _ORC_TYPES_H */