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" 14*8bfe2732SVasily Gorbik #include "endianness.h" 15627fce14SJosh Poimboeuf 16627fce14SJosh Poimboeuf int create_orc(struct objtool_file *file) 17627fce14SJosh Poimboeuf { 18627fce14SJosh Poimboeuf struct instruction *insn; 19627fce14SJosh Poimboeuf 20627fce14SJosh Poimboeuf for_each_insn(file, insn) { 21627fce14SJosh Poimboeuf struct orc_entry *orc = &insn->orc; 22e7c0219bSPeter Zijlstra struct cfi_reg *cfa = &insn->cfi.cfa; 23e7c0219bSPeter Zijlstra struct cfi_reg *bp = &insn->cfi.regs[CFI_BP]; 24627fce14SJosh Poimboeuf 253eaecac8SJulien Thierry if (!insn->sec->text) 263eaecac8SJulien Thierry continue; 273eaecac8SJulien Thierry 28e7c0219bSPeter Zijlstra orc->end = insn->cfi.end; 29d31a5802SJosh Poimboeuf 30627fce14SJosh Poimboeuf if (cfa->base == CFI_UNDEFINED) { 31627fce14SJosh Poimboeuf orc->sp_reg = ORC_REG_UNDEFINED; 32627fce14SJosh Poimboeuf continue; 33627fce14SJosh Poimboeuf } 34627fce14SJosh Poimboeuf 35627fce14SJosh Poimboeuf switch (cfa->base) { 36627fce14SJosh Poimboeuf case CFI_SP: 37627fce14SJosh Poimboeuf orc->sp_reg = ORC_REG_SP; 38627fce14SJosh Poimboeuf break; 39627fce14SJosh Poimboeuf case CFI_SP_INDIRECT: 40627fce14SJosh Poimboeuf orc->sp_reg = ORC_REG_SP_INDIRECT; 41627fce14SJosh Poimboeuf break; 42627fce14SJosh Poimboeuf case CFI_BP: 43627fce14SJosh Poimboeuf orc->sp_reg = ORC_REG_BP; 44627fce14SJosh Poimboeuf break; 45627fce14SJosh Poimboeuf case CFI_BP_INDIRECT: 46627fce14SJosh Poimboeuf orc->sp_reg = ORC_REG_BP_INDIRECT; 47627fce14SJosh Poimboeuf break; 48627fce14SJosh Poimboeuf case CFI_R10: 49627fce14SJosh Poimboeuf orc->sp_reg = ORC_REG_R10; 50627fce14SJosh Poimboeuf break; 51627fce14SJosh Poimboeuf case CFI_R13: 52627fce14SJosh Poimboeuf orc->sp_reg = ORC_REG_R13; 53627fce14SJosh Poimboeuf break; 54627fce14SJosh Poimboeuf case CFI_DI: 55627fce14SJosh Poimboeuf orc->sp_reg = ORC_REG_DI; 56627fce14SJosh Poimboeuf break; 57627fce14SJosh Poimboeuf case CFI_DX: 58627fce14SJosh Poimboeuf orc->sp_reg = ORC_REG_DX; 59627fce14SJosh Poimboeuf break; 60627fce14SJosh Poimboeuf default: 61627fce14SJosh Poimboeuf WARN_FUNC("unknown CFA base reg %d", 62627fce14SJosh Poimboeuf insn->sec, insn->offset, cfa->base); 63627fce14SJosh Poimboeuf return -1; 64627fce14SJosh Poimboeuf } 65627fce14SJosh Poimboeuf 66627fce14SJosh Poimboeuf switch(bp->base) { 67627fce14SJosh Poimboeuf case CFI_UNDEFINED: 68627fce14SJosh Poimboeuf orc->bp_reg = ORC_REG_UNDEFINED; 69627fce14SJosh Poimboeuf break; 70627fce14SJosh Poimboeuf case CFI_CFA: 71627fce14SJosh Poimboeuf orc->bp_reg = ORC_REG_PREV_SP; 72627fce14SJosh Poimboeuf break; 73627fce14SJosh Poimboeuf case CFI_BP: 74627fce14SJosh Poimboeuf orc->bp_reg = ORC_REG_BP; 75627fce14SJosh Poimboeuf break; 76627fce14SJosh Poimboeuf default: 77627fce14SJosh Poimboeuf WARN_FUNC("unknown BP base reg %d", 78627fce14SJosh Poimboeuf insn->sec, insn->offset, bp->base); 79627fce14SJosh Poimboeuf return -1; 80627fce14SJosh Poimboeuf } 81627fce14SJosh Poimboeuf 82627fce14SJosh Poimboeuf orc->sp_offset = cfa->offset; 83627fce14SJosh Poimboeuf orc->bp_offset = bp->offset; 84e7c0219bSPeter Zijlstra orc->type = insn->cfi.type; 85627fce14SJosh Poimboeuf } 86627fce14SJosh Poimboeuf 87627fce14SJosh Poimboeuf return 0; 88627fce14SJosh Poimboeuf } 89627fce14SJosh Poimboeuf 90f1974222SMatt Helsley static int create_orc_entry(struct elf *elf, struct section *u_sec, struct section *ip_relocsec, 91627fce14SJosh Poimboeuf unsigned int idx, struct section *insn_sec, 92627fce14SJosh Poimboeuf unsigned long insn_off, struct orc_entry *o) 93627fce14SJosh Poimboeuf { 94627fce14SJosh Poimboeuf struct orc_entry *orc; 95f1974222SMatt Helsley struct reloc *reloc; 96627fce14SJosh Poimboeuf 97627fce14SJosh Poimboeuf /* populate ORC data */ 98627fce14SJosh Poimboeuf orc = (struct orc_entry *)u_sec->data->d_buf + idx; 99627fce14SJosh Poimboeuf memcpy(orc, o, sizeof(*orc)); 100*8bfe2732SVasily Gorbik orc->sp_offset = bswap_if_needed(orc->sp_offset); 101*8bfe2732SVasily Gorbik orc->bp_offset = bswap_if_needed(orc->bp_offset); 102627fce14SJosh Poimboeuf 103f1974222SMatt Helsley /* populate reloc for ip */ 104f1974222SMatt Helsley reloc = malloc(sizeof(*reloc)); 105f1974222SMatt Helsley if (!reloc) { 106627fce14SJosh Poimboeuf perror("malloc"); 107627fce14SJosh Poimboeuf return -1; 108627fce14SJosh Poimboeuf } 109f1974222SMatt Helsley memset(reloc, 0, sizeof(*reloc)); 110627fce14SJosh Poimboeuf 11144f6a7c0SJosh Poimboeuf insn_to_reloc_sym_addend(insn_sec, insn_off, reloc); 112f1974222SMatt Helsley if (!reloc->sym) { 11344f6a7c0SJosh Poimboeuf WARN("missing symbol for insn at offset 0x%lx", 114e81e0724SJosh Poimboeuf insn_off); 115e81e0724SJosh Poimboeuf return -1; 116e81e0724SJosh Poimboeuf } 117e81e0724SJosh Poimboeuf 118f1974222SMatt Helsley reloc->type = R_X86_64_PC32; 119f1974222SMatt Helsley reloc->offset = idx * sizeof(int); 120f1974222SMatt Helsley reloc->sec = ip_relocsec; 121627fce14SJosh Poimboeuf 122f1974222SMatt Helsley elf_add_reloc(elf, reloc); 123627fce14SJosh Poimboeuf 124627fce14SJosh Poimboeuf return 0; 125627fce14SJosh Poimboeuf } 126627fce14SJosh Poimboeuf 127627fce14SJosh Poimboeuf int create_orc_sections(struct objtool_file *file) 128627fce14SJosh Poimboeuf { 129627fce14SJosh Poimboeuf struct instruction *insn, *prev_insn; 130f1974222SMatt Helsley struct section *sec, *u_sec, *ip_relocsec; 131627fce14SJosh Poimboeuf unsigned int idx; 132627fce14SJosh Poimboeuf 133627fce14SJosh Poimboeuf struct orc_entry empty = { 134627fce14SJosh Poimboeuf .sp_reg = ORC_REG_UNDEFINED, 135627fce14SJosh Poimboeuf .bp_reg = ORC_REG_UNDEFINED, 136ee819aedSJulien Thierry .type = UNWIND_HINT_TYPE_CALL, 137627fce14SJosh Poimboeuf }; 138627fce14SJosh Poimboeuf 139627fce14SJosh Poimboeuf sec = find_section_by_name(file->elf, ".orc_unwind"); 140627fce14SJosh Poimboeuf if (sec) { 141627fce14SJosh Poimboeuf WARN("file already has .orc_unwind section, skipping"); 142627fce14SJosh Poimboeuf return -1; 143627fce14SJosh Poimboeuf } 144627fce14SJosh Poimboeuf 145627fce14SJosh Poimboeuf /* count the number of needed orcs */ 146627fce14SJosh Poimboeuf idx = 0; 147627fce14SJosh Poimboeuf for_each_sec(file, sec) { 148627fce14SJosh Poimboeuf if (!sec->text) 149627fce14SJosh Poimboeuf continue; 150627fce14SJosh Poimboeuf 151627fce14SJosh Poimboeuf prev_insn = NULL; 152627fce14SJosh Poimboeuf sec_for_each_insn(file, sec, insn) { 153627fce14SJosh Poimboeuf if (!prev_insn || 154627fce14SJosh Poimboeuf memcmp(&insn->orc, &prev_insn->orc, 155627fce14SJosh Poimboeuf sizeof(struct orc_entry))) { 156627fce14SJosh Poimboeuf idx++; 157627fce14SJosh Poimboeuf } 158627fce14SJosh Poimboeuf prev_insn = insn; 159627fce14SJosh Poimboeuf } 160627fce14SJosh Poimboeuf 161627fce14SJosh Poimboeuf /* section terminator */ 162627fce14SJosh Poimboeuf if (prev_insn) 163627fce14SJosh Poimboeuf idx++; 164627fce14SJosh Poimboeuf } 165627fce14SJosh Poimboeuf if (!idx) 166627fce14SJosh Poimboeuf return -1; 167627fce14SJosh Poimboeuf 168627fce14SJosh Poimboeuf 169627fce14SJosh Poimboeuf /* create .orc_unwind_ip and .rela.orc_unwind_ip sections */ 1701e7e4788SJosh Poimboeuf sec = elf_create_section(file->elf, ".orc_unwind_ip", 0, sizeof(int), idx); 171ce90aaf5SSimon Ser if (!sec) 172ce90aaf5SSimon Ser return -1; 173627fce14SJosh Poimboeuf 174fb414783SMatt Helsley ip_relocsec = elf_create_reloc_section(file->elf, sec, SHT_RELA); 175f1974222SMatt Helsley if (!ip_relocsec) 176627fce14SJosh Poimboeuf return -1; 177627fce14SJosh Poimboeuf 178627fce14SJosh Poimboeuf /* create .orc_unwind section */ 1791e7e4788SJosh Poimboeuf u_sec = elf_create_section(file->elf, ".orc_unwind", 0, 180627fce14SJosh Poimboeuf sizeof(struct orc_entry), idx); 181627fce14SJosh Poimboeuf 182627fce14SJosh Poimboeuf /* populate sections */ 183627fce14SJosh Poimboeuf idx = 0; 184627fce14SJosh Poimboeuf for_each_sec(file, sec) { 185627fce14SJosh Poimboeuf if (!sec->text) 186627fce14SJosh Poimboeuf continue; 187627fce14SJosh Poimboeuf 188627fce14SJosh Poimboeuf prev_insn = NULL; 189627fce14SJosh Poimboeuf sec_for_each_insn(file, sec, insn) { 190627fce14SJosh Poimboeuf if (!prev_insn || memcmp(&insn->orc, &prev_insn->orc, 191627fce14SJosh Poimboeuf sizeof(struct orc_entry))) { 192627fce14SJosh Poimboeuf 193f1974222SMatt Helsley if (create_orc_entry(file->elf, u_sec, ip_relocsec, idx, 194627fce14SJosh Poimboeuf insn->sec, insn->offset, 195627fce14SJosh Poimboeuf &insn->orc)) 196627fce14SJosh Poimboeuf return -1; 197627fce14SJosh Poimboeuf 198627fce14SJosh Poimboeuf idx++; 199627fce14SJosh Poimboeuf } 200627fce14SJosh Poimboeuf prev_insn = insn; 201627fce14SJosh Poimboeuf } 202627fce14SJosh Poimboeuf 203627fce14SJosh Poimboeuf /* section terminator */ 204627fce14SJosh Poimboeuf if (prev_insn) { 205f1974222SMatt Helsley if (create_orc_entry(file->elf, u_sec, ip_relocsec, idx, 206627fce14SJosh Poimboeuf prev_insn->sec, 207627fce14SJosh Poimboeuf prev_insn->offset + prev_insn->len, 208627fce14SJosh Poimboeuf &empty)) 209627fce14SJosh Poimboeuf return -1; 210627fce14SJosh Poimboeuf 211627fce14SJosh Poimboeuf idx++; 212627fce14SJosh Poimboeuf } 213627fce14SJosh Poimboeuf } 214627fce14SJosh Poimboeuf 215d832c005SPeter Zijlstra if (elf_rebuild_reloc_section(file->elf, ip_relocsec)) 216627fce14SJosh Poimboeuf return -1; 217627fce14SJosh Poimboeuf 218627fce14SJosh Poimboeuf return 0; 219627fce14SJosh Poimboeuf } 220