1 /* 2 * Copyright (C) 2017 Josh Poimboeuf <jpoimboe@redhat.com> 3 * 4 * This program is free software; you can redistribute it and/or 5 * modify it under the terms of the GNU General Public License 6 * as published by the Free Software Foundation; either version 2 7 * of the License, or (at your option) any later version. 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with this program; if not, see <http://www.gnu.org/licenses/>. 16 */ 17 18 #include <stdlib.h> 19 #include <string.h> 20 21 #include "orc.h" 22 #include "check.h" 23 #include "warn.h" 24 25 int create_orc(struct objtool_file *file) 26 { 27 struct instruction *insn; 28 29 for_each_insn(file, insn) { 30 struct orc_entry *orc = &insn->orc; 31 struct cfi_reg *cfa = &insn->state.cfa; 32 struct cfi_reg *bp = &insn->state.regs[CFI_BP]; 33 34 orc->end = insn->state.end; 35 36 if (cfa->base == CFI_UNDEFINED) { 37 orc->sp_reg = ORC_REG_UNDEFINED; 38 continue; 39 } 40 41 switch (cfa->base) { 42 case CFI_SP: 43 orc->sp_reg = ORC_REG_SP; 44 break; 45 case CFI_SP_INDIRECT: 46 orc->sp_reg = ORC_REG_SP_INDIRECT; 47 break; 48 case CFI_BP: 49 orc->sp_reg = ORC_REG_BP; 50 break; 51 case CFI_BP_INDIRECT: 52 orc->sp_reg = ORC_REG_BP_INDIRECT; 53 break; 54 case CFI_R10: 55 orc->sp_reg = ORC_REG_R10; 56 break; 57 case CFI_R13: 58 orc->sp_reg = ORC_REG_R13; 59 break; 60 case CFI_DI: 61 orc->sp_reg = ORC_REG_DI; 62 break; 63 case CFI_DX: 64 orc->sp_reg = ORC_REG_DX; 65 break; 66 default: 67 WARN_FUNC("unknown CFA base reg %d", 68 insn->sec, insn->offset, cfa->base); 69 return -1; 70 } 71 72 switch(bp->base) { 73 case CFI_UNDEFINED: 74 orc->bp_reg = ORC_REG_UNDEFINED; 75 break; 76 case CFI_CFA: 77 orc->bp_reg = ORC_REG_PREV_SP; 78 break; 79 case CFI_BP: 80 orc->bp_reg = ORC_REG_BP; 81 break; 82 default: 83 WARN_FUNC("unknown BP base reg %d", 84 insn->sec, insn->offset, bp->base); 85 return -1; 86 } 87 88 orc->sp_offset = cfa->offset; 89 orc->bp_offset = bp->offset; 90 orc->type = insn->state.type; 91 } 92 93 return 0; 94 } 95 96 static int create_orc_entry(struct section *u_sec, struct section *ip_relasec, 97 unsigned int idx, struct section *insn_sec, 98 unsigned long insn_off, struct orc_entry *o) 99 { 100 struct orc_entry *orc; 101 struct rela *rela; 102 103 if (!insn_sec->sym) { 104 WARN("missing symbol for section %s", insn_sec->name); 105 return -1; 106 } 107 108 /* populate ORC data */ 109 orc = (struct orc_entry *)u_sec->data->d_buf + idx; 110 memcpy(orc, o, sizeof(*orc)); 111 112 /* populate rela for ip */ 113 rela = malloc(sizeof(*rela)); 114 if (!rela) { 115 perror("malloc"); 116 return -1; 117 } 118 memset(rela, 0, sizeof(*rela)); 119 120 rela->sym = insn_sec->sym; 121 rela->addend = insn_off; 122 rela->type = R_X86_64_PC32; 123 rela->offset = idx * sizeof(int); 124 125 list_add_tail(&rela->list, &ip_relasec->rela_list); 126 hash_add(ip_relasec->rela_hash, &rela->hash, rela->offset); 127 128 return 0; 129 } 130 131 int create_orc_sections(struct objtool_file *file) 132 { 133 struct instruction *insn, *prev_insn; 134 struct section *sec, *u_sec, *ip_relasec; 135 unsigned int idx; 136 137 struct orc_entry empty = { 138 .sp_reg = ORC_REG_UNDEFINED, 139 .bp_reg = ORC_REG_UNDEFINED, 140 .type = ORC_TYPE_CALL, 141 }; 142 143 sec = find_section_by_name(file->elf, ".orc_unwind"); 144 if (sec) { 145 WARN("file already has .orc_unwind section, skipping"); 146 return -1; 147 } 148 149 /* count the number of needed orcs */ 150 idx = 0; 151 for_each_sec(file, sec) { 152 if (!sec->text) 153 continue; 154 155 prev_insn = NULL; 156 sec_for_each_insn(file, sec, insn) { 157 if (!prev_insn || 158 memcmp(&insn->orc, &prev_insn->orc, 159 sizeof(struct orc_entry))) { 160 idx++; 161 } 162 prev_insn = insn; 163 } 164 165 /* section terminator */ 166 if (prev_insn) 167 idx++; 168 } 169 if (!idx) 170 return -1; 171 172 173 /* create .orc_unwind_ip and .rela.orc_unwind_ip sections */ 174 sec = elf_create_section(file->elf, ".orc_unwind_ip", sizeof(int), idx); 175 if (!sec) 176 return -1; 177 178 ip_relasec = elf_create_rela_section(file->elf, sec); 179 if (!ip_relasec) 180 return -1; 181 182 /* create .orc_unwind section */ 183 u_sec = elf_create_section(file->elf, ".orc_unwind", 184 sizeof(struct orc_entry), idx); 185 186 /* populate sections */ 187 idx = 0; 188 for_each_sec(file, sec) { 189 if (!sec->text) 190 continue; 191 192 prev_insn = NULL; 193 sec_for_each_insn(file, sec, insn) { 194 if (!prev_insn || memcmp(&insn->orc, &prev_insn->orc, 195 sizeof(struct orc_entry))) { 196 197 if (create_orc_entry(u_sec, ip_relasec, idx, 198 insn->sec, insn->offset, 199 &insn->orc)) 200 return -1; 201 202 idx++; 203 } 204 prev_insn = insn; 205 } 206 207 /* section terminator */ 208 if (prev_insn) { 209 if (create_orc_entry(u_sec, ip_relasec, idx, 210 prev_insn->sec, 211 prev_insn->offset + prev_insn->len, 212 &empty)) 213 return -1; 214 215 idx++; 216 } 217 } 218 219 if (elf_rebuild_rela_section(ip_relasec)) 220 return -1; 221 222 return 0; 223 } 224