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 168b946cc3SPeter Zijlstra static int init_orc_entry(struct orc_entry *orc, struct cfi_state *cfi, 178b946cc3SPeter Zijlstra struct instruction *insn) 18627fce14SJosh Poimboeuf { 19ab4e0744SJosh Poimboeuf struct cfi_reg *bp = &cfi->regs[CFI_BP]; 20627fce14SJosh Poimboeuf 21ab4e0744SJosh Poimboeuf memset(orc, 0, sizeof(*orc)); 22627fce14SJosh Poimboeuf 238b946cc3SPeter Zijlstra if (!cfi) { 248b946cc3SPeter Zijlstra orc->end = 0; 258b946cc3SPeter Zijlstra orc->sp_reg = ORC_REG_UNDEFINED; 268b946cc3SPeter Zijlstra return 0; 278b946cc3SPeter Zijlstra } 288b946cc3SPeter Zijlstra 29ab4e0744SJosh Poimboeuf orc->end = cfi->end; 30*00c8f01cSJosh Poimboeuf orc->signal = cfi->signal; 313eaecac8SJulien Thierry 32ab4e0744SJosh Poimboeuf if (cfi->cfa.base == CFI_UNDEFINED) { 33627fce14SJosh Poimboeuf orc->sp_reg = ORC_REG_UNDEFINED; 34ab4e0744SJosh Poimboeuf return 0; 35627fce14SJosh Poimboeuf } 36627fce14SJosh Poimboeuf 37ab4e0744SJosh Poimboeuf switch (cfi->cfa.base) { 38627fce14SJosh Poimboeuf case CFI_SP: 39627fce14SJosh Poimboeuf orc->sp_reg = ORC_REG_SP; 40627fce14SJosh Poimboeuf break; 41627fce14SJosh Poimboeuf case CFI_SP_INDIRECT: 42627fce14SJosh Poimboeuf orc->sp_reg = ORC_REG_SP_INDIRECT; 43627fce14SJosh Poimboeuf break; 44627fce14SJosh Poimboeuf case CFI_BP: 45627fce14SJosh Poimboeuf orc->sp_reg = ORC_REG_BP; 46627fce14SJosh Poimboeuf break; 47627fce14SJosh Poimboeuf case CFI_BP_INDIRECT: 48627fce14SJosh Poimboeuf orc->sp_reg = ORC_REG_BP_INDIRECT; 49627fce14SJosh Poimboeuf break; 50627fce14SJosh Poimboeuf case CFI_R10: 51627fce14SJosh Poimboeuf orc->sp_reg = ORC_REG_R10; 52627fce14SJosh Poimboeuf break; 53627fce14SJosh Poimboeuf case CFI_R13: 54627fce14SJosh Poimboeuf orc->sp_reg = ORC_REG_R13; 55627fce14SJosh Poimboeuf break; 56627fce14SJosh Poimboeuf case CFI_DI: 57627fce14SJosh Poimboeuf orc->sp_reg = ORC_REG_DI; 58627fce14SJosh Poimboeuf break; 59627fce14SJosh Poimboeuf case CFI_DX: 60627fce14SJosh Poimboeuf orc->sp_reg = ORC_REG_DX; 61627fce14SJosh Poimboeuf break; 62627fce14SJosh Poimboeuf default: 63627fce14SJosh Poimboeuf WARN_FUNC("unknown CFA base reg %d", 64ab4e0744SJosh Poimboeuf insn->sec, insn->offset, cfi->cfa.base); 65627fce14SJosh Poimboeuf return -1; 66627fce14SJosh Poimboeuf } 67627fce14SJosh Poimboeuf 68627fce14SJosh Poimboeuf switch (bp->base) { 69627fce14SJosh Poimboeuf case CFI_UNDEFINED: 70627fce14SJosh Poimboeuf orc->bp_reg = ORC_REG_UNDEFINED; 71627fce14SJosh Poimboeuf break; 72627fce14SJosh Poimboeuf case CFI_CFA: 73627fce14SJosh Poimboeuf orc->bp_reg = ORC_REG_PREV_SP; 74627fce14SJosh Poimboeuf break; 75627fce14SJosh Poimboeuf case CFI_BP: 76627fce14SJosh Poimboeuf orc->bp_reg = ORC_REG_BP; 77627fce14SJosh Poimboeuf break; 78627fce14SJosh Poimboeuf default: 79627fce14SJosh Poimboeuf WARN_FUNC("unknown BP base reg %d", 80627fce14SJosh Poimboeuf insn->sec, insn->offset, bp->base); 81627fce14SJosh Poimboeuf return -1; 82627fce14SJosh Poimboeuf } 83627fce14SJosh Poimboeuf 84ab4e0744SJosh Poimboeuf orc->sp_offset = cfi->cfa.offset; 85627fce14SJosh Poimboeuf orc->bp_offset = bp->offset; 86ab4e0744SJosh Poimboeuf orc->type = cfi->type; 87627fce14SJosh Poimboeuf 88627fce14SJosh Poimboeuf return 0; 89627fce14SJosh Poimboeuf } 90627fce14SJosh Poimboeuf 91ab4e0744SJosh Poimboeuf static int write_orc_entry(struct elf *elf, struct section *orc_sec, 92ef47cc01SPeter Zijlstra struct section *ip_sec, unsigned int idx, 93ab4e0744SJosh Poimboeuf struct section *insn_sec, unsigned long insn_off, 94ab4e0744SJosh Poimboeuf struct orc_entry *o) 95627fce14SJosh Poimboeuf { 96627fce14SJosh Poimboeuf struct orc_entry *orc; 97627fce14SJosh Poimboeuf 98627fce14SJosh Poimboeuf /* populate ORC data */ 99ab4e0744SJosh Poimboeuf orc = (struct orc_entry *)orc_sec->data->d_buf + idx; 100627fce14SJosh Poimboeuf memcpy(orc, o, sizeof(*orc)); 1010646c28bSChristophe Leroy orc->sp_offset = bswap_if_needed(elf, orc->sp_offset); 1020646c28bSChristophe Leroy orc->bp_offset = bswap_if_needed(elf, orc->bp_offset); 103627fce14SJosh Poimboeuf 104f1974222SMatt Helsley /* populate reloc for ip */ 105ef47cc01SPeter Zijlstra if (elf_add_reloc_to_insn(elf, ip_sec, idx * sizeof(int), R_X86_64_PC32, 106ef47cc01SPeter Zijlstra insn_sec, insn_off)) 107627fce14SJosh Poimboeuf return -1; 108627fce14SJosh Poimboeuf 109627fce14SJosh Poimboeuf return 0; 110627fce14SJosh Poimboeuf } 111627fce14SJosh Poimboeuf 112ab4e0744SJosh Poimboeuf struct orc_list_entry { 113ab4e0744SJosh Poimboeuf struct list_head list; 114ab4e0744SJosh Poimboeuf struct orc_entry orc; 115ab4e0744SJosh Poimboeuf struct section *insn_sec; 116ab4e0744SJosh Poimboeuf unsigned long insn_off; 117ab4e0744SJosh Poimboeuf }; 118627fce14SJosh Poimboeuf 119ab4e0744SJosh Poimboeuf static int orc_list_add(struct list_head *orc_list, struct orc_entry *orc, 120ab4e0744SJosh Poimboeuf struct section *sec, unsigned long offset) 121ab4e0744SJosh Poimboeuf { 122ab4e0744SJosh Poimboeuf struct orc_list_entry *entry = malloc(sizeof(*entry)); 123ab4e0744SJosh Poimboeuf 124ab4e0744SJosh Poimboeuf if (!entry) { 125ab4e0744SJosh Poimboeuf WARN("malloc failed"); 126ab4e0744SJosh Poimboeuf return -1; 127ab4e0744SJosh Poimboeuf } 128ab4e0744SJosh Poimboeuf 129ab4e0744SJosh Poimboeuf entry->orc = *orc; 130ab4e0744SJosh Poimboeuf entry->insn_sec = sec; 131ab4e0744SJosh Poimboeuf entry->insn_off = offset; 132ab4e0744SJosh Poimboeuf 133ab4e0744SJosh Poimboeuf list_add_tail(&entry->list, orc_list); 134ab4e0744SJosh Poimboeuf return 0; 135ab4e0744SJosh Poimboeuf } 136ab4e0744SJosh Poimboeuf 137c9c324dcSJosh Poimboeuf static unsigned long alt_group_len(struct alt_group *alt_group) 138c9c324dcSJosh Poimboeuf { 139c9c324dcSJosh Poimboeuf return alt_group->last_insn->offset + 140c9c324dcSJosh Poimboeuf alt_group->last_insn->len - 141c9c324dcSJosh Poimboeuf alt_group->first_insn->offset; 142c9c324dcSJosh Poimboeuf } 143c9c324dcSJosh Poimboeuf 144ab4e0744SJosh Poimboeuf int orc_create(struct objtool_file *file) 145ab4e0744SJosh Poimboeuf { 146ef47cc01SPeter Zijlstra struct section *sec, *orc_sec; 147ab4e0744SJosh Poimboeuf unsigned int nr = 0, idx = 0; 148ab4e0744SJosh Poimboeuf struct orc_list_entry *entry; 149ab4e0744SJosh Poimboeuf struct list_head orc_list; 150ab4e0744SJosh Poimboeuf 151ab4e0744SJosh Poimboeuf struct orc_entry null = { 152627fce14SJosh Poimboeuf .sp_reg = ORC_REG_UNDEFINED, 153627fce14SJosh Poimboeuf .bp_reg = ORC_REG_UNDEFINED, 154ee819aedSJulien Thierry .type = UNWIND_HINT_TYPE_CALL, 155627fce14SJosh Poimboeuf }; 156627fce14SJosh Poimboeuf 157ab4e0744SJosh Poimboeuf /* Build a deduplicated list of ORC entries: */ 158ab4e0744SJosh Poimboeuf INIT_LIST_HEAD(&orc_list); 159ab4e0744SJosh Poimboeuf for_each_sec(file, sec) { 160ab4e0744SJosh Poimboeuf struct orc_entry orc, prev_orc = {0}; 161ab4e0744SJosh Poimboeuf struct instruction *insn; 162ab4e0744SJosh Poimboeuf bool empty = true; 163ab4e0744SJosh Poimboeuf 164ab4e0744SJosh Poimboeuf if (!sec->text) 165ab4e0744SJosh Poimboeuf continue; 166ab4e0744SJosh Poimboeuf 167ab4e0744SJosh Poimboeuf sec_for_each_insn(file, sec, insn) { 168c9c324dcSJosh Poimboeuf struct alt_group *alt_group = insn->alt_group; 169c9c324dcSJosh Poimboeuf int i; 170c9c324dcSJosh Poimboeuf 171c9c324dcSJosh Poimboeuf if (!alt_group) { 1728b946cc3SPeter Zijlstra if (init_orc_entry(&orc, insn->cfi, insn)) 173ab4e0744SJosh Poimboeuf return -1; 174ab4e0744SJosh Poimboeuf if (!memcmp(&prev_orc, &orc, sizeof(orc))) 175ab4e0744SJosh Poimboeuf continue; 176c9c324dcSJosh Poimboeuf if (orc_list_add(&orc_list, &orc, sec, 177c9c324dcSJosh Poimboeuf insn->offset)) 178ab4e0744SJosh Poimboeuf return -1; 179ab4e0744SJosh Poimboeuf nr++; 180ab4e0744SJosh Poimboeuf prev_orc = orc; 181ab4e0744SJosh Poimboeuf empty = false; 182c9c324dcSJosh Poimboeuf continue; 183c9c324dcSJosh Poimboeuf } 184c9c324dcSJosh Poimboeuf 185c9c324dcSJosh Poimboeuf /* 186c9c324dcSJosh Poimboeuf * Alternatives can have different stack layout 187c9c324dcSJosh Poimboeuf * possibilities (but they shouldn't conflict). 188c9c324dcSJosh Poimboeuf * Instead of traversing the instructions, use the 189c9c324dcSJosh Poimboeuf * alt_group's flattened byte-offset-addressed CFI 190c9c324dcSJosh Poimboeuf * array. 191c9c324dcSJosh Poimboeuf */ 192c9c324dcSJosh Poimboeuf for (i = 0; i < alt_group_len(alt_group); i++) { 193c9c324dcSJosh Poimboeuf struct cfi_state *cfi = alt_group->cfi[i]; 194c9c324dcSJosh Poimboeuf if (!cfi) 195c9c324dcSJosh Poimboeuf continue; 1968b946cc3SPeter Zijlstra /* errors are reported on the original insn */ 1978b946cc3SPeter Zijlstra if (init_orc_entry(&orc, cfi, insn)) 198c9c324dcSJosh Poimboeuf return -1; 199c9c324dcSJosh Poimboeuf if (!memcmp(&prev_orc, &orc, sizeof(orc))) 200c9c324dcSJosh Poimboeuf continue; 201c9c324dcSJosh Poimboeuf if (orc_list_add(&orc_list, &orc, insn->sec, 202c9c324dcSJosh Poimboeuf insn->offset + i)) 203c9c324dcSJosh Poimboeuf return -1; 204c9c324dcSJosh Poimboeuf nr++; 205c9c324dcSJosh Poimboeuf prev_orc = orc; 206c9c324dcSJosh Poimboeuf empty = false; 207c9c324dcSJosh Poimboeuf } 208c9c324dcSJosh Poimboeuf 209c9c324dcSJosh Poimboeuf /* Skip to the end of the alt_group */ 210c9c324dcSJosh Poimboeuf insn = alt_group->last_insn; 211ab4e0744SJosh Poimboeuf } 212ab4e0744SJosh Poimboeuf 213ab4e0744SJosh Poimboeuf /* Add a section terminator */ 214ab4e0744SJosh Poimboeuf if (!empty) { 215fe255fe6SJoe Lawrence orc_list_add(&orc_list, &null, sec, sec->sh.sh_size); 216ab4e0744SJosh Poimboeuf nr++; 217ab4e0744SJosh Poimboeuf } 218ab4e0744SJosh Poimboeuf } 219ab4e0744SJosh Poimboeuf if (!nr) 220ab4e0744SJosh Poimboeuf return 0; 221ab4e0744SJosh Poimboeuf 222ab4e0744SJosh Poimboeuf /* Create .orc_unwind, .orc_unwind_ip and .rela.orc_unwind_ip sections: */ 223627fce14SJosh Poimboeuf sec = find_section_by_name(file->elf, ".orc_unwind"); 224627fce14SJosh Poimboeuf if (sec) { 225627fce14SJosh Poimboeuf WARN("file already has .orc_unwind section, skipping"); 226627fce14SJosh Poimboeuf return -1; 227627fce14SJosh Poimboeuf } 228ab4e0744SJosh Poimboeuf orc_sec = elf_create_section(file->elf, ".orc_unwind", 0, 229ab4e0744SJosh Poimboeuf sizeof(struct orc_entry), nr); 230ab4e0744SJosh Poimboeuf if (!orc_sec) 231627fce14SJosh Poimboeuf return -1; 232627fce14SJosh Poimboeuf 233ab4e0744SJosh Poimboeuf sec = elf_create_section(file->elf, ".orc_unwind_ip", 0, sizeof(int), nr); 234ce90aaf5SSimon Ser if (!sec) 235ce90aaf5SSimon Ser return -1; 236627fce14SJosh Poimboeuf 237ab4e0744SJosh Poimboeuf /* Write ORC entries to sections: */ 238ab4e0744SJosh Poimboeuf list_for_each_entry(entry, &orc_list, list) { 239ef47cc01SPeter Zijlstra if (write_orc_entry(file->elf, orc_sec, sec, idx++, 240ab4e0744SJosh Poimboeuf entry->insn_sec, entry->insn_off, 241ab4e0744SJosh Poimboeuf &entry->orc)) 242627fce14SJosh Poimboeuf return -1; 243627fce14SJosh Poimboeuf } 244627fce14SJosh Poimboeuf 245627fce14SJosh Poimboeuf return 0; 246627fce14SJosh Poimboeuf } 247