1627fce14SJosh Poimboeuf /* 2627fce14SJosh Poimboeuf * Copyright (C) 2017 Josh Poimboeuf <jpoimboe@redhat.com> 3627fce14SJosh Poimboeuf * 4627fce14SJosh Poimboeuf * This program is free software; you can redistribute it and/or 5627fce14SJosh Poimboeuf * modify it under the terms of the GNU General Public License 6627fce14SJosh Poimboeuf * as published by the Free Software Foundation; either version 2 7627fce14SJosh Poimboeuf * of the License, or (at your option) any later version. 8627fce14SJosh Poimboeuf * 9627fce14SJosh Poimboeuf * This program is distributed in the hope that it will be useful, 10627fce14SJosh Poimboeuf * but WITHOUT ANY WARRANTY; without even the implied warranty of 11627fce14SJosh Poimboeuf * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12627fce14SJosh Poimboeuf * GNU General Public License for more details. 13627fce14SJosh Poimboeuf * 14627fce14SJosh Poimboeuf * You should have received a copy of the GNU General Public License 15627fce14SJosh Poimboeuf * along with this program; if not, see <http://www.gnu.org/licenses/>. 16627fce14SJosh Poimboeuf */ 17627fce14SJosh Poimboeuf 18627fce14SJosh Poimboeuf #include <stdlib.h> 19627fce14SJosh Poimboeuf #include <string.h> 20627fce14SJosh Poimboeuf 21627fce14SJosh Poimboeuf #include "orc.h" 22627fce14SJosh Poimboeuf #include "check.h" 23627fce14SJosh Poimboeuf #include "warn.h" 24627fce14SJosh Poimboeuf 25627fce14SJosh Poimboeuf int create_orc(struct objtool_file *file) 26627fce14SJosh Poimboeuf { 27627fce14SJosh Poimboeuf struct instruction *insn; 28627fce14SJosh Poimboeuf 29627fce14SJosh Poimboeuf for_each_insn(file, insn) { 30627fce14SJosh Poimboeuf struct orc_entry *orc = &insn->orc; 31627fce14SJosh Poimboeuf struct cfi_reg *cfa = &insn->state.cfa; 32627fce14SJosh Poimboeuf struct cfi_reg *bp = &insn->state.regs[CFI_BP]; 33627fce14SJosh Poimboeuf 34627fce14SJosh Poimboeuf if (cfa->base == CFI_UNDEFINED) { 35627fce14SJosh Poimboeuf orc->sp_reg = ORC_REG_UNDEFINED; 36627fce14SJosh Poimboeuf continue; 37627fce14SJosh Poimboeuf } 38627fce14SJosh Poimboeuf 39627fce14SJosh Poimboeuf switch (cfa->base) { 40627fce14SJosh Poimboeuf case CFI_SP: 41627fce14SJosh Poimboeuf orc->sp_reg = ORC_REG_SP; 42627fce14SJosh Poimboeuf break; 43627fce14SJosh Poimboeuf case CFI_SP_INDIRECT: 44627fce14SJosh Poimboeuf orc->sp_reg = ORC_REG_SP_INDIRECT; 45627fce14SJosh Poimboeuf break; 46627fce14SJosh Poimboeuf case CFI_BP: 47627fce14SJosh Poimboeuf orc->sp_reg = ORC_REG_BP; 48627fce14SJosh Poimboeuf break; 49627fce14SJosh Poimboeuf case CFI_BP_INDIRECT: 50627fce14SJosh Poimboeuf orc->sp_reg = ORC_REG_BP_INDIRECT; 51627fce14SJosh Poimboeuf break; 52627fce14SJosh Poimboeuf case CFI_R10: 53627fce14SJosh Poimboeuf orc->sp_reg = ORC_REG_R10; 54627fce14SJosh Poimboeuf break; 55627fce14SJosh Poimboeuf case CFI_R13: 56627fce14SJosh Poimboeuf orc->sp_reg = ORC_REG_R13; 57627fce14SJosh Poimboeuf break; 58627fce14SJosh Poimboeuf case CFI_DI: 59627fce14SJosh Poimboeuf orc->sp_reg = ORC_REG_DI; 60627fce14SJosh Poimboeuf break; 61627fce14SJosh Poimboeuf case CFI_DX: 62627fce14SJosh Poimboeuf orc->sp_reg = ORC_REG_DX; 63627fce14SJosh Poimboeuf break; 64627fce14SJosh Poimboeuf default: 65627fce14SJosh Poimboeuf WARN_FUNC("unknown CFA base reg %d", 66627fce14SJosh Poimboeuf insn->sec, insn->offset, cfa->base); 67627fce14SJosh Poimboeuf return -1; 68627fce14SJosh Poimboeuf } 69627fce14SJosh Poimboeuf 70627fce14SJosh Poimboeuf switch(bp->base) { 71627fce14SJosh Poimboeuf case CFI_UNDEFINED: 72627fce14SJosh Poimboeuf orc->bp_reg = ORC_REG_UNDEFINED; 73627fce14SJosh Poimboeuf break; 74627fce14SJosh Poimboeuf case CFI_CFA: 75627fce14SJosh Poimboeuf orc->bp_reg = ORC_REG_PREV_SP; 76627fce14SJosh Poimboeuf break; 77627fce14SJosh Poimboeuf case CFI_BP: 78627fce14SJosh Poimboeuf orc->bp_reg = ORC_REG_BP; 79627fce14SJosh Poimboeuf break; 80627fce14SJosh Poimboeuf default: 81627fce14SJosh Poimboeuf WARN_FUNC("unknown BP base reg %d", 82627fce14SJosh Poimboeuf insn->sec, insn->offset, bp->base); 83627fce14SJosh Poimboeuf return -1; 84627fce14SJosh Poimboeuf } 85627fce14SJosh Poimboeuf 86627fce14SJosh Poimboeuf orc->sp_offset = cfa->offset; 87627fce14SJosh Poimboeuf orc->bp_offset = bp->offset; 88627fce14SJosh Poimboeuf orc->type = insn->state.type; 89627fce14SJosh Poimboeuf } 90627fce14SJosh Poimboeuf 91627fce14SJosh Poimboeuf return 0; 92627fce14SJosh Poimboeuf } 93627fce14SJosh Poimboeuf 94627fce14SJosh Poimboeuf static int create_orc_entry(struct section *u_sec, struct section *ip_relasec, 95627fce14SJosh Poimboeuf unsigned int idx, struct section *insn_sec, 96627fce14SJosh Poimboeuf unsigned long insn_off, struct orc_entry *o) 97627fce14SJosh Poimboeuf { 98627fce14SJosh Poimboeuf struct orc_entry *orc; 99627fce14SJosh Poimboeuf struct rela *rela; 100627fce14SJosh Poimboeuf 101627fce14SJosh Poimboeuf /* populate ORC data */ 102627fce14SJosh Poimboeuf orc = (struct orc_entry *)u_sec->data->d_buf + idx; 103627fce14SJosh Poimboeuf memcpy(orc, o, sizeof(*orc)); 104627fce14SJosh Poimboeuf 105627fce14SJosh Poimboeuf /* populate rela for ip */ 106627fce14SJosh Poimboeuf rela = malloc(sizeof(*rela)); 107627fce14SJosh Poimboeuf if (!rela) { 108627fce14SJosh Poimboeuf perror("malloc"); 109627fce14SJosh Poimboeuf return -1; 110627fce14SJosh Poimboeuf } 111627fce14SJosh Poimboeuf memset(rela, 0, sizeof(*rela)); 112627fce14SJosh Poimboeuf 113627fce14SJosh Poimboeuf rela->sym = insn_sec->sym; 114627fce14SJosh Poimboeuf rela->addend = insn_off; 115627fce14SJosh Poimboeuf rela->type = R_X86_64_PC32; 116627fce14SJosh Poimboeuf rela->offset = idx * sizeof(int); 117627fce14SJosh Poimboeuf 118627fce14SJosh Poimboeuf list_add_tail(&rela->list, &ip_relasec->rela_list); 119627fce14SJosh Poimboeuf hash_add(ip_relasec->rela_hash, &rela->hash, rela->offset); 120627fce14SJosh Poimboeuf 121627fce14SJosh Poimboeuf return 0; 122627fce14SJosh Poimboeuf } 123627fce14SJosh Poimboeuf 124627fce14SJosh Poimboeuf int create_orc_sections(struct objtool_file *file) 125627fce14SJosh Poimboeuf { 126627fce14SJosh Poimboeuf struct instruction *insn, *prev_insn; 127627fce14SJosh Poimboeuf struct section *sec, *u_sec, *ip_relasec; 128627fce14SJosh Poimboeuf unsigned int idx; 129627fce14SJosh Poimboeuf 130627fce14SJosh Poimboeuf struct orc_entry empty = { 131627fce14SJosh Poimboeuf .sp_reg = ORC_REG_UNDEFINED, 132627fce14SJosh Poimboeuf .bp_reg = ORC_REG_UNDEFINED, 133627fce14SJosh Poimboeuf .type = ORC_TYPE_CALL, 134627fce14SJosh Poimboeuf }; 135627fce14SJosh Poimboeuf 136627fce14SJosh Poimboeuf sec = find_section_by_name(file->elf, ".orc_unwind"); 137627fce14SJosh Poimboeuf if (sec) { 138627fce14SJosh Poimboeuf WARN("file already has .orc_unwind section, skipping"); 139627fce14SJosh Poimboeuf return -1; 140627fce14SJosh Poimboeuf } 141627fce14SJosh Poimboeuf 142627fce14SJosh Poimboeuf /* count the number of needed orcs */ 143627fce14SJosh Poimboeuf idx = 0; 144627fce14SJosh Poimboeuf for_each_sec(file, sec) { 145627fce14SJosh Poimboeuf if (!sec->text) 146627fce14SJosh Poimboeuf continue; 147627fce14SJosh Poimboeuf 148627fce14SJosh Poimboeuf prev_insn = NULL; 149627fce14SJosh Poimboeuf sec_for_each_insn(file, sec, insn) { 150627fce14SJosh Poimboeuf if (!prev_insn || 151627fce14SJosh Poimboeuf memcmp(&insn->orc, &prev_insn->orc, 152627fce14SJosh Poimboeuf sizeof(struct orc_entry))) { 153627fce14SJosh Poimboeuf idx++; 154627fce14SJosh Poimboeuf } 155627fce14SJosh Poimboeuf prev_insn = insn; 156627fce14SJosh Poimboeuf } 157627fce14SJosh Poimboeuf 158627fce14SJosh Poimboeuf /* section terminator */ 159627fce14SJosh Poimboeuf if (prev_insn) 160627fce14SJosh Poimboeuf idx++; 161627fce14SJosh Poimboeuf } 162627fce14SJosh Poimboeuf if (!idx) 163627fce14SJosh Poimboeuf return -1; 164627fce14SJosh Poimboeuf 165627fce14SJosh Poimboeuf 166627fce14SJosh Poimboeuf /* create .orc_unwind_ip and .rela.orc_unwind_ip sections */ 167627fce14SJosh Poimboeuf sec = elf_create_section(file->elf, ".orc_unwind_ip", sizeof(int), idx); 168627fce14SJosh Poimboeuf 169627fce14SJosh Poimboeuf ip_relasec = elf_create_rela_section(file->elf, sec); 170627fce14SJosh Poimboeuf if (!ip_relasec) 171627fce14SJosh Poimboeuf return -1; 172627fce14SJosh Poimboeuf 173627fce14SJosh Poimboeuf /* create .orc_unwind section */ 174627fce14SJosh Poimboeuf u_sec = elf_create_section(file->elf, ".orc_unwind", 175627fce14SJosh Poimboeuf sizeof(struct orc_entry), idx); 176627fce14SJosh Poimboeuf 177627fce14SJosh Poimboeuf /* populate sections */ 178627fce14SJosh Poimboeuf idx = 0; 179627fce14SJosh Poimboeuf for_each_sec(file, sec) { 180627fce14SJosh Poimboeuf if (!sec->text) 181627fce14SJosh Poimboeuf continue; 182627fce14SJosh Poimboeuf 183627fce14SJosh Poimboeuf prev_insn = NULL; 184627fce14SJosh Poimboeuf sec_for_each_insn(file, sec, insn) { 185627fce14SJosh Poimboeuf if (!prev_insn || memcmp(&insn->orc, &prev_insn->orc, 186627fce14SJosh Poimboeuf sizeof(struct orc_entry))) { 187627fce14SJosh Poimboeuf 188627fce14SJosh Poimboeuf if (create_orc_entry(u_sec, ip_relasec, idx, 189627fce14SJosh Poimboeuf insn->sec, insn->offset, 190627fce14SJosh Poimboeuf &insn->orc)) 191627fce14SJosh Poimboeuf return -1; 192627fce14SJosh Poimboeuf 193627fce14SJosh Poimboeuf idx++; 194627fce14SJosh Poimboeuf } 195627fce14SJosh Poimboeuf prev_insn = insn; 196627fce14SJosh Poimboeuf } 197627fce14SJosh Poimboeuf 198627fce14SJosh Poimboeuf /* section terminator */ 199627fce14SJosh Poimboeuf if (prev_insn) { 200627fce14SJosh Poimboeuf if (create_orc_entry(u_sec, ip_relasec, idx, 201627fce14SJosh Poimboeuf prev_insn->sec, 202627fce14SJosh Poimboeuf prev_insn->offset + prev_insn->len, 203627fce14SJosh Poimboeuf &empty)) 204627fce14SJosh Poimboeuf return -1; 205627fce14SJosh Poimboeuf 206627fce14SJosh Poimboeuf idx++; 207627fce14SJosh Poimboeuf } 208627fce14SJosh Poimboeuf } 209627fce14SJosh Poimboeuf 210627fce14SJosh Poimboeuf if (elf_rebuild_rela_section(ip_relasec)) 211627fce14SJosh Poimboeuf return -1; 212627fce14SJosh Poimboeuf 213627fce14SJosh Poimboeuf return 0; 214627fce14SJosh Poimboeuf } 215