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