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.h> 10 #include <asm/orc_types.h> 11 12 #include <objtool/check.h> 13 #include <objtool/warn.h> 14 #include <objtool/endianness.h> 15 16 static int init_orc_entry(struct orc_entry *orc, struct cfi_state *cfi, 17 struct instruction *insn) 18 { 19 struct cfi_reg *bp = &cfi->regs[CFI_BP]; 20 21 memset(orc, 0, sizeof(*orc)); 22 23 if (!cfi) { 24 orc->end = 0; 25 orc->sp_reg = ORC_REG_UNDEFINED; 26 return 0; 27 } 28 29 orc->end = cfi->end; 30 orc->signal = cfi->signal; 31 32 if (cfi->cfa.base == CFI_UNDEFINED) { 33 orc->sp_reg = ORC_REG_UNDEFINED; 34 return 0; 35 } 36 37 switch (cfi->cfa.base) { 38 case CFI_SP: 39 orc->sp_reg = ORC_REG_SP; 40 break; 41 case CFI_SP_INDIRECT: 42 orc->sp_reg = ORC_REG_SP_INDIRECT; 43 break; 44 case CFI_BP: 45 orc->sp_reg = ORC_REG_BP; 46 break; 47 case CFI_BP_INDIRECT: 48 orc->sp_reg = ORC_REG_BP_INDIRECT; 49 break; 50 case CFI_R10: 51 orc->sp_reg = ORC_REG_R10; 52 break; 53 case CFI_R13: 54 orc->sp_reg = ORC_REG_R13; 55 break; 56 case CFI_DI: 57 orc->sp_reg = ORC_REG_DI; 58 break; 59 case CFI_DX: 60 orc->sp_reg = ORC_REG_DX; 61 break; 62 default: 63 WARN_FUNC("unknown CFA base reg %d", 64 insn->sec, insn->offset, cfi->cfa.base); 65 return -1; 66 } 67 68 switch (bp->base) { 69 case CFI_UNDEFINED: 70 orc->bp_reg = ORC_REG_UNDEFINED; 71 break; 72 case CFI_CFA: 73 orc->bp_reg = ORC_REG_PREV_SP; 74 break; 75 case CFI_BP: 76 orc->bp_reg = ORC_REG_BP; 77 break; 78 default: 79 WARN_FUNC("unknown BP base reg %d", 80 insn->sec, insn->offset, bp->base); 81 return -1; 82 } 83 84 orc->sp_offset = cfi->cfa.offset; 85 orc->bp_offset = bp->offset; 86 orc->type = cfi->type; 87 88 return 0; 89 } 90 91 static int write_orc_entry(struct elf *elf, struct section *orc_sec, 92 struct section *ip_sec, unsigned int idx, 93 struct section *insn_sec, unsigned long insn_off, 94 struct orc_entry *o) 95 { 96 struct orc_entry *orc; 97 98 /* populate ORC data */ 99 orc = (struct orc_entry *)orc_sec->data->d_buf + idx; 100 memcpy(orc, o, sizeof(*orc)); 101 orc->sp_offset = bswap_if_needed(elf, orc->sp_offset); 102 orc->bp_offset = bswap_if_needed(elf, orc->bp_offset); 103 104 /* populate reloc for ip */ 105 if (elf_add_reloc_to_insn(elf, ip_sec, idx * sizeof(int), R_X86_64_PC32, 106 insn_sec, insn_off)) 107 return -1; 108 109 return 0; 110 } 111 112 struct orc_list_entry { 113 struct list_head list; 114 struct orc_entry orc; 115 struct section *insn_sec; 116 unsigned long insn_off; 117 }; 118 119 static int orc_list_add(struct list_head *orc_list, struct orc_entry *orc, 120 struct section *sec, unsigned long offset) 121 { 122 struct orc_list_entry *entry = malloc(sizeof(*entry)); 123 124 if (!entry) { 125 WARN("malloc failed"); 126 return -1; 127 } 128 129 entry->orc = *orc; 130 entry->insn_sec = sec; 131 entry->insn_off = offset; 132 133 list_add_tail(&entry->list, orc_list); 134 return 0; 135 } 136 137 static unsigned long alt_group_len(struct alt_group *alt_group) 138 { 139 return alt_group->last_insn->offset + 140 alt_group->last_insn->len - 141 alt_group->first_insn->offset; 142 } 143 144 int orc_create(struct objtool_file *file) 145 { 146 struct section *sec, *orc_sec; 147 unsigned int nr = 0, idx = 0; 148 struct orc_list_entry *entry; 149 struct list_head orc_list; 150 151 struct orc_entry null = { 152 .sp_reg = ORC_REG_UNDEFINED, 153 .bp_reg = ORC_REG_UNDEFINED, 154 .type = UNWIND_HINT_TYPE_CALL, 155 }; 156 157 /* Build a deduplicated list of ORC entries: */ 158 INIT_LIST_HEAD(&orc_list); 159 for_each_sec(file, sec) { 160 struct orc_entry orc, prev_orc = {0}; 161 struct instruction *insn; 162 bool empty = true; 163 164 if (!sec->text) 165 continue; 166 167 sec_for_each_insn(file, sec, insn) { 168 struct alt_group *alt_group = insn->alt_group; 169 int i; 170 171 if (!alt_group) { 172 if (init_orc_entry(&orc, insn->cfi, insn)) 173 return -1; 174 if (!memcmp(&prev_orc, &orc, sizeof(orc))) 175 continue; 176 if (orc_list_add(&orc_list, &orc, sec, 177 insn->offset)) 178 return -1; 179 nr++; 180 prev_orc = orc; 181 empty = false; 182 continue; 183 } 184 185 /* 186 * Alternatives can have different stack layout 187 * possibilities (but they shouldn't conflict). 188 * Instead of traversing the instructions, use the 189 * alt_group's flattened byte-offset-addressed CFI 190 * array. 191 */ 192 for (i = 0; i < alt_group_len(alt_group); i++) { 193 struct cfi_state *cfi = alt_group->cfi[i]; 194 if (!cfi) 195 continue; 196 /* errors are reported on the original insn */ 197 if (init_orc_entry(&orc, cfi, insn)) 198 return -1; 199 if (!memcmp(&prev_orc, &orc, sizeof(orc))) 200 continue; 201 if (orc_list_add(&orc_list, &orc, insn->sec, 202 insn->offset + i)) 203 return -1; 204 nr++; 205 prev_orc = orc; 206 empty = false; 207 } 208 209 /* Skip to the end of the alt_group */ 210 insn = alt_group->last_insn; 211 } 212 213 /* Add a section terminator */ 214 if (!empty) { 215 orc_list_add(&orc_list, &null, sec, sec->sh.sh_size); 216 nr++; 217 } 218 } 219 if (!nr) 220 return 0; 221 222 /* Create .orc_unwind, .orc_unwind_ip and .rela.orc_unwind_ip sections: */ 223 sec = find_section_by_name(file->elf, ".orc_unwind"); 224 if (sec) { 225 WARN("file already has .orc_unwind section, skipping"); 226 return -1; 227 } 228 orc_sec = elf_create_section(file->elf, ".orc_unwind", 0, 229 sizeof(struct orc_entry), nr); 230 if (!orc_sec) 231 return -1; 232 233 sec = elf_create_section(file->elf, ".orc_unwind_ip", 0, sizeof(int), nr); 234 if (!sec) 235 return -1; 236 237 /* Write ORC entries to sections: */ 238 list_for_each_entry(entry, &orc_list, list) { 239 if (write_orc_entry(file->elf, orc_sec, sec, idx++, 240 entry->insn_sec, entry->insn_off, 241 &entry->orc)) 242 return -1; 243 } 244 245 return 0; 246 } 247