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 12627fce14SJosh Poimboeuf #include "check.h" 13627fce14SJosh Poimboeuf #include "warn.h" 14627fce14SJosh Poimboeuf 15627fce14SJosh Poimboeuf int create_orc(struct objtool_file *file) 16627fce14SJosh Poimboeuf { 17627fce14SJosh Poimboeuf struct instruction *insn; 18627fce14SJosh Poimboeuf 19627fce14SJosh Poimboeuf for_each_insn(file, insn) { 20627fce14SJosh Poimboeuf struct orc_entry *orc = &insn->orc; 21e7c0219bSPeter Zijlstra struct cfi_reg *cfa = &insn->cfi.cfa; 22e7c0219bSPeter Zijlstra struct cfi_reg *bp = &insn->cfi.regs[CFI_BP]; 23627fce14SJosh Poimboeuf 243eaecac8SJulien Thierry if (!insn->sec->text) 253eaecac8SJulien Thierry continue; 263eaecac8SJulien Thierry 27e7c0219bSPeter Zijlstra orc->end = insn->cfi.end; 28d31a5802SJosh Poimboeuf 29627fce14SJosh Poimboeuf if (cfa->base == CFI_UNDEFINED) { 30627fce14SJosh Poimboeuf orc->sp_reg = ORC_REG_UNDEFINED; 31627fce14SJosh Poimboeuf continue; 32627fce14SJosh Poimboeuf } 33627fce14SJosh Poimboeuf 34627fce14SJosh Poimboeuf switch (cfa->base) { 35627fce14SJosh Poimboeuf case CFI_SP: 36627fce14SJosh Poimboeuf orc->sp_reg = ORC_REG_SP; 37627fce14SJosh Poimboeuf break; 38627fce14SJosh Poimboeuf case CFI_SP_INDIRECT: 39627fce14SJosh Poimboeuf orc->sp_reg = ORC_REG_SP_INDIRECT; 40627fce14SJosh Poimboeuf break; 41627fce14SJosh Poimboeuf case CFI_BP: 42627fce14SJosh Poimboeuf orc->sp_reg = ORC_REG_BP; 43627fce14SJosh Poimboeuf break; 44627fce14SJosh Poimboeuf case CFI_BP_INDIRECT: 45627fce14SJosh Poimboeuf orc->sp_reg = ORC_REG_BP_INDIRECT; 46627fce14SJosh Poimboeuf break; 47627fce14SJosh Poimboeuf case CFI_R10: 48627fce14SJosh Poimboeuf orc->sp_reg = ORC_REG_R10; 49627fce14SJosh Poimboeuf break; 50627fce14SJosh Poimboeuf case CFI_R13: 51627fce14SJosh Poimboeuf orc->sp_reg = ORC_REG_R13; 52627fce14SJosh Poimboeuf break; 53627fce14SJosh Poimboeuf case CFI_DI: 54627fce14SJosh Poimboeuf orc->sp_reg = ORC_REG_DI; 55627fce14SJosh Poimboeuf break; 56627fce14SJosh Poimboeuf case CFI_DX: 57627fce14SJosh Poimboeuf orc->sp_reg = ORC_REG_DX; 58627fce14SJosh Poimboeuf break; 59627fce14SJosh Poimboeuf default: 60627fce14SJosh Poimboeuf WARN_FUNC("unknown CFA base reg %d", 61627fce14SJosh Poimboeuf insn->sec, insn->offset, cfa->base); 62627fce14SJosh Poimboeuf return -1; 63627fce14SJosh Poimboeuf } 64627fce14SJosh Poimboeuf 65627fce14SJosh Poimboeuf switch(bp->base) { 66627fce14SJosh Poimboeuf case CFI_UNDEFINED: 67627fce14SJosh Poimboeuf orc->bp_reg = ORC_REG_UNDEFINED; 68627fce14SJosh Poimboeuf break; 69627fce14SJosh Poimboeuf case CFI_CFA: 70627fce14SJosh Poimboeuf orc->bp_reg = ORC_REG_PREV_SP; 71627fce14SJosh Poimboeuf break; 72627fce14SJosh Poimboeuf case CFI_BP: 73627fce14SJosh Poimboeuf orc->bp_reg = ORC_REG_BP; 74627fce14SJosh Poimboeuf break; 75627fce14SJosh Poimboeuf default: 76627fce14SJosh Poimboeuf WARN_FUNC("unknown BP base reg %d", 77627fce14SJosh Poimboeuf insn->sec, insn->offset, bp->base); 78627fce14SJosh Poimboeuf return -1; 79627fce14SJosh Poimboeuf } 80627fce14SJosh Poimboeuf 81627fce14SJosh Poimboeuf orc->sp_offset = cfa->offset; 82627fce14SJosh Poimboeuf orc->bp_offset = bp->offset; 83e7c0219bSPeter Zijlstra orc->type = insn->cfi.type; 84627fce14SJosh Poimboeuf } 85627fce14SJosh Poimboeuf 86627fce14SJosh Poimboeuf return 0; 87627fce14SJosh Poimboeuf } 88627fce14SJosh Poimboeuf 89f1974222SMatt Helsley static int create_orc_entry(struct elf *elf, struct section *u_sec, struct section *ip_relocsec, 90627fce14SJosh Poimboeuf unsigned int idx, struct section *insn_sec, 91627fce14SJosh Poimboeuf unsigned long insn_off, struct orc_entry *o) 92627fce14SJosh Poimboeuf { 93627fce14SJosh Poimboeuf struct orc_entry *orc; 94f1974222SMatt Helsley struct reloc *reloc; 95627fce14SJosh Poimboeuf 96627fce14SJosh Poimboeuf /* populate ORC data */ 97627fce14SJosh Poimboeuf orc = (struct orc_entry *)u_sec->data->d_buf + idx; 98627fce14SJosh Poimboeuf memcpy(orc, o, sizeof(*orc)); 99627fce14SJosh Poimboeuf 100f1974222SMatt Helsley /* populate reloc for ip */ 101f1974222SMatt Helsley reloc = malloc(sizeof(*reloc)); 102f1974222SMatt Helsley if (!reloc) { 103627fce14SJosh Poimboeuf perror("malloc"); 104627fce14SJosh Poimboeuf return -1; 105627fce14SJosh Poimboeuf } 106f1974222SMatt Helsley memset(reloc, 0, sizeof(*reloc)); 107627fce14SJosh Poimboeuf 108*44f6a7c0SJosh Poimboeuf insn_to_reloc_sym_addend(insn_sec, insn_off, reloc); 109f1974222SMatt Helsley if (!reloc->sym) { 110*44f6a7c0SJosh Poimboeuf WARN("missing symbol for insn at offset 0x%lx", 111e81e0724SJosh Poimboeuf insn_off); 112e81e0724SJosh Poimboeuf return -1; 113e81e0724SJosh Poimboeuf } 114e81e0724SJosh Poimboeuf 115f1974222SMatt Helsley reloc->type = R_X86_64_PC32; 116f1974222SMatt Helsley reloc->offset = idx * sizeof(int); 117f1974222SMatt Helsley reloc->sec = ip_relocsec; 118627fce14SJosh Poimboeuf 119f1974222SMatt Helsley elf_add_reloc(elf, reloc); 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; 127f1974222SMatt Helsley struct section *sec, *u_sec, *ip_relocsec; 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, 133ee819aedSJulien Thierry .type = UNWIND_HINT_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 */ 1671e7e4788SJosh Poimboeuf sec = elf_create_section(file->elf, ".orc_unwind_ip", 0, sizeof(int), idx); 168ce90aaf5SSimon Ser if (!sec) 169ce90aaf5SSimon Ser return -1; 170627fce14SJosh Poimboeuf 171fb414783SMatt Helsley ip_relocsec = elf_create_reloc_section(file->elf, sec, SHT_RELA); 172f1974222SMatt Helsley if (!ip_relocsec) 173627fce14SJosh Poimboeuf return -1; 174627fce14SJosh Poimboeuf 175627fce14SJosh Poimboeuf /* create .orc_unwind section */ 1761e7e4788SJosh Poimboeuf u_sec = elf_create_section(file->elf, ".orc_unwind", 0, 177627fce14SJosh Poimboeuf sizeof(struct orc_entry), idx); 178627fce14SJosh Poimboeuf 179627fce14SJosh Poimboeuf /* populate sections */ 180627fce14SJosh Poimboeuf idx = 0; 181627fce14SJosh Poimboeuf for_each_sec(file, sec) { 182627fce14SJosh Poimboeuf if (!sec->text) 183627fce14SJosh Poimboeuf continue; 184627fce14SJosh Poimboeuf 185627fce14SJosh Poimboeuf prev_insn = NULL; 186627fce14SJosh Poimboeuf sec_for_each_insn(file, sec, insn) { 187627fce14SJosh Poimboeuf if (!prev_insn || memcmp(&insn->orc, &prev_insn->orc, 188627fce14SJosh Poimboeuf sizeof(struct orc_entry))) { 189627fce14SJosh Poimboeuf 190f1974222SMatt Helsley if (create_orc_entry(file->elf, u_sec, ip_relocsec, idx, 191627fce14SJosh Poimboeuf insn->sec, insn->offset, 192627fce14SJosh Poimboeuf &insn->orc)) 193627fce14SJosh Poimboeuf return -1; 194627fce14SJosh Poimboeuf 195627fce14SJosh Poimboeuf idx++; 196627fce14SJosh Poimboeuf } 197627fce14SJosh Poimboeuf prev_insn = insn; 198627fce14SJosh Poimboeuf } 199627fce14SJosh Poimboeuf 200627fce14SJosh Poimboeuf /* section terminator */ 201627fce14SJosh Poimboeuf if (prev_insn) { 202f1974222SMatt Helsley if (create_orc_entry(file->elf, u_sec, ip_relocsec, idx, 203627fce14SJosh Poimboeuf prev_insn->sec, 204627fce14SJosh Poimboeuf prev_insn->offset + prev_insn->len, 205627fce14SJosh Poimboeuf &empty)) 206627fce14SJosh Poimboeuf return -1; 207627fce14SJosh Poimboeuf 208627fce14SJosh Poimboeuf idx++; 209627fce14SJosh Poimboeuf } 210627fce14SJosh Poimboeuf } 211627fce14SJosh Poimboeuf 212d832c005SPeter Zijlstra if (elf_rebuild_reloc_section(file->elf, ip_relocsec)) 213627fce14SJosh Poimboeuf return -1; 214627fce14SJosh Poimboeuf 215627fce14SJosh Poimboeuf return 0; 216627fce14SJosh Poimboeuf } 217