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 16ab4e0744SJosh Poimboeuf static int init_orc_entry(struct orc_entry *orc, struct cfi_state *cfi) 17627fce14SJosh Poimboeuf { 18ab4e0744SJosh Poimboeuf struct instruction *insn = container_of(cfi, struct instruction, cfi); 19ab4e0744SJosh Poimboeuf struct cfi_reg *bp = &cfi->regs[CFI_BP]; 20627fce14SJosh Poimboeuf 21ab4e0744SJosh Poimboeuf memset(orc, 0, sizeof(*orc)); 22627fce14SJosh Poimboeuf 23ab4e0744SJosh Poimboeuf orc->end = cfi->end; 243eaecac8SJulien Thierry 25ab4e0744SJosh Poimboeuf if (cfi->cfa.base == CFI_UNDEFINED) { 26627fce14SJosh Poimboeuf orc->sp_reg = ORC_REG_UNDEFINED; 27ab4e0744SJosh Poimboeuf return 0; 28627fce14SJosh Poimboeuf } 29627fce14SJosh Poimboeuf 30ab4e0744SJosh 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", 57ab4e0744SJosh 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 77ab4e0744SJosh Poimboeuf orc->sp_offset = cfi->cfa.offset; 78627fce14SJosh Poimboeuf orc->bp_offset = bp->offset; 79ab4e0744SJosh Poimboeuf orc->type = cfi->type; 80627fce14SJosh Poimboeuf 81627fce14SJosh Poimboeuf return 0; 82627fce14SJosh Poimboeuf } 83627fce14SJosh Poimboeuf 84ab4e0744SJosh Poimboeuf static int write_orc_entry(struct elf *elf, struct section *orc_sec, 85*ef47cc01SPeter Zijlstra struct section *ip_sec, unsigned int idx, 86ab4e0744SJosh Poimboeuf struct section *insn_sec, unsigned long insn_off, 87ab4e0744SJosh Poimboeuf struct orc_entry *o) 88627fce14SJosh Poimboeuf { 89627fce14SJosh Poimboeuf struct orc_entry *orc; 90627fce14SJosh Poimboeuf 91627fce14SJosh Poimboeuf /* populate ORC data */ 92ab4e0744SJosh Poimboeuf orc = (struct orc_entry *)orc_sec->data->d_buf + idx; 93627fce14SJosh Poimboeuf memcpy(orc, o, sizeof(*orc)); 948bfe2732SVasily Gorbik orc->sp_offset = bswap_if_needed(orc->sp_offset); 958bfe2732SVasily Gorbik orc->bp_offset = bswap_if_needed(orc->bp_offset); 96627fce14SJosh Poimboeuf 97f1974222SMatt Helsley /* populate reloc for ip */ 98*ef47cc01SPeter Zijlstra if (elf_add_reloc_to_insn(elf, ip_sec, idx * sizeof(int), R_X86_64_PC32, 99*ef47cc01SPeter Zijlstra insn_sec, insn_off)) 100627fce14SJosh Poimboeuf return -1; 101627fce14SJosh Poimboeuf 102627fce14SJosh Poimboeuf return 0; 103627fce14SJosh Poimboeuf } 104627fce14SJosh Poimboeuf 105ab4e0744SJosh Poimboeuf struct orc_list_entry { 106ab4e0744SJosh Poimboeuf struct list_head list; 107ab4e0744SJosh Poimboeuf struct orc_entry orc; 108ab4e0744SJosh Poimboeuf struct section *insn_sec; 109ab4e0744SJosh Poimboeuf unsigned long insn_off; 110ab4e0744SJosh Poimboeuf }; 111627fce14SJosh Poimboeuf 112ab4e0744SJosh Poimboeuf static int orc_list_add(struct list_head *orc_list, struct orc_entry *orc, 113ab4e0744SJosh Poimboeuf struct section *sec, unsigned long offset) 114ab4e0744SJosh Poimboeuf { 115ab4e0744SJosh Poimboeuf struct orc_list_entry *entry = malloc(sizeof(*entry)); 116ab4e0744SJosh Poimboeuf 117ab4e0744SJosh Poimboeuf if (!entry) { 118ab4e0744SJosh Poimboeuf WARN("malloc failed"); 119ab4e0744SJosh Poimboeuf return -1; 120ab4e0744SJosh Poimboeuf } 121ab4e0744SJosh Poimboeuf 122ab4e0744SJosh Poimboeuf entry->orc = *orc; 123ab4e0744SJosh Poimboeuf entry->insn_sec = sec; 124ab4e0744SJosh Poimboeuf entry->insn_off = offset; 125ab4e0744SJosh Poimboeuf 126ab4e0744SJosh Poimboeuf list_add_tail(&entry->list, orc_list); 127ab4e0744SJosh Poimboeuf return 0; 128ab4e0744SJosh Poimboeuf } 129ab4e0744SJosh Poimboeuf 130c9c324dcSJosh Poimboeuf static unsigned long alt_group_len(struct alt_group *alt_group) 131c9c324dcSJosh Poimboeuf { 132c9c324dcSJosh Poimboeuf return alt_group->last_insn->offset + 133c9c324dcSJosh Poimboeuf alt_group->last_insn->len - 134c9c324dcSJosh Poimboeuf alt_group->first_insn->offset; 135c9c324dcSJosh Poimboeuf } 136c9c324dcSJosh Poimboeuf 137ab4e0744SJosh Poimboeuf int orc_create(struct objtool_file *file) 138ab4e0744SJosh Poimboeuf { 139*ef47cc01SPeter Zijlstra struct section *sec, *orc_sec; 140ab4e0744SJosh Poimboeuf unsigned int nr = 0, idx = 0; 141ab4e0744SJosh Poimboeuf struct orc_list_entry *entry; 142ab4e0744SJosh Poimboeuf struct list_head orc_list; 143ab4e0744SJosh Poimboeuf 144ab4e0744SJosh Poimboeuf struct orc_entry null = { 145627fce14SJosh Poimboeuf .sp_reg = ORC_REG_UNDEFINED, 146627fce14SJosh Poimboeuf .bp_reg = ORC_REG_UNDEFINED, 147ee819aedSJulien Thierry .type = UNWIND_HINT_TYPE_CALL, 148627fce14SJosh Poimboeuf }; 149627fce14SJosh Poimboeuf 150ab4e0744SJosh Poimboeuf /* Build a deduplicated list of ORC entries: */ 151ab4e0744SJosh Poimboeuf INIT_LIST_HEAD(&orc_list); 152ab4e0744SJosh Poimboeuf for_each_sec(file, sec) { 153ab4e0744SJosh Poimboeuf struct orc_entry orc, prev_orc = {0}; 154ab4e0744SJosh Poimboeuf struct instruction *insn; 155ab4e0744SJosh Poimboeuf bool empty = true; 156ab4e0744SJosh Poimboeuf 157ab4e0744SJosh Poimboeuf if (!sec->text) 158ab4e0744SJosh Poimboeuf continue; 159ab4e0744SJosh Poimboeuf 160ab4e0744SJosh Poimboeuf sec_for_each_insn(file, sec, insn) { 161c9c324dcSJosh Poimboeuf struct alt_group *alt_group = insn->alt_group; 162c9c324dcSJosh Poimboeuf int i; 163c9c324dcSJosh Poimboeuf 164c9c324dcSJosh Poimboeuf if (!alt_group) { 165ab4e0744SJosh Poimboeuf if (init_orc_entry(&orc, &insn->cfi)) 166ab4e0744SJosh Poimboeuf return -1; 167ab4e0744SJosh Poimboeuf if (!memcmp(&prev_orc, &orc, sizeof(orc))) 168ab4e0744SJosh Poimboeuf continue; 169c9c324dcSJosh Poimboeuf if (orc_list_add(&orc_list, &orc, sec, 170c9c324dcSJosh Poimboeuf insn->offset)) 171ab4e0744SJosh Poimboeuf return -1; 172ab4e0744SJosh Poimboeuf nr++; 173ab4e0744SJosh Poimboeuf prev_orc = orc; 174ab4e0744SJosh Poimboeuf empty = false; 175c9c324dcSJosh Poimboeuf continue; 176c9c324dcSJosh Poimboeuf } 177c9c324dcSJosh Poimboeuf 178c9c324dcSJosh Poimboeuf /* 179c9c324dcSJosh Poimboeuf * Alternatives can have different stack layout 180c9c324dcSJosh Poimboeuf * possibilities (but they shouldn't conflict). 181c9c324dcSJosh Poimboeuf * Instead of traversing the instructions, use the 182c9c324dcSJosh Poimboeuf * alt_group's flattened byte-offset-addressed CFI 183c9c324dcSJosh Poimboeuf * array. 184c9c324dcSJosh Poimboeuf */ 185c9c324dcSJosh Poimboeuf for (i = 0; i < alt_group_len(alt_group); i++) { 186c9c324dcSJosh Poimboeuf struct cfi_state *cfi = alt_group->cfi[i]; 187c9c324dcSJosh Poimboeuf if (!cfi) 188c9c324dcSJosh Poimboeuf continue; 189c9c324dcSJosh Poimboeuf if (init_orc_entry(&orc, cfi)) 190c9c324dcSJosh Poimboeuf return -1; 191c9c324dcSJosh Poimboeuf if (!memcmp(&prev_orc, &orc, sizeof(orc))) 192c9c324dcSJosh Poimboeuf continue; 193c9c324dcSJosh Poimboeuf if (orc_list_add(&orc_list, &orc, insn->sec, 194c9c324dcSJosh Poimboeuf insn->offset + i)) 195c9c324dcSJosh Poimboeuf return -1; 196c9c324dcSJosh Poimboeuf nr++; 197c9c324dcSJosh Poimboeuf prev_orc = orc; 198c9c324dcSJosh Poimboeuf empty = false; 199c9c324dcSJosh Poimboeuf } 200c9c324dcSJosh Poimboeuf 201c9c324dcSJosh Poimboeuf /* Skip to the end of the alt_group */ 202c9c324dcSJosh Poimboeuf insn = alt_group->last_insn; 203ab4e0744SJosh Poimboeuf } 204ab4e0744SJosh Poimboeuf 205ab4e0744SJosh Poimboeuf /* Add a section terminator */ 206ab4e0744SJosh Poimboeuf if (!empty) { 207ab4e0744SJosh Poimboeuf orc_list_add(&orc_list, &null, sec, sec->len); 208ab4e0744SJosh Poimboeuf nr++; 209ab4e0744SJosh Poimboeuf } 210ab4e0744SJosh Poimboeuf } 211ab4e0744SJosh Poimboeuf if (!nr) 212ab4e0744SJosh Poimboeuf return 0; 213ab4e0744SJosh Poimboeuf 214ab4e0744SJosh Poimboeuf /* Create .orc_unwind, .orc_unwind_ip and .rela.orc_unwind_ip sections: */ 215627fce14SJosh Poimboeuf sec = find_section_by_name(file->elf, ".orc_unwind"); 216627fce14SJosh Poimboeuf if (sec) { 217627fce14SJosh Poimboeuf WARN("file already has .orc_unwind section, skipping"); 218627fce14SJosh Poimboeuf return -1; 219627fce14SJosh Poimboeuf } 220ab4e0744SJosh Poimboeuf orc_sec = elf_create_section(file->elf, ".orc_unwind", 0, 221ab4e0744SJosh Poimboeuf sizeof(struct orc_entry), nr); 222ab4e0744SJosh Poimboeuf if (!orc_sec) 223627fce14SJosh Poimboeuf return -1; 224627fce14SJosh Poimboeuf 225ab4e0744SJosh Poimboeuf sec = elf_create_section(file->elf, ".orc_unwind_ip", 0, sizeof(int), nr); 226ce90aaf5SSimon Ser if (!sec) 227ce90aaf5SSimon Ser return -1; 228*ef47cc01SPeter Zijlstra if (!elf_create_reloc_section(file->elf, sec, SHT_RELA)) 229627fce14SJosh Poimboeuf return -1; 230627fce14SJosh Poimboeuf 231ab4e0744SJosh Poimboeuf /* Write ORC entries to sections: */ 232ab4e0744SJosh Poimboeuf list_for_each_entry(entry, &orc_list, list) { 233*ef47cc01SPeter Zijlstra if (write_orc_entry(file->elf, orc_sec, sec, idx++, 234ab4e0744SJosh Poimboeuf entry->insn_sec, entry->insn_off, 235ab4e0744SJosh Poimboeuf &entry->orc)) 236627fce14SJosh Poimboeuf return -1; 237627fce14SJosh Poimboeuf } 238627fce14SJosh Poimboeuf 239627fce14SJosh Poimboeuf return 0; 240627fce14SJosh Poimboeuf } 241