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->cfi.cfa; 20 struct cfi_reg *bp = &insn->cfi.regs[CFI_BP]; 21 22 orc->end = insn->cfi.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->cfi.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 elf_add_rela(elf, rela); 134 135 return 0; 136 } 137 138 int create_orc_sections(struct objtool_file *file) 139 { 140 struct instruction *insn, *prev_insn; 141 struct section *sec, *u_sec, *ip_relasec; 142 unsigned int idx; 143 144 struct orc_entry empty = { 145 .sp_reg = ORC_REG_UNDEFINED, 146 .bp_reg = ORC_REG_UNDEFINED, 147 .type = ORC_TYPE_CALL, 148 }; 149 150 sec = find_section_by_name(file->elf, ".orc_unwind"); 151 if (sec) { 152 WARN("file already has .orc_unwind section, skipping"); 153 return -1; 154 } 155 156 /* count the number of needed orcs */ 157 idx = 0; 158 for_each_sec(file, sec) { 159 if (!sec->text) 160 continue; 161 162 prev_insn = NULL; 163 sec_for_each_insn(file, sec, insn) { 164 if (!prev_insn || 165 memcmp(&insn->orc, &prev_insn->orc, 166 sizeof(struct orc_entry))) { 167 idx++; 168 } 169 prev_insn = insn; 170 } 171 172 /* section terminator */ 173 if (prev_insn) 174 idx++; 175 } 176 if (!idx) 177 return -1; 178 179 180 /* create .orc_unwind_ip and .rela.orc_unwind_ip sections */ 181 sec = elf_create_section(file->elf, ".orc_unwind_ip", sizeof(int), idx); 182 if (!sec) 183 return -1; 184 185 ip_relasec = elf_create_rela_section(file->elf, sec); 186 if (!ip_relasec) 187 return -1; 188 189 /* create .orc_unwind section */ 190 u_sec = elf_create_section(file->elf, ".orc_unwind", 191 sizeof(struct orc_entry), idx); 192 193 /* populate sections */ 194 idx = 0; 195 for_each_sec(file, sec) { 196 if (!sec->text) 197 continue; 198 199 prev_insn = NULL; 200 sec_for_each_insn(file, sec, insn) { 201 if (!prev_insn || memcmp(&insn->orc, &prev_insn->orc, 202 sizeof(struct orc_entry))) { 203 204 if (create_orc_entry(file->elf, u_sec, ip_relasec, idx, 205 insn->sec, insn->offset, 206 &insn->orc)) 207 return -1; 208 209 idx++; 210 } 211 prev_insn = insn; 212 } 213 214 /* section terminator */ 215 if (prev_insn) { 216 if (create_orc_entry(file->elf, u_sec, ip_relasec, idx, 217 prev_insn->sec, 218 prev_insn->offset + prev_insn->len, 219 &empty)) 220 return -1; 221 222 idx++; 223 } 224 } 225 226 if (elf_rebuild_rela_section(ip_relasec)) 227 return -1; 228 229 return 0; 230 } 231