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 "orc.h" 10 #include "check.h" 11 #include "warn.h" 12 13 int create_orc(struct objtool_file *file) 14 { 15 struct instruction *insn; 16 17 for_each_insn(file, insn) { 18 struct orc_entry *orc = &insn->orc; 19 struct cfi_reg *cfa = &insn->state.cfa; 20 struct cfi_reg *bp = &insn->state.regs[CFI_BP]; 21 22 orc->end = insn->state.end; 23 24 if (cfa->base == CFI_UNDEFINED) { 25 orc->sp_reg = ORC_REG_UNDEFINED; 26 continue; 27 } 28 29 switch (cfa->base) { 30 case CFI_SP: 31 orc->sp_reg = ORC_REG_SP; 32 break; 33 case CFI_SP_INDIRECT: 34 orc->sp_reg = ORC_REG_SP_INDIRECT; 35 break; 36 case CFI_BP: 37 orc->sp_reg = ORC_REG_BP; 38 break; 39 case CFI_BP_INDIRECT: 40 orc->sp_reg = ORC_REG_BP_INDIRECT; 41 break; 42 case CFI_R10: 43 orc->sp_reg = ORC_REG_R10; 44 break; 45 case CFI_R13: 46 orc->sp_reg = ORC_REG_R13; 47 break; 48 case CFI_DI: 49 orc->sp_reg = ORC_REG_DI; 50 break; 51 case CFI_DX: 52 orc->sp_reg = ORC_REG_DX; 53 break; 54 default: 55 WARN_FUNC("unknown CFA base reg %d", 56 insn->sec, insn->offset, cfa->base); 57 return -1; 58 } 59 60 switch(bp->base) { 61 case CFI_UNDEFINED: 62 orc->bp_reg = ORC_REG_UNDEFINED; 63 break; 64 case CFI_CFA: 65 orc->bp_reg = ORC_REG_PREV_SP; 66 break; 67 case CFI_BP: 68 orc->bp_reg = ORC_REG_BP; 69 break; 70 default: 71 WARN_FUNC("unknown BP base reg %d", 72 insn->sec, insn->offset, bp->base); 73 return -1; 74 } 75 76 orc->sp_offset = cfa->offset; 77 orc->bp_offset = bp->offset; 78 orc->type = insn->state.type; 79 } 80 81 return 0; 82 } 83 84 static int create_orc_entry(struct elf *elf, struct section *u_sec, struct section *ip_relasec, 85 unsigned int idx, struct section *insn_sec, 86 unsigned long insn_off, struct orc_entry *o) 87 { 88 struct orc_entry *orc; 89 struct rela *rela; 90 91 /* populate ORC data */ 92 orc = (struct orc_entry *)u_sec->data->d_buf + idx; 93 memcpy(orc, o, sizeof(*orc)); 94 95 /* populate rela for ip */ 96 rela = malloc(sizeof(*rela)); 97 if (!rela) { 98 perror("malloc"); 99 return -1; 100 } 101 memset(rela, 0, sizeof(*rela)); 102 103 if (insn_sec->sym) { 104 rela->sym = insn_sec->sym; 105 rela->addend = insn_off; 106 } else { 107 /* 108 * The Clang assembler doesn't produce section symbols, so we 109 * have to reference the function symbol instead: 110 */ 111 rela->sym = find_symbol_containing(insn_sec, insn_off); 112 if (!rela->sym) { 113 /* 114 * Hack alert. This happens when we need to reference 115 * the NOP pad insn immediately after the function. 116 */ 117 rela->sym = find_symbol_containing(insn_sec, 118 insn_off - 1); 119 } 120 if (!rela->sym) { 121 WARN("missing symbol for insn at offset 0x%lx\n", 122 insn_off); 123 return -1; 124 } 125 126 rela->addend = insn_off - rela->sym->offset; 127 } 128 129 rela->type = R_X86_64_PC32; 130 rela->offset = idx * sizeof(int); 131 rela->sec = ip_relasec; 132 133 list_add_tail(&rela->list, &ip_relasec->rela_list); 134 hash_add(elf->rela_hash, &rela->hash, rela_hash(rela)); 135 136 return 0; 137 } 138 139 int create_orc_sections(struct objtool_file *file) 140 { 141 struct instruction *insn, *prev_insn; 142 struct section *sec, *u_sec, *ip_relasec; 143 unsigned int idx; 144 145 struct orc_entry empty = { 146 .sp_reg = ORC_REG_UNDEFINED, 147 .bp_reg = ORC_REG_UNDEFINED, 148 .type = ORC_TYPE_CALL, 149 }; 150 151 sec = find_section_by_name(file->elf, ".orc_unwind"); 152 if (sec) { 153 WARN("file already has .orc_unwind section, skipping"); 154 return -1; 155 } 156 157 /* count the number of needed orcs */ 158 idx = 0; 159 for_each_sec(file, sec) { 160 if (!sec->text) 161 continue; 162 163 prev_insn = NULL; 164 sec_for_each_insn(file, sec, insn) { 165 if (!prev_insn || 166 memcmp(&insn->orc, &prev_insn->orc, 167 sizeof(struct orc_entry))) { 168 idx++; 169 } 170 prev_insn = insn; 171 } 172 173 /* section terminator */ 174 if (prev_insn) 175 idx++; 176 } 177 if (!idx) 178 return -1; 179 180 181 /* create .orc_unwind_ip and .rela.orc_unwind_ip sections */ 182 sec = elf_create_section(file->elf, ".orc_unwind_ip", sizeof(int), idx); 183 if (!sec) 184 return -1; 185 186 ip_relasec = elf_create_rela_section(file->elf, sec); 187 if (!ip_relasec) 188 return -1; 189 190 /* create .orc_unwind section */ 191 u_sec = elf_create_section(file->elf, ".orc_unwind", 192 sizeof(struct orc_entry), idx); 193 194 /* populate sections */ 195 idx = 0; 196 for_each_sec(file, sec) { 197 if (!sec->text) 198 continue; 199 200 prev_insn = NULL; 201 sec_for_each_insn(file, sec, insn) { 202 if (!prev_insn || memcmp(&insn->orc, &prev_insn->orc, 203 sizeof(struct orc_entry))) { 204 205 if (create_orc_entry(file->elf, u_sec, ip_relasec, idx, 206 insn->sec, insn->offset, 207 &insn->orc)) 208 return -1; 209 210 idx++; 211 } 212 prev_insn = insn; 213 } 214 215 /* section terminator */ 216 if (prev_insn) { 217 if (create_orc_entry(file->elf, u_sec, ip_relasec, idx, 218 prev_insn->sec, 219 prev_insn->offset + prev_insn->len, 220 &empty)) 221 return -1; 222 223 idx++; 224 } 225 } 226 227 if (elf_rebuild_rela_section(ip_relasec)) 228 return -1; 229 230 return 0; 231 } 232