1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * Copyright (C) 2017 Josh Poimboeuf <jpoimboe@redhat.com> 4 */ 5 6 #include <stdlib.h> 7 #include <string.h> 8 9 #include <linux/objtool_types.h> 10 #include <asm/orc_types.h> 11 12 #include <objtool/check.h> 13 #include <objtool/orc.h> 14 #include <objtool/warn.h> 15 #include <objtool/endianness.h> 16 17 struct orc_list_entry { 18 struct list_head list; 19 struct orc_entry orc; 20 struct section *insn_sec; 21 unsigned long insn_off; 22 }; 23 24 static int orc_list_add(struct list_head *orc_list, struct orc_entry *orc, 25 struct section *sec, unsigned long offset) 26 { 27 struct orc_list_entry *entry = malloc(sizeof(*entry)); 28 29 if (!entry) { 30 WARN("malloc failed"); 31 return -1; 32 } 33 34 entry->orc = *orc; 35 entry->insn_sec = sec; 36 entry->insn_off = offset; 37 38 list_add_tail(&entry->list, orc_list); 39 return 0; 40 } 41 42 static unsigned long alt_group_len(struct alt_group *alt_group) 43 { 44 return alt_group->last_insn->offset + 45 alt_group->last_insn->len - 46 alt_group->first_insn->offset; 47 } 48 49 int orc_create(struct objtool_file *file) 50 { 51 struct section *sec, *orc_sec; 52 unsigned int nr = 0, idx = 0; 53 struct orc_list_entry *entry; 54 struct list_head orc_list; 55 56 struct orc_entry null = { .type = ORC_TYPE_UNDEFINED }; 57 58 /* Build a deduplicated list of ORC entries: */ 59 INIT_LIST_HEAD(&orc_list); 60 for_each_sec(file, sec) { 61 struct orc_entry orc, prev_orc = {0}; 62 struct instruction *insn; 63 bool empty = true; 64 65 if (!sec->text) 66 continue; 67 68 sec_for_each_insn(file, sec, insn) { 69 struct alt_group *alt_group = insn->alt_group; 70 int i; 71 72 if (!alt_group) { 73 if (init_orc_entry(&orc, insn->cfi, insn)) 74 return -1; 75 if (!memcmp(&prev_orc, &orc, sizeof(orc))) 76 continue; 77 if (orc_list_add(&orc_list, &orc, sec, 78 insn->offset)) 79 return -1; 80 nr++; 81 prev_orc = orc; 82 empty = false; 83 continue; 84 } 85 86 /* 87 * Alternatives can have different stack layout 88 * possibilities (but they shouldn't conflict). 89 * Instead of traversing the instructions, use the 90 * alt_group's flattened byte-offset-addressed CFI 91 * array. 92 */ 93 for (i = 0; i < alt_group_len(alt_group); i++) { 94 struct cfi_state *cfi = alt_group->cfi[i]; 95 if (!cfi) 96 continue; 97 /* errors are reported on the original insn */ 98 if (init_orc_entry(&orc, cfi, insn)) 99 return -1; 100 if (!memcmp(&prev_orc, &orc, sizeof(orc))) 101 continue; 102 if (orc_list_add(&orc_list, &orc, insn->sec, 103 insn->offset + i)) 104 return -1; 105 nr++; 106 prev_orc = orc; 107 empty = false; 108 } 109 110 /* Skip to the end of the alt_group */ 111 insn = alt_group->last_insn; 112 } 113 114 /* Add a section terminator */ 115 if (!empty) { 116 orc_list_add(&orc_list, &null, sec, sec->sh.sh_size); 117 nr++; 118 } 119 } 120 if (!nr) 121 return 0; 122 123 /* Create .orc_unwind, .orc_unwind_ip and .rela.orc_unwind_ip sections: */ 124 sec = find_section_by_name(file->elf, ".orc_unwind"); 125 if (sec) { 126 WARN("file already has .orc_unwind section, skipping"); 127 return -1; 128 } 129 orc_sec = elf_create_section(file->elf, ".orc_unwind", 130 sizeof(struct orc_entry), nr); 131 if (!orc_sec) 132 return -1; 133 134 sec = elf_create_section_pair(file->elf, ".orc_unwind_ip", sizeof(int), nr, nr); 135 if (!sec) 136 return -1; 137 138 /* Write ORC entries to sections: */ 139 list_for_each_entry(entry, &orc_list, list) { 140 if (write_orc_entry(file->elf, orc_sec, sec, idx++, 141 entry->insn_sec, entry->insn_off, 142 &entry->orc)) 143 return -1; 144 } 145 146 return 0; 147 } 148