11ccea77eSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 2627fce14SJosh Poimboeuf /* 3627fce14SJosh Poimboeuf * Copyright (C) 2017 Josh Poimboeuf <jpoimboe@redhat.com> 4627fce14SJosh Poimboeuf */ 5627fce14SJosh Poimboeuf 6627fce14SJosh Poimboeuf #include <stdlib.h> 7627fce14SJosh Poimboeuf #include <string.h> 8627fce14SJosh Poimboeuf 9627fce14SJosh Poimboeuf #include "check.h" 10627fce14SJosh Poimboeuf #include "warn.h" 11627fce14SJosh Poimboeuf 12627fce14SJosh Poimboeuf int create_orc(struct objtool_file *file) 13627fce14SJosh Poimboeuf { 14627fce14SJosh Poimboeuf struct instruction *insn; 15627fce14SJosh Poimboeuf 16627fce14SJosh Poimboeuf for_each_insn(file, insn) { 17627fce14SJosh Poimboeuf struct orc_entry *orc = &insn->orc; 18e7c0219bSPeter Zijlstra struct cfi_reg *cfa = &insn->cfi.cfa; 19e7c0219bSPeter Zijlstra struct cfi_reg *bp = &insn->cfi.regs[CFI_BP]; 20627fce14SJosh Poimboeuf 21e7c0219bSPeter Zijlstra orc->end = insn->cfi.end; 22d31a5802SJosh Poimboeuf 23627fce14SJosh Poimboeuf if (cfa->base == CFI_UNDEFINED) { 24627fce14SJosh Poimboeuf orc->sp_reg = ORC_REG_UNDEFINED; 25627fce14SJosh Poimboeuf continue; 26627fce14SJosh Poimboeuf } 27627fce14SJosh Poimboeuf 28627fce14SJosh Poimboeuf switch (cfa->base) { 29627fce14SJosh Poimboeuf case CFI_SP: 30627fce14SJosh Poimboeuf orc->sp_reg = ORC_REG_SP; 31627fce14SJosh Poimboeuf break; 32627fce14SJosh Poimboeuf case CFI_SP_INDIRECT: 33627fce14SJosh Poimboeuf orc->sp_reg = ORC_REG_SP_INDIRECT; 34627fce14SJosh Poimboeuf break; 35627fce14SJosh Poimboeuf case CFI_BP: 36627fce14SJosh Poimboeuf orc->sp_reg = ORC_REG_BP; 37627fce14SJosh Poimboeuf break; 38627fce14SJosh Poimboeuf case CFI_BP_INDIRECT: 39627fce14SJosh Poimboeuf orc->sp_reg = ORC_REG_BP_INDIRECT; 40627fce14SJosh Poimboeuf break; 41627fce14SJosh Poimboeuf case CFI_R10: 42627fce14SJosh Poimboeuf orc->sp_reg = ORC_REG_R10; 43627fce14SJosh Poimboeuf break; 44627fce14SJosh Poimboeuf case CFI_R13: 45627fce14SJosh Poimboeuf orc->sp_reg = ORC_REG_R13; 46627fce14SJosh Poimboeuf break; 47627fce14SJosh Poimboeuf case CFI_DI: 48627fce14SJosh Poimboeuf orc->sp_reg = ORC_REG_DI; 49627fce14SJosh Poimboeuf break; 50627fce14SJosh Poimboeuf case CFI_DX: 51627fce14SJosh Poimboeuf orc->sp_reg = ORC_REG_DX; 52627fce14SJosh Poimboeuf break; 53627fce14SJosh Poimboeuf default: 54627fce14SJosh Poimboeuf WARN_FUNC("unknown CFA base reg %d", 55627fce14SJosh Poimboeuf insn->sec, insn->offset, cfa->base); 56627fce14SJosh Poimboeuf return -1; 57627fce14SJosh Poimboeuf } 58627fce14SJosh Poimboeuf 59627fce14SJosh Poimboeuf switch(bp->base) { 60627fce14SJosh Poimboeuf case CFI_UNDEFINED: 61627fce14SJosh Poimboeuf orc->bp_reg = ORC_REG_UNDEFINED; 62627fce14SJosh Poimboeuf break; 63627fce14SJosh Poimboeuf case CFI_CFA: 64627fce14SJosh Poimboeuf orc->bp_reg = ORC_REG_PREV_SP; 65627fce14SJosh Poimboeuf break; 66627fce14SJosh Poimboeuf case CFI_BP: 67627fce14SJosh Poimboeuf orc->bp_reg = ORC_REG_BP; 68627fce14SJosh Poimboeuf break; 69627fce14SJosh Poimboeuf default: 70627fce14SJosh Poimboeuf WARN_FUNC("unknown BP base reg %d", 71627fce14SJosh Poimboeuf insn->sec, insn->offset, bp->base); 72627fce14SJosh Poimboeuf return -1; 73627fce14SJosh Poimboeuf } 74627fce14SJosh Poimboeuf 75627fce14SJosh Poimboeuf orc->sp_offset = cfa->offset; 76627fce14SJosh Poimboeuf orc->bp_offset = bp->offset; 77e7c0219bSPeter Zijlstra orc->type = insn->cfi.type; 78627fce14SJosh Poimboeuf } 79627fce14SJosh Poimboeuf 80627fce14SJosh Poimboeuf return 0; 81627fce14SJosh Poimboeuf } 82627fce14SJosh Poimboeuf 83f1974222SMatt Helsley static int create_orc_entry(struct elf *elf, struct section *u_sec, struct section *ip_relocsec, 84627fce14SJosh Poimboeuf unsigned int idx, struct section *insn_sec, 85627fce14SJosh Poimboeuf unsigned long insn_off, struct orc_entry *o) 86627fce14SJosh Poimboeuf { 87627fce14SJosh Poimboeuf struct orc_entry *orc; 88f1974222SMatt Helsley struct reloc *reloc; 89627fce14SJosh Poimboeuf 90627fce14SJosh Poimboeuf /* populate ORC data */ 91627fce14SJosh Poimboeuf orc = (struct orc_entry *)u_sec->data->d_buf + idx; 92627fce14SJosh Poimboeuf memcpy(orc, o, sizeof(*orc)); 93627fce14SJosh Poimboeuf 94f1974222SMatt Helsley /* populate reloc for ip */ 95f1974222SMatt Helsley reloc = malloc(sizeof(*reloc)); 96f1974222SMatt Helsley if (!reloc) { 97627fce14SJosh Poimboeuf perror("malloc"); 98627fce14SJosh Poimboeuf return -1; 99627fce14SJosh Poimboeuf } 100f1974222SMatt Helsley memset(reloc, 0, sizeof(*reloc)); 101627fce14SJosh Poimboeuf 102e81e0724SJosh Poimboeuf if (insn_sec->sym) { 103f1974222SMatt Helsley reloc->sym = insn_sec->sym; 104f1974222SMatt Helsley reloc->addend = insn_off; 105e81e0724SJosh Poimboeuf } else { 106e81e0724SJosh Poimboeuf /* 107e81e0724SJosh Poimboeuf * The Clang assembler doesn't produce section symbols, so we 108e81e0724SJosh Poimboeuf * have to reference the function symbol instead: 109e81e0724SJosh Poimboeuf */ 110f1974222SMatt Helsley reloc->sym = find_symbol_containing(insn_sec, insn_off); 111f1974222SMatt Helsley if (!reloc->sym) { 112e81e0724SJosh Poimboeuf /* 113e81e0724SJosh Poimboeuf * Hack alert. This happens when we need to reference 114e81e0724SJosh Poimboeuf * the NOP pad insn immediately after the function. 115e81e0724SJosh Poimboeuf */ 116f1974222SMatt Helsley reloc->sym = find_symbol_containing(insn_sec, 117e81e0724SJosh Poimboeuf insn_off - 1); 118e81e0724SJosh Poimboeuf } 119f1974222SMatt Helsley if (!reloc->sym) { 120e81e0724SJosh Poimboeuf WARN("missing symbol for insn at offset 0x%lx\n", 121e81e0724SJosh Poimboeuf insn_off); 122e81e0724SJosh Poimboeuf return -1; 123e81e0724SJosh Poimboeuf } 124e81e0724SJosh Poimboeuf 125f1974222SMatt Helsley reloc->addend = insn_off - reloc->sym->offset; 126e81e0724SJosh Poimboeuf } 127e81e0724SJosh Poimboeuf 128f1974222SMatt Helsley reloc->type = R_X86_64_PC32; 129f1974222SMatt Helsley reloc->offset = idx * sizeof(int); 130f1974222SMatt Helsley reloc->sec = ip_relocsec; 131627fce14SJosh Poimboeuf 132f1974222SMatt Helsley elf_add_reloc(elf, reloc); 133627fce14SJosh Poimboeuf 134627fce14SJosh Poimboeuf return 0; 135627fce14SJosh Poimboeuf } 136627fce14SJosh Poimboeuf 137627fce14SJosh Poimboeuf int create_orc_sections(struct objtool_file *file) 138627fce14SJosh Poimboeuf { 139627fce14SJosh Poimboeuf struct instruction *insn, *prev_insn; 140f1974222SMatt Helsley struct section *sec, *u_sec, *ip_relocsec; 141627fce14SJosh Poimboeuf unsigned int idx; 142627fce14SJosh Poimboeuf 143627fce14SJosh Poimboeuf struct orc_entry empty = { 144627fce14SJosh Poimboeuf .sp_reg = ORC_REG_UNDEFINED, 145627fce14SJosh Poimboeuf .bp_reg = ORC_REG_UNDEFINED, 146627fce14SJosh Poimboeuf .type = ORC_TYPE_CALL, 147627fce14SJosh Poimboeuf }; 148627fce14SJosh Poimboeuf 149627fce14SJosh Poimboeuf sec = find_section_by_name(file->elf, ".orc_unwind"); 150627fce14SJosh Poimboeuf if (sec) { 151627fce14SJosh Poimboeuf WARN("file already has .orc_unwind section, skipping"); 152627fce14SJosh Poimboeuf return -1; 153627fce14SJosh Poimboeuf } 154627fce14SJosh Poimboeuf 155627fce14SJosh Poimboeuf /* count the number of needed orcs */ 156627fce14SJosh Poimboeuf idx = 0; 157627fce14SJosh Poimboeuf for_each_sec(file, sec) { 158627fce14SJosh Poimboeuf if (!sec->text) 159627fce14SJosh Poimboeuf continue; 160627fce14SJosh Poimboeuf 161627fce14SJosh Poimboeuf prev_insn = NULL; 162627fce14SJosh Poimboeuf sec_for_each_insn(file, sec, insn) { 163627fce14SJosh Poimboeuf if (!prev_insn || 164627fce14SJosh Poimboeuf memcmp(&insn->orc, &prev_insn->orc, 165627fce14SJosh Poimboeuf sizeof(struct orc_entry))) { 166627fce14SJosh Poimboeuf idx++; 167627fce14SJosh Poimboeuf } 168627fce14SJosh Poimboeuf prev_insn = insn; 169627fce14SJosh Poimboeuf } 170627fce14SJosh Poimboeuf 171627fce14SJosh Poimboeuf /* section terminator */ 172627fce14SJosh Poimboeuf if (prev_insn) 173627fce14SJosh Poimboeuf idx++; 174627fce14SJosh Poimboeuf } 175627fce14SJosh Poimboeuf if (!idx) 176627fce14SJosh Poimboeuf return -1; 177627fce14SJosh Poimboeuf 178627fce14SJosh Poimboeuf 179627fce14SJosh Poimboeuf /* create .orc_unwind_ip and .rela.orc_unwind_ip sections */ 180627fce14SJosh Poimboeuf sec = elf_create_section(file->elf, ".orc_unwind_ip", sizeof(int), idx); 181ce90aaf5SSimon Ser if (!sec) 182ce90aaf5SSimon Ser return -1; 183627fce14SJosh Poimboeuf 184fb414783SMatt Helsley ip_relocsec = elf_create_reloc_section(file->elf, sec, SHT_RELA); 185f1974222SMatt Helsley if (!ip_relocsec) 186627fce14SJosh Poimboeuf return -1; 187627fce14SJosh Poimboeuf 188627fce14SJosh Poimboeuf /* create .orc_unwind section */ 189627fce14SJosh Poimboeuf u_sec = elf_create_section(file->elf, ".orc_unwind", 190627fce14SJosh Poimboeuf sizeof(struct orc_entry), idx); 191627fce14SJosh Poimboeuf 192627fce14SJosh Poimboeuf /* populate sections */ 193627fce14SJosh Poimboeuf idx = 0; 194627fce14SJosh Poimboeuf for_each_sec(file, sec) { 195627fce14SJosh Poimboeuf if (!sec->text) 196627fce14SJosh Poimboeuf continue; 197627fce14SJosh Poimboeuf 198627fce14SJosh Poimboeuf prev_insn = NULL; 199627fce14SJosh Poimboeuf sec_for_each_insn(file, sec, insn) { 200627fce14SJosh Poimboeuf if (!prev_insn || memcmp(&insn->orc, &prev_insn->orc, 201627fce14SJosh Poimboeuf sizeof(struct orc_entry))) { 202627fce14SJosh Poimboeuf 203f1974222SMatt Helsley if (create_orc_entry(file->elf, u_sec, ip_relocsec, idx, 204627fce14SJosh Poimboeuf insn->sec, insn->offset, 205627fce14SJosh Poimboeuf &insn->orc)) 206627fce14SJosh Poimboeuf return -1; 207627fce14SJosh Poimboeuf 208627fce14SJosh Poimboeuf idx++; 209627fce14SJosh Poimboeuf } 210627fce14SJosh Poimboeuf prev_insn = insn; 211627fce14SJosh Poimboeuf } 212627fce14SJosh Poimboeuf 213627fce14SJosh Poimboeuf /* section terminator */ 214627fce14SJosh Poimboeuf if (prev_insn) { 215f1974222SMatt Helsley if (create_orc_entry(file->elf, u_sec, ip_relocsec, idx, 216627fce14SJosh Poimboeuf prev_insn->sec, 217627fce14SJosh Poimboeuf prev_insn->offset + prev_insn->len, 218627fce14SJosh Poimboeuf &empty)) 219627fce14SJosh Poimboeuf return -1; 220627fce14SJosh Poimboeuf 221627fce14SJosh Poimboeuf idx++; 222627fce14SJosh Poimboeuf } 223627fce14SJosh Poimboeuf } 224627fce14SJosh Poimboeuf 225f1974222SMatt Helsley if (elf_rebuild_reloc_section(ip_relocsec)) 226627fce14SJosh Poimboeuf return -1; 227627fce14SJosh Poimboeuf 228627fce14SJosh Poimboeuf return 0; 229627fce14SJosh Poimboeuf } 230