aboutsummaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
authorJosh Poimboeuf <jpoimboe@redhat.com>2017-07-11 11:33:43 -0400
committerIngo Molnar <mingo@kernel.org>2017-07-18 04:57:44 -0400
commit39358a033b2e4432052265c1fa0f36f572d8cfb5 (patch)
tree24fb37d89e27976869fae11325f9758e09de00be /tools
parent627fce14809ba5610b0cb476cd0186d3fcedecfc (diff)
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>
Diffstat (limited to 'tools')
-rw-r--r--tools/objtool/Makefile3
-rw-r--r--tools/objtool/check.c191
-rw-r--r--tools/objtool/check.h4
-rw-r--r--tools/objtool/orc_types.h22
4 files changed, 207 insertions, 13 deletions
diff --git a/tools/objtool/Makefile b/tools/objtool/Makefile
index 0e2765e243c0..3a6425fefc43 100644
--- a/tools/objtool/Makefile
+++ b/tools/objtool/Makefile
@@ -52,6 +52,9 @@ $(OBJTOOL): $(LIBSUBCMD) $(OBJTOOL_IN)
52 diff -I'^#include' arch/x86/insn/inat.h ../../arch/x86/include/asm/inat.h >/dev/null && \ 52 diff -I'^#include' arch/x86/insn/inat.h ../../arch/x86/include/asm/inat.h >/dev/null && \
53 diff -I'^#include' arch/x86/insn/inat_types.h ../../arch/x86/include/asm/inat_types.h >/dev/null) \ 53 diff -I'^#include' arch/x86/insn/inat_types.h ../../arch/x86/include/asm/inat_types.h >/dev/null) \
54 || echo "warning: objtool: x86 instruction decoder differs from kernel" >&2 )) || true 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 $(QUIET_LINK)$(CC) $(OBJTOOL_IN) $(LDFLAGS) -o $@ 58 $(QUIET_LINK)$(CC) $(OBJTOOL_IN) $(LDFLAGS) -o $@
56 59
57 60
diff --git a/tools/objtool/check.c b/tools/objtool/check.c
index cb57c526ba17..368275de5f23 100644
--- a/tools/objtool/check.c
+++ b/tools/objtool/check.c
@@ -100,7 +100,6 @@ static bool gcov_enabled(struct objtool_file *file)
100static bool ignore_func(struct objtool_file *file, struct symbol *func) 100static bool ignore_func(struct objtool_file *file, struct symbol *func)
101{ 101{
102 struct rela *rela; 102 struct rela *rela;
103 struct instruction *insn;
104 103
105 /* check for STACK_FRAME_NON_STANDARD */ 104 /* check for STACK_FRAME_NON_STANDARD */
106 if (file->whitelist && file->whitelist->rela) 105 if (file->whitelist && file->whitelist->rela)
@@ -113,11 +112,6 @@ static bool ignore_func(struct objtool_file *file, struct symbol *func)
113 return true; 112 return true;
114 } 113 }
115 114
116 /* check if it has a context switching instruction */
117 func_for_each_insn(file, func, insn)
118 if (insn->type == INSN_CONTEXT_SWITCH)
119 return true;
120
121 return false; 115 return false;
122} 116}
123 117
@@ -879,6 +873,99 @@ static int add_switch_table_alts(struct objtool_file *file)
879 return 0; 873 return 0;
880} 874}
881 875
876static int read_unwind_hints(struct objtool_file *file)
877{
878 struct section *sec, *relasec;
879 struct rela *rela;
880 struct unwind_hint *hint;
881 struct instruction *insn;
882 struct cfi_reg *cfa;
883 int i;
884
885 sec = find_section_by_name(file->elf, ".discard.unwind_hints");
886 if (!sec)
887 return 0;
888
889 relasec = sec->rela;
890 if (!relasec) {
891 WARN("missing .rela.discard.unwind_hints section");
892 return -1;
893 }
894
895 if (sec->len % sizeof(struct unwind_hint)) {
896 WARN("struct unwind_hint size mismatch");
897 return -1;
898 }
899
900 file->hints = true;
901
902 for (i = 0; i < sec->len / sizeof(struct unwind_hint); i++) {
903 hint = (struct unwind_hint *)sec->data->d_buf + i;
904
905 rela = find_rela_by_dest(sec, i * sizeof(*hint));
906 if (!rela) {
907 WARN("can't find rela for unwind_hints[%d]", i);
908 return -1;
909 }
910
911 insn = find_insn(file, rela->sym->sec, rela->addend);
912 if (!insn) {
913 WARN("can't find insn for unwind_hints[%d]", i);
914 return -1;
915 }
916
917 cfa = &insn->state.cfa;
918
919 if (hint->type == UNWIND_HINT_TYPE_SAVE) {
920 insn->save = true;
921 continue;
922
923 } else if (hint->type == UNWIND_HINT_TYPE_RESTORE) {
924 insn->restore = true;
925 insn->hint = true;
926 continue;
927 }
928
929 insn->hint = true;
930
931 switch (hint->sp_reg) {
932 case ORC_REG_UNDEFINED:
933 cfa->base = CFI_UNDEFINED;
934 break;
935 case ORC_REG_SP:
936 cfa->base = CFI_SP;
937 break;
938 case ORC_REG_BP:
939 cfa->base = CFI_BP;
940 break;
941 case ORC_REG_SP_INDIRECT:
942 cfa->base = CFI_SP_INDIRECT;
943 break;
944 case ORC_REG_R10:
945 cfa->base = CFI_R10;
946 break;
947 case ORC_REG_R13:
948 cfa->base = CFI_R13;
949 break;
950 case ORC_REG_DI:
951 cfa->base = CFI_DI;
952 break;
953 case ORC_REG_DX:
954 cfa->base = CFI_DX;
955 break;
956 default:
957 WARN_FUNC("unsupported unwind_hint sp base reg %d",
958 insn->sec, insn->offset, hint->sp_reg);
959 return -1;
960 }
961
962 cfa->offset = hint->sp_offset;
963 insn->state.type = hint->type;
964 }
965
966 return 0;
967}
968
882static int decode_sections(struct objtool_file *file) 969static int decode_sections(struct objtool_file *file)
883{ 970{
884 int ret; 971 int ret;
@@ -909,6 +996,10 @@ static int decode_sections(struct objtool_file *file)
909 if (ret) 996 if (ret)
910 return ret; 997 return ret;
911 998
999 ret = read_unwind_hints(file);
1000 if (ret)
1001 return ret;
1002
912 return 0; 1003 return 0;
913} 1004}
914 1005
@@ -1382,7 +1473,7 @@ static int validate_branch(struct objtool_file *file, struct instruction *first,
1382 struct insn_state state) 1473 struct insn_state state)
1383{ 1474{
1384 struct alternative *alt; 1475 struct alternative *alt;
1385 struct instruction *insn; 1476 struct instruction *insn, *next_insn;
1386 struct section *sec; 1477 struct section *sec;
1387 struct symbol *func = NULL; 1478 struct symbol *func = NULL;
1388 int ret; 1479 int ret;
@@ -1397,6 +1488,8 @@ static int validate_branch(struct objtool_file *file, struct instruction *first,
1397 } 1488 }
1398 1489
1399 while (1) { 1490 while (1) {
1491 next_insn = next_insn_same_sec(file, insn);
1492
1400 if (file->c_file && insn->func) { 1493 if (file->c_file && insn->func) {
1401 if (func && func != insn->func) { 1494 if (func && func != insn->func) {
1402 WARN("%s() falls through to next function %s()", 1495 WARN("%s() falls through to next function %s()",
@@ -1414,13 +1507,54 @@ static int validate_branch(struct objtool_file *file, struct instruction *first,
1414 } 1507 }
1415 1508
1416 if (insn->visited) { 1509 if (insn->visited) {
1417 if (!!insn_state_match(insn, &state)) 1510 if (!insn->hint && !insn_state_match(insn, &state))
1418 return 1; 1511 return 1;
1419 1512
1420 return 0; 1513 return 0;
1421 } 1514 }
1422 1515
1423 insn->state = state; 1516 if (insn->hint) {
1517 if (insn->restore) {
1518 struct instruction *save_insn, *i;
1519
1520 i = insn;
1521 save_insn = NULL;
1522 func_for_each_insn_continue_reverse(file, func, i) {
1523 if (i->save) {
1524 save_insn = i;
1525 break;
1526 }
1527 }
1528
1529 if (!save_insn) {
1530 WARN_FUNC("no corresponding CFI save for CFI restore",
1531 sec, insn->offset);
1532 return 1;
1533 }
1534
1535 if (!save_insn->visited) {
1536 /*
1537 * Oops, no state to copy yet.
1538 * Hopefully we can reach this
1539 * instruction from another branch
1540 * after the save insn has been
1541 * visited.
1542 */
1543 if (insn == first)
1544 return 0;
1545
1546 WARN_FUNC("objtool isn't smart enough to handle this CFI save/restore combo",
1547 sec, insn->offset);
1548 return 1;
1549 }
1550
1551 insn->state = save_insn->state;
1552 }
1553
1554 state = insn->state;
1555
1556 } else
1557 insn->state = state;
1424 1558
1425 insn->visited = true; 1559 insn->visited = true;
1426 1560
@@ -1497,6 +1631,14 @@ static int validate_branch(struct objtool_file *file, struct instruction *first,
1497 1631
1498 return 0; 1632 return 0;
1499 1633
1634 case INSN_CONTEXT_SWITCH:
1635 if (func && (!next_insn || !next_insn->hint)) {
1636 WARN_FUNC("unsupported instruction in callable function",
1637 sec, insn->offset);
1638 return 1;
1639 }
1640 return 0;
1641
1500 case INSN_STACK: 1642 case INSN_STACK:
1501 if (update_insn_state(insn, &state)) 1643 if (update_insn_state(insn, &state))
1502 return -1; 1644 return -1;
@@ -1510,7 +1652,7 @@ static int validate_branch(struct objtool_file *file, struct instruction *first,
1510 if (insn->dead_end) 1652 if (insn->dead_end)
1511 return 0; 1653 return 0;
1512 1654
1513 insn = next_insn_same_sec(file, insn); 1655 insn = next_insn;
1514 if (!insn) { 1656 if (!insn) {
1515 WARN("%s: unexpected end of section", sec->name); 1657 WARN("%s: unexpected end of section", sec->name);
1516 return 1; 1658 return 1;
@@ -1520,6 +1662,27 @@ static int validate_branch(struct objtool_file *file, struct instruction *first,
1520 return 0; 1662 return 0;
1521} 1663}
1522 1664
1665static int validate_unwind_hints(struct objtool_file *file)
1666{
1667 struct instruction *insn;
1668 int ret, warnings = 0;
1669 struct insn_state state;
1670
1671 if (!file->hints)
1672 return 0;
1673
1674 clear_insn_state(&state);
1675
1676 for_each_insn(file, insn) {
1677 if (insn->hint && !insn->visited) {
1678 ret = validate_branch(file, insn, state);
1679 warnings += ret;
1680 }
1681 }
1682
1683 return warnings;
1684}
1685
1523static bool is_kasan_insn(struct instruction *insn) 1686static bool is_kasan_insn(struct instruction *insn)
1524{ 1687{
1525 return (insn->type == INSN_CALL && 1688 return (insn->type == INSN_CALL &&
@@ -1665,8 +1828,9 @@ int check(const char *_objname, bool _nofp, bool orc)
1665 hash_init(file.insn_hash); 1828 hash_init(file.insn_hash);
1666 file.whitelist = find_section_by_name(file.elf, ".discard.func_stack_frame_non_standard"); 1829 file.whitelist = find_section_by_name(file.elf, ".discard.func_stack_frame_non_standard");
1667 file.rodata = find_section_by_name(file.elf, ".rodata"); 1830 file.rodata = find_section_by_name(file.elf, ".rodata");
1668 file.ignore_unreachables = false;
1669 file.c_file = find_section_by_name(file.elf, ".comment"); 1831 file.c_file = find_section_by_name(file.elf, ".comment");
1832 file.ignore_unreachables = false;
1833 file.hints = false;
1670 1834
1671 arch_initial_func_cfi_state(&initial_func_cfi); 1835 arch_initial_func_cfi_state(&initial_func_cfi);
1672 1836
@@ -1683,6 +1847,11 @@ int check(const char *_objname, bool _nofp, bool orc)
1683 goto out; 1847 goto out;
1684 warnings += ret; 1848 warnings += ret;
1685 1849
1850 ret = validate_unwind_hints(&file);
1851 if (ret < 0)
1852 goto out;
1853 warnings += ret;
1854
1686 if (!warnings) { 1855 if (!warnings) {
1687 ret = validate_reachable_instructions(&file); 1856 ret = validate_reachable_instructions(&file);
1688 if (ret < 0) 1857 if (ret < 0)
diff --git a/tools/objtool/check.h b/tools/objtool/check.h
index 046874bbe226..ac3d4b13f17b 100644
--- a/tools/objtool/check.h
+++ b/tools/objtool/check.h
@@ -43,7 +43,7 @@ struct instruction {
43 unsigned int len; 43 unsigned int len;
44 unsigned char type; 44 unsigned char type;
45 unsigned long immediate; 45 unsigned long immediate;
46 bool alt_group, visited, dead_end, ignore; 46 bool alt_group, visited, dead_end, ignore, hint, save, restore;
47 struct symbol *call_dest; 47 struct symbol *call_dest;
48 struct instruction *jump_dest; 48 struct instruction *jump_dest;
49 struct list_head alts; 49 struct list_head alts;
@@ -58,7 +58,7 @@ struct objtool_file {
58 struct list_head insn_list; 58 struct list_head insn_list;
59 DECLARE_HASHTABLE(insn_hash, 16); 59 DECLARE_HASHTABLE(insn_hash, 16);
60 struct section *rodata, *whitelist; 60 struct section *rodata, *whitelist;
61 bool ignore_unreachables, c_file; 61 bool ignore_unreachables, c_file, hints;
62}; 62};
63 63
64int check(const char *objname, bool nofp, bool orc); 64int check(const char *objname, bool nofp, bool orc);
diff --git a/tools/objtool/orc_types.h b/tools/objtool/orc_types.h
index fc5cf6cffd9a..9c9dc579bd7d 100644
--- a/tools/objtool/orc_types.h
+++ b/tools/objtool/orc_types.h
@@ -61,11 +61,19 @@
61 * 61 *
62 * ORC_TYPE_REGS_IRET: Used in entry code to indicate that sp_reg+sp_offset 62 * ORC_TYPE_REGS_IRET: Used in entry code to indicate that sp_reg+sp_offset
63 * points to the iret return frame. 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#define ORC_TYPE_CALL 0 70#define ORC_TYPE_CALL 0
66#define ORC_TYPE_REGS 1 71#define ORC_TYPE_REGS 1
67#define ORC_TYPE_REGS_IRET 2 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 * This struct is more or less a vastly simplified version of the DWARF Call 78 * This struct is more or less a vastly simplified version of the DWARF Call
71 * Frame Information standard. It contains only the necessary parts of DWARF 79 * Frame Information standard. It contains only the necessary parts of DWARF
@@ -82,4 +90,18 @@ struct orc_entry {
82 unsigned type:2; 90 unsigned type:2;
83} __packed; 91} __packed;
84 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 */
99struct unwind_hint {
100 u32 ip;
101 s16 sp_offset;
102 u8 sp_reg;
103 u8 type;
104};
105#endif /* __ASSEMBLY__ */
106
85#endif /* _ORC_TYPES_H */ 107#endif /* _ORC_TYPES_H */