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