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 "orc.h" 10627fce14SJosh Poimboeuf #include "check.h" 11627fce14SJosh Poimboeuf #include "warn.h" 12627fce14SJosh Poimboeuf 13627fce14SJosh Poimboeuf int create_orc(struct objtool_file *file) 14627fce14SJosh Poimboeuf { 15627fce14SJosh Poimboeuf struct instruction *insn; 16627fce14SJosh Poimboeuf 17627fce14SJosh Poimboeuf for_each_insn(file, insn) { 18627fce14SJosh Poimboeuf struct orc_entry *orc = &insn->orc; 19e7c0219bSPeter Zijlstra struct cfi_reg *cfa = &insn->cfi.cfa; 20e7c0219bSPeter Zijlstra struct cfi_reg *bp = &insn->cfi.regs[CFI_BP]; 21627fce14SJosh Poimboeuf 22e7c0219bSPeter Zijlstra orc->end = insn->cfi.end; 23d31a5802SJosh Poimboeuf 24627fce14SJosh Poimboeuf if (cfa->base == CFI_UNDEFINED) { 25627fce14SJosh Poimboeuf orc->sp_reg = ORC_REG_UNDEFINED; 26627fce14SJosh Poimboeuf continue; 27627fce14SJosh Poimboeuf } 28627fce14SJosh Poimboeuf 29627fce14SJosh Poimboeuf switch (cfa->base) { 30627fce14SJosh Poimboeuf case CFI_SP: 31627fce14SJosh Poimboeuf orc->sp_reg = ORC_REG_SP; 32627fce14SJosh Poimboeuf break; 33627fce14SJosh Poimboeuf case CFI_SP_INDIRECT: 34627fce14SJosh Poimboeuf orc->sp_reg = ORC_REG_SP_INDIRECT; 35627fce14SJosh Poimboeuf break; 36627fce14SJosh Poimboeuf case CFI_BP: 37627fce14SJosh Poimboeuf orc->sp_reg = ORC_REG_BP; 38627fce14SJosh Poimboeuf break; 39627fce14SJosh Poimboeuf case CFI_BP_INDIRECT: 40627fce14SJosh Poimboeuf orc->sp_reg = ORC_REG_BP_INDIRECT; 41627fce14SJosh Poimboeuf break; 42627fce14SJosh Poimboeuf case CFI_R10: 43627fce14SJosh Poimboeuf orc->sp_reg = ORC_REG_R10; 44627fce14SJosh Poimboeuf break; 45627fce14SJosh Poimboeuf case CFI_R13: 46627fce14SJosh Poimboeuf orc->sp_reg = ORC_REG_R13; 47627fce14SJosh Poimboeuf break; 48627fce14SJosh Poimboeuf case CFI_DI: 49627fce14SJosh Poimboeuf orc->sp_reg = ORC_REG_DI; 50627fce14SJosh Poimboeuf break; 51627fce14SJosh Poimboeuf case CFI_DX: 52627fce14SJosh Poimboeuf orc->sp_reg = ORC_REG_DX; 53627fce14SJosh Poimboeuf break; 54627fce14SJosh Poimboeuf default: 55627fce14SJosh Poimboeuf WARN_FUNC("unknown CFA base reg %d", 56627fce14SJosh Poimboeuf insn->sec, insn->offset, cfa->base); 57627fce14SJosh Poimboeuf return -1; 58627fce14SJosh Poimboeuf } 59627fce14SJosh Poimboeuf 60627fce14SJosh Poimboeuf switch(bp->base) { 61627fce14SJosh Poimboeuf case CFI_UNDEFINED: 62627fce14SJosh Poimboeuf orc->bp_reg = ORC_REG_UNDEFINED; 63627fce14SJosh Poimboeuf break; 64627fce14SJosh Poimboeuf case CFI_CFA: 65627fce14SJosh Poimboeuf orc->bp_reg = ORC_REG_PREV_SP; 66627fce14SJosh Poimboeuf break; 67627fce14SJosh Poimboeuf case CFI_BP: 68627fce14SJosh Poimboeuf orc->bp_reg = ORC_REG_BP; 69627fce14SJosh Poimboeuf break; 70627fce14SJosh Poimboeuf default: 71627fce14SJosh Poimboeuf WARN_FUNC("unknown BP base reg %d", 72627fce14SJosh Poimboeuf insn->sec, insn->offset, bp->base); 73627fce14SJosh Poimboeuf return -1; 74627fce14SJosh Poimboeuf } 75627fce14SJosh Poimboeuf 76627fce14SJosh Poimboeuf orc->sp_offset = cfa->offset; 77627fce14SJosh Poimboeuf orc->bp_offset = bp->offset; 78e7c0219bSPeter Zijlstra orc->type = insn->cfi.type; 79627fce14SJosh Poimboeuf } 80627fce14SJosh Poimboeuf 81627fce14SJosh Poimboeuf return 0; 82627fce14SJosh Poimboeuf } 83627fce14SJosh Poimboeuf 848b5fa6bcSPeter Zijlstra static int create_orc_entry(struct elf *elf, struct section *u_sec, struct section *ip_relasec, 85627fce14SJosh Poimboeuf unsigned int idx, struct section *insn_sec, 86627fce14SJosh Poimboeuf unsigned long insn_off, struct orc_entry *o) 87627fce14SJosh Poimboeuf { 88627fce14SJosh Poimboeuf struct orc_entry *orc; 89627fce14SJosh Poimboeuf struct rela *rela; 90627fce14SJosh Poimboeuf 91627fce14SJosh Poimboeuf /* populate ORC data */ 92627fce14SJosh Poimboeuf orc = (struct orc_entry *)u_sec->data->d_buf + idx; 93627fce14SJosh Poimboeuf memcpy(orc, o, sizeof(*orc)); 94627fce14SJosh Poimboeuf 95627fce14SJosh Poimboeuf /* populate rela for ip */ 96627fce14SJosh Poimboeuf rela = malloc(sizeof(*rela)); 97627fce14SJosh Poimboeuf if (!rela) { 98627fce14SJosh Poimboeuf perror("malloc"); 99627fce14SJosh Poimboeuf return -1; 100627fce14SJosh Poimboeuf } 101627fce14SJosh Poimboeuf memset(rela, 0, sizeof(*rela)); 102627fce14SJosh Poimboeuf 103e81e0724SJosh Poimboeuf if (insn_sec->sym) { 104627fce14SJosh Poimboeuf rela->sym = insn_sec->sym; 105627fce14SJosh Poimboeuf rela->addend = insn_off; 106e81e0724SJosh Poimboeuf } else { 107e81e0724SJosh Poimboeuf /* 108e81e0724SJosh Poimboeuf * The Clang assembler doesn't produce section symbols, so we 109e81e0724SJosh Poimboeuf * have to reference the function symbol instead: 110e81e0724SJosh Poimboeuf */ 111e81e0724SJosh Poimboeuf rela->sym = find_symbol_containing(insn_sec, insn_off); 112e81e0724SJosh Poimboeuf if (!rela->sym) { 113e81e0724SJosh Poimboeuf /* 114e81e0724SJosh Poimboeuf * Hack alert. This happens when we need to reference 115e81e0724SJosh Poimboeuf * the NOP pad insn immediately after the function. 116e81e0724SJosh Poimboeuf */ 117e81e0724SJosh Poimboeuf rela->sym = find_symbol_containing(insn_sec, 118e81e0724SJosh Poimboeuf insn_off - 1); 119e81e0724SJosh Poimboeuf } 120e81e0724SJosh Poimboeuf if (!rela->sym) { 121e81e0724SJosh Poimboeuf WARN("missing symbol for insn at offset 0x%lx\n", 122e81e0724SJosh Poimboeuf insn_off); 123e81e0724SJosh Poimboeuf return -1; 124e81e0724SJosh Poimboeuf } 125e81e0724SJosh Poimboeuf 126e81e0724SJosh Poimboeuf rela->addend = insn_off - rela->sym->offset; 127e81e0724SJosh Poimboeuf } 128e81e0724SJosh Poimboeuf 129627fce14SJosh Poimboeuf rela->type = R_X86_64_PC32; 130627fce14SJosh Poimboeuf rela->offset = idx * sizeof(int); 1318b5fa6bcSPeter Zijlstra rela->sec = ip_relasec; 132627fce14SJosh Poimboeuf 133627fce14SJosh Poimboeuf list_add_tail(&rela->list, &ip_relasec->rela_list); 1348b5fa6bcSPeter Zijlstra hash_add(elf->rela_hash, &rela->hash, rela_hash(rela)); 135627fce14SJosh Poimboeuf 136627fce14SJosh Poimboeuf return 0; 137627fce14SJosh Poimboeuf } 138627fce14SJosh Poimboeuf 139627fce14SJosh Poimboeuf int create_orc_sections(struct objtool_file *file) 140627fce14SJosh Poimboeuf { 141627fce14SJosh Poimboeuf struct instruction *insn, *prev_insn; 142627fce14SJosh Poimboeuf struct section *sec, *u_sec, *ip_relasec; 143627fce14SJosh Poimboeuf unsigned int idx; 144627fce14SJosh Poimboeuf 145627fce14SJosh Poimboeuf struct orc_entry empty = { 146627fce14SJosh Poimboeuf .sp_reg = ORC_REG_UNDEFINED, 147627fce14SJosh Poimboeuf .bp_reg = ORC_REG_UNDEFINED, 148627fce14SJosh Poimboeuf .type = ORC_TYPE_CALL, 149627fce14SJosh Poimboeuf }; 150627fce14SJosh Poimboeuf 151627fce14SJosh Poimboeuf sec = find_section_by_name(file->elf, ".orc_unwind"); 152627fce14SJosh Poimboeuf if (sec) { 153627fce14SJosh Poimboeuf WARN("file already has .orc_unwind section, skipping"); 154627fce14SJosh Poimboeuf return -1; 155627fce14SJosh Poimboeuf } 156627fce14SJosh Poimboeuf 157627fce14SJosh Poimboeuf /* count the number of needed orcs */ 158627fce14SJosh Poimboeuf idx = 0; 159627fce14SJosh Poimboeuf for_each_sec(file, sec) { 160627fce14SJosh Poimboeuf if (!sec->text) 161627fce14SJosh Poimboeuf continue; 162627fce14SJosh Poimboeuf 163627fce14SJosh Poimboeuf prev_insn = NULL; 164627fce14SJosh Poimboeuf sec_for_each_insn(file, sec, insn) { 165627fce14SJosh Poimboeuf if (!prev_insn || 166627fce14SJosh Poimboeuf memcmp(&insn->orc, &prev_insn->orc, 167627fce14SJosh Poimboeuf sizeof(struct orc_entry))) { 168627fce14SJosh Poimboeuf idx++; 169627fce14SJosh Poimboeuf } 170627fce14SJosh Poimboeuf prev_insn = insn; 171627fce14SJosh Poimboeuf } 172627fce14SJosh Poimboeuf 173627fce14SJosh Poimboeuf /* section terminator */ 174627fce14SJosh Poimboeuf if (prev_insn) 175627fce14SJosh Poimboeuf idx++; 176627fce14SJosh Poimboeuf } 177627fce14SJosh Poimboeuf if (!idx) 178627fce14SJosh Poimboeuf return -1; 179627fce14SJosh Poimboeuf 180627fce14SJosh Poimboeuf 181627fce14SJosh Poimboeuf /* create .orc_unwind_ip and .rela.orc_unwind_ip sections */ 182627fce14SJosh Poimboeuf sec = elf_create_section(file->elf, ".orc_unwind_ip", sizeof(int), idx); 183ce90aaf5SSimon Ser if (!sec) 184ce90aaf5SSimon Ser return -1; 185627fce14SJosh Poimboeuf 186627fce14SJosh Poimboeuf ip_relasec = elf_create_rela_section(file->elf, sec); 187627fce14SJosh Poimboeuf if (!ip_relasec) 188627fce14SJosh Poimboeuf return -1; 189627fce14SJosh Poimboeuf 190627fce14SJosh Poimboeuf /* create .orc_unwind section */ 191627fce14SJosh Poimboeuf u_sec = elf_create_section(file->elf, ".orc_unwind", 192627fce14SJosh Poimboeuf sizeof(struct orc_entry), idx); 193627fce14SJosh Poimboeuf 194627fce14SJosh Poimboeuf /* populate sections */ 195627fce14SJosh Poimboeuf idx = 0; 196627fce14SJosh Poimboeuf for_each_sec(file, sec) { 197627fce14SJosh Poimboeuf if (!sec->text) 198627fce14SJosh Poimboeuf continue; 199627fce14SJosh Poimboeuf 200627fce14SJosh Poimboeuf prev_insn = NULL; 201627fce14SJosh Poimboeuf sec_for_each_insn(file, sec, insn) { 202627fce14SJosh Poimboeuf if (!prev_insn || memcmp(&insn->orc, &prev_insn->orc, 203627fce14SJosh Poimboeuf sizeof(struct orc_entry))) { 204627fce14SJosh Poimboeuf 2058b5fa6bcSPeter Zijlstra if (create_orc_entry(file->elf, u_sec, ip_relasec, idx, 206627fce14SJosh Poimboeuf insn->sec, insn->offset, 207627fce14SJosh Poimboeuf &insn->orc)) 208627fce14SJosh Poimboeuf return -1; 209627fce14SJosh Poimboeuf 210627fce14SJosh Poimboeuf idx++; 211627fce14SJosh Poimboeuf } 212627fce14SJosh Poimboeuf prev_insn = insn; 213627fce14SJosh Poimboeuf } 214627fce14SJosh Poimboeuf 215627fce14SJosh Poimboeuf /* section terminator */ 216627fce14SJosh Poimboeuf if (prev_insn) { 2178b5fa6bcSPeter Zijlstra if (create_orc_entry(file->elf, u_sec, ip_relasec, idx, 218627fce14SJosh Poimboeuf prev_insn->sec, 219627fce14SJosh Poimboeuf prev_insn->offset + prev_insn->len, 220627fce14SJosh Poimboeuf &empty)) 221627fce14SJosh Poimboeuf return -1; 222627fce14SJosh Poimboeuf 223627fce14SJosh Poimboeuf idx++; 224627fce14SJosh Poimboeuf } 225627fce14SJosh Poimboeuf } 226627fce14SJosh Poimboeuf 227627fce14SJosh Poimboeuf if (elf_rebuild_rela_section(ip_relasec)) 228627fce14SJosh Poimboeuf return -1; 229627fce14SJosh Poimboeuf 230627fce14SJosh Poimboeuf return 0; 231627fce14SJosh Poimboeuf } 232