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 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 if (!insn_sec->sym) { 92 WARN("missing symbol for section %s", insn_sec->name); 93 return -1; 94 } 95 96 /* populate ORC data */ 97 orc = (struct orc_entry *)u_sec->data->d_buf + idx; 98 memcpy(orc, o, sizeof(*orc)); 99 100 /* populate rela for ip */ 101 rela = malloc(sizeof(*rela)); 102 if (!rela) { 103 perror("malloc"); 104 return -1; 105 } 106 memset(rela, 0, sizeof(*rela)); 107 108 rela->sym = insn_sec->sym; 109 rela->addend = insn_off; 110 rela->type = R_X86_64_PC32; 111 rela->offset = idx * sizeof(int); 112 113 list_add_tail(&rela->list, &ip_relasec->rela_list); 114 hash_add(ip_relasec->rela_hash, &rela->hash, rela->offset); 115 116 return 0; 117 } 118 119 int create_orc_sections(struct objtool_file *file) 120 { 121 struct instruction *insn, *prev_insn; 122 struct section *sec, *u_sec, *ip_relasec; 123 unsigned int idx; 124 125 struct orc_entry empty = { 126 .sp_reg = ORC_REG_UNDEFINED, 127 .bp_reg = ORC_REG_UNDEFINED, 128 .type = ORC_TYPE_CALL, 129 }; 130 131 sec = find_section_by_name(file->elf, ".orc_unwind"); 132 if (sec) { 133 WARN("file already has .orc_unwind section, skipping"); 134 return -1; 135 } 136 137 /* count the number of needed orcs */ 138 idx = 0; 139 for_each_sec(file, sec) { 140 if (!sec->text) 141 continue; 142 143 prev_insn = NULL; 144 sec_for_each_insn(file, sec, insn) { 145 if (!prev_insn || 146 memcmp(&insn->orc, &prev_insn->orc, 147 sizeof(struct orc_entry))) { 148 idx++; 149 } 150 prev_insn = insn; 151 } 152 153 /* section terminator */ 154 if (prev_insn) 155 idx++; 156 } 157 if (!idx) 158 return -1; 159 160 161 /* create .orc_unwind_ip and .rela.orc_unwind_ip sections */ 162 sec = elf_create_section(file->elf, ".orc_unwind_ip", sizeof(int), idx); 163 if (!sec) 164 return -1; 165 166 ip_relasec = elf_create_rela_section(file->elf, sec); 167 if (!ip_relasec) 168 return -1; 169 170 /* create .orc_unwind section */ 171 u_sec = elf_create_section(file->elf, ".orc_unwind", 172 sizeof(struct orc_entry), idx); 173 174 /* populate sections */ 175 idx = 0; 176 for_each_sec(file, sec) { 177 if (!sec->text) 178 continue; 179 180 prev_insn = NULL; 181 sec_for_each_insn(file, sec, insn) { 182 if (!prev_insn || memcmp(&insn->orc, &prev_insn->orc, 183 sizeof(struct orc_entry))) { 184 185 if (create_orc_entry(u_sec, ip_relasec, idx, 186 insn->sec, insn->offset, 187 &insn->orc)) 188 return -1; 189 190 idx++; 191 } 192 prev_insn = insn; 193 } 194 195 /* section terminator */ 196 if (prev_insn) { 197 if (create_orc_entry(u_sec, ip_relasec, idx, 198 prev_insn->sec, 199 prev_insn->offset + prev_insn->len, 200 &empty)) 201 return -1; 202 203 idx++; 204 } 205 } 206 207 if (elf_rebuild_rela_section(ip_relasec)) 208 return -1; 209 210 return 0; 211 } 212