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 9ee819aedSJulien Thierry #include <linux/objtool.h> 10ee819aedSJulien Thierry #include <asm/orc_types.h> 11ee819aedSJulien Thierry 127786032eSVasily Gorbik #include <objtool/check.h> 137786032eSVasily Gorbik #include <objtool/warn.h> 147786032eSVasily Gorbik #include <objtool/endianness.h> 15627fce14SJosh Poimboeuf 16*ab4e0744SJosh Poimboeuf static int init_orc_entry(struct orc_entry *orc, struct cfi_state *cfi) 17627fce14SJosh Poimboeuf { 18*ab4e0744SJosh Poimboeuf struct instruction *insn = container_of(cfi, struct instruction, cfi); 19*ab4e0744SJosh Poimboeuf struct cfi_reg *bp = &cfi->regs[CFI_BP]; 20627fce14SJosh Poimboeuf 21*ab4e0744SJosh Poimboeuf memset(orc, 0, sizeof(*orc)); 22627fce14SJosh Poimboeuf 23*ab4e0744SJosh Poimboeuf orc->end = cfi->end; 243eaecac8SJulien Thierry 25*ab4e0744SJosh Poimboeuf if (cfi->cfa.base == CFI_UNDEFINED) { 26627fce14SJosh Poimboeuf orc->sp_reg = ORC_REG_UNDEFINED; 27*ab4e0744SJosh Poimboeuf return 0; 28627fce14SJosh Poimboeuf } 29627fce14SJosh Poimboeuf 30*ab4e0744SJosh Poimboeuf switch (cfi->cfa.base) { 31627fce14SJosh Poimboeuf case CFI_SP: 32627fce14SJosh Poimboeuf orc->sp_reg = ORC_REG_SP; 33627fce14SJosh Poimboeuf break; 34627fce14SJosh Poimboeuf case CFI_SP_INDIRECT: 35627fce14SJosh Poimboeuf orc->sp_reg = ORC_REG_SP_INDIRECT; 36627fce14SJosh Poimboeuf break; 37627fce14SJosh Poimboeuf case CFI_BP: 38627fce14SJosh Poimboeuf orc->sp_reg = ORC_REG_BP; 39627fce14SJosh Poimboeuf break; 40627fce14SJosh Poimboeuf case CFI_BP_INDIRECT: 41627fce14SJosh Poimboeuf orc->sp_reg = ORC_REG_BP_INDIRECT; 42627fce14SJosh Poimboeuf break; 43627fce14SJosh Poimboeuf case CFI_R10: 44627fce14SJosh Poimboeuf orc->sp_reg = ORC_REG_R10; 45627fce14SJosh Poimboeuf break; 46627fce14SJosh Poimboeuf case CFI_R13: 47627fce14SJosh Poimboeuf orc->sp_reg = ORC_REG_R13; 48627fce14SJosh Poimboeuf break; 49627fce14SJosh Poimboeuf case CFI_DI: 50627fce14SJosh Poimboeuf orc->sp_reg = ORC_REG_DI; 51627fce14SJosh Poimboeuf break; 52627fce14SJosh Poimboeuf case CFI_DX: 53627fce14SJosh Poimboeuf orc->sp_reg = ORC_REG_DX; 54627fce14SJosh Poimboeuf break; 55627fce14SJosh Poimboeuf default: 56627fce14SJosh Poimboeuf WARN_FUNC("unknown CFA base reg %d", 57*ab4e0744SJosh Poimboeuf insn->sec, insn->offset, cfi->cfa.base); 58627fce14SJosh Poimboeuf return -1; 59627fce14SJosh Poimboeuf } 60627fce14SJosh Poimboeuf 61627fce14SJosh Poimboeuf switch (bp->base) { 62627fce14SJosh Poimboeuf case CFI_UNDEFINED: 63627fce14SJosh Poimboeuf orc->bp_reg = ORC_REG_UNDEFINED; 64627fce14SJosh Poimboeuf break; 65627fce14SJosh Poimboeuf case CFI_CFA: 66627fce14SJosh Poimboeuf orc->bp_reg = ORC_REG_PREV_SP; 67627fce14SJosh Poimboeuf break; 68627fce14SJosh Poimboeuf case CFI_BP: 69627fce14SJosh Poimboeuf orc->bp_reg = ORC_REG_BP; 70627fce14SJosh Poimboeuf break; 71627fce14SJosh Poimboeuf default: 72627fce14SJosh Poimboeuf WARN_FUNC("unknown BP base reg %d", 73627fce14SJosh Poimboeuf insn->sec, insn->offset, bp->base); 74627fce14SJosh Poimboeuf return -1; 75627fce14SJosh Poimboeuf } 76627fce14SJosh Poimboeuf 77*ab4e0744SJosh Poimboeuf orc->sp_offset = cfi->cfa.offset; 78627fce14SJosh Poimboeuf orc->bp_offset = bp->offset; 79*ab4e0744SJosh Poimboeuf orc->type = cfi->type; 80627fce14SJosh Poimboeuf 81627fce14SJosh Poimboeuf return 0; 82627fce14SJosh Poimboeuf } 83627fce14SJosh Poimboeuf 84*ab4e0744SJosh Poimboeuf static int write_orc_entry(struct elf *elf, struct section *orc_sec, 85*ab4e0744SJosh Poimboeuf struct section *ip_rsec, unsigned int idx, 86*ab4e0744SJosh Poimboeuf struct section *insn_sec, unsigned long insn_off, 87*ab4e0744SJosh Poimboeuf struct orc_entry *o) 88627fce14SJosh Poimboeuf { 89627fce14SJosh Poimboeuf struct orc_entry *orc; 90f1974222SMatt Helsley struct reloc *reloc; 91627fce14SJosh Poimboeuf 92627fce14SJosh Poimboeuf /* populate ORC data */ 93*ab4e0744SJosh Poimboeuf orc = (struct orc_entry *)orc_sec->data->d_buf + idx; 94627fce14SJosh Poimboeuf memcpy(orc, o, sizeof(*orc)); 958bfe2732SVasily Gorbik orc->sp_offset = bswap_if_needed(orc->sp_offset); 968bfe2732SVasily Gorbik orc->bp_offset = bswap_if_needed(orc->bp_offset); 97627fce14SJosh Poimboeuf 98f1974222SMatt Helsley /* populate reloc for ip */ 99f1974222SMatt Helsley reloc = malloc(sizeof(*reloc)); 100f1974222SMatt Helsley if (!reloc) { 101627fce14SJosh Poimboeuf perror("malloc"); 102627fce14SJosh Poimboeuf return -1; 103627fce14SJosh Poimboeuf } 104f1974222SMatt Helsley memset(reloc, 0, sizeof(*reloc)); 105627fce14SJosh Poimboeuf 10644f6a7c0SJosh Poimboeuf insn_to_reloc_sym_addend(insn_sec, insn_off, reloc); 107f1974222SMatt Helsley if (!reloc->sym) { 10844f6a7c0SJosh Poimboeuf WARN("missing symbol for insn at offset 0x%lx", 109e81e0724SJosh Poimboeuf insn_off); 110e81e0724SJosh Poimboeuf return -1; 111e81e0724SJosh Poimboeuf } 112e81e0724SJosh Poimboeuf 113f1974222SMatt Helsley reloc->type = R_X86_64_PC32; 114f1974222SMatt Helsley reloc->offset = idx * sizeof(int); 115*ab4e0744SJosh Poimboeuf reloc->sec = ip_rsec; 116627fce14SJosh Poimboeuf 117f1974222SMatt Helsley elf_add_reloc(elf, reloc); 118627fce14SJosh Poimboeuf 119627fce14SJosh Poimboeuf return 0; 120627fce14SJosh Poimboeuf } 121627fce14SJosh Poimboeuf 122*ab4e0744SJosh Poimboeuf struct orc_list_entry { 123*ab4e0744SJosh Poimboeuf struct list_head list; 124*ab4e0744SJosh Poimboeuf struct orc_entry orc; 125*ab4e0744SJosh Poimboeuf struct section *insn_sec; 126*ab4e0744SJosh Poimboeuf unsigned long insn_off; 127*ab4e0744SJosh Poimboeuf }; 128627fce14SJosh Poimboeuf 129*ab4e0744SJosh Poimboeuf static int orc_list_add(struct list_head *orc_list, struct orc_entry *orc, 130*ab4e0744SJosh Poimboeuf struct section *sec, unsigned long offset) 131*ab4e0744SJosh Poimboeuf { 132*ab4e0744SJosh Poimboeuf struct orc_list_entry *entry = malloc(sizeof(*entry)); 133*ab4e0744SJosh Poimboeuf 134*ab4e0744SJosh Poimboeuf if (!entry) { 135*ab4e0744SJosh Poimboeuf WARN("malloc failed"); 136*ab4e0744SJosh Poimboeuf return -1; 137*ab4e0744SJosh Poimboeuf } 138*ab4e0744SJosh Poimboeuf 139*ab4e0744SJosh Poimboeuf entry->orc = *orc; 140*ab4e0744SJosh Poimboeuf entry->insn_sec = sec; 141*ab4e0744SJosh Poimboeuf entry->insn_off = offset; 142*ab4e0744SJosh Poimboeuf 143*ab4e0744SJosh Poimboeuf list_add_tail(&entry->list, orc_list); 144*ab4e0744SJosh Poimboeuf return 0; 145*ab4e0744SJosh Poimboeuf } 146*ab4e0744SJosh Poimboeuf 147*ab4e0744SJosh Poimboeuf int orc_create(struct objtool_file *file) 148*ab4e0744SJosh Poimboeuf { 149*ab4e0744SJosh Poimboeuf struct section *sec, *ip_rsec, *orc_sec; 150*ab4e0744SJosh Poimboeuf unsigned int nr = 0, idx = 0; 151*ab4e0744SJosh Poimboeuf struct orc_list_entry *entry; 152*ab4e0744SJosh Poimboeuf struct list_head orc_list; 153*ab4e0744SJosh Poimboeuf 154*ab4e0744SJosh Poimboeuf struct orc_entry null = { 155627fce14SJosh Poimboeuf .sp_reg = ORC_REG_UNDEFINED, 156627fce14SJosh Poimboeuf .bp_reg = ORC_REG_UNDEFINED, 157ee819aedSJulien Thierry .type = UNWIND_HINT_TYPE_CALL, 158627fce14SJosh Poimboeuf }; 159627fce14SJosh Poimboeuf 160*ab4e0744SJosh Poimboeuf /* Build a deduplicated list of ORC entries: */ 161*ab4e0744SJosh Poimboeuf INIT_LIST_HEAD(&orc_list); 162*ab4e0744SJosh Poimboeuf for_each_sec(file, sec) { 163*ab4e0744SJosh Poimboeuf struct orc_entry orc, prev_orc = {0}; 164*ab4e0744SJosh Poimboeuf struct instruction *insn; 165*ab4e0744SJosh Poimboeuf bool empty = true; 166*ab4e0744SJosh Poimboeuf 167*ab4e0744SJosh Poimboeuf if (!sec->text) 168*ab4e0744SJosh Poimboeuf continue; 169*ab4e0744SJosh Poimboeuf 170*ab4e0744SJosh Poimboeuf sec_for_each_insn(file, sec, insn) { 171*ab4e0744SJosh Poimboeuf if (init_orc_entry(&orc, &insn->cfi)) 172*ab4e0744SJosh Poimboeuf return -1; 173*ab4e0744SJosh Poimboeuf if (!memcmp(&prev_orc, &orc, sizeof(orc))) 174*ab4e0744SJosh Poimboeuf continue; 175*ab4e0744SJosh Poimboeuf if (orc_list_add(&orc_list, &orc, sec, insn->offset)) 176*ab4e0744SJosh Poimboeuf return -1; 177*ab4e0744SJosh Poimboeuf nr++; 178*ab4e0744SJosh Poimboeuf prev_orc = orc; 179*ab4e0744SJosh Poimboeuf empty = false; 180*ab4e0744SJosh Poimboeuf } 181*ab4e0744SJosh Poimboeuf 182*ab4e0744SJosh Poimboeuf /* Add a section terminator */ 183*ab4e0744SJosh Poimboeuf if (!empty) { 184*ab4e0744SJosh Poimboeuf orc_list_add(&orc_list, &null, sec, sec->len); 185*ab4e0744SJosh Poimboeuf nr++; 186*ab4e0744SJosh Poimboeuf } 187*ab4e0744SJosh Poimboeuf } 188*ab4e0744SJosh Poimboeuf if (!nr) 189*ab4e0744SJosh Poimboeuf return 0; 190*ab4e0744SJosh Poimboeuf 191*ab4e0744SJosh Poimboeuf /* Create .orc_unwind, .orc_unwind_ip and .rela.orc_unwind_ip sections: */ 192627fce14SJosh Poimboeuf sec = find_section_by_name(file->elf, ".orc_unwind"); 193627fce14SJosh Poimboeuf if (sec) { 194627fce14SJosh Poimboeuf WARN("file already has .orc_unwind section, skipping"); 195627fce14SJosh Poimboeuf return -1; 196627fce14SJosh Poimboeuf } 197*ab4e0744SJosh Poimboeuf orc_sec = elf_create_section(file->elf, ".orc_unwind", 0, 198*ab4e0744SJosh Poimboeuf sizeof(struct orc_entry), nr); 199*ab4e0744SJosh Poimboeuf if (!orc_sec) 200627fce14SJosh Poimboeuf return -1; 201627fce14SJosh Poimboeuf 202*ab4e0744SJosh Poimboeuf sec = elf_create_section(file->elf, ".orc_unwind_ip", 0, sizeof(int), nr); 203ce90aaf5SSimon Ser if (!sec) 204ce90aaf5SSimon Ser return -1; 205*ab4e0744SJosh Poimboeuf ip_rsec = elf_create_reloc_section(file->elf, sec, SHT_RELA); 206*ab4e0744SJosh Poimboeuf if (!ip_rsec) 207627fce14SJosh Poimboeuf return -1; 208627fce14SJosh Poimboeuf 209*ab4e0744SJosh Poimboeuf /* Write ORC entries to sections: */ 210*ab4e0744SJosh Poimboeuf list_for_each_entry(entry, &orc_list, list) { 211*ab4e0744SJosh Poimboeuf if (write_orc_entry(file->elf, orc_sec, ip_rsec, idx++, 212*ab4e0744SJosh Poimboeuf entry->insn_sec, entry->insn_off, 213*ab4e0744SJosh Poimboeuf &entry->orc)) 214627fce14SJosh Poimboeuf return -1; 215627fce14SJosh Poimboeuf } 216627fce14SJosh Poimboeuf 217*ab4e0744SJosh Poimboeuf if (elf_rebuild_reloc_section(file->elf, ip_rsec)) 218627fce14SJosh Poimboeuf return -1; 219627fce14SJosh Poimboeuf 220627fce14SJosh Poimboeuf return 0; 221627fce14SJosh Poimboeuf } 222