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