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