162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2017 Josh Poimboeuf <jpoimboe@redhat.com> 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <unistd.h> 762306a36Sopenharmony_ci#include <asm/orc_types.h> 862306a36Sopenharmony_ci#include <objtool/objtool.h> 962306a36Sopenharmony_ci#include <objtool/warn.h> 1062306a36Sopenharmony_ci#include <objtool/endianness.h> 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_cistatic const char *reg_name(unsigned int reg) 1362306a36Sopenharmony_ci{ 1462306a36Sopenharmony_ci switch (reg) { 1562306a36Sopenharmony_ci case ORC_REG_PREV_SP: 1662306a36Sopenharmony_ci return "prevsp"; 1762306a36Sopenharmony_ci case ORC_REG_DX: 1862306a36Sopenharmony_ci return "dx"; 1962306a36Sopenharmony_ci case ORC_REG_DI: 2062306a36Sopenharmony_ci return "di"; 2162306a36Sopenharmony_ci case ORC_REG_BP: 2262306a36Sopenharmony_ci return "bp"; 2362306a36Sopenharmony_ci case ORC_REG_SP: 2462306a36Sopenharmony_ci return "sp"; 2562306a36Sopenharmony_ci case ORC_REG_R10: 2662306a36Sopenharmony_ci return "r10"; 2762306a36Sopenharmony_ci case ORC_REG_R13: 2862306a36Sopenharmony_ci return "r13"; 2962306a36Sopenharmony_ci case ORC_REG_BP_INDIRECT: 3062306a36Sopenharmony_ci return "bp(ind)"; 3162306a36Sopenharmony_ci case ORC_REG_SP_INDIRECT: 3262306a36Sopenharmony_ci return "sp(ind)"; 3362306a36Sopenharmony_ci default: 3462306a36Sopenharmony_ci return "?"; 3562306a36Sopenharmony_ci } 3662306a36Sopenharmony_ci} 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_cistatic const char *orc_type_name(unsigned int type) 3962306a36Sopenharmony_ci{ 4062306a36Sopenharmony_ci switch (type) { 4162306a36Sopenharmony_ci case ORC_TYPE_UNDEFINED: 4262306a36Sopenharmony_ci return "(und)"; 4362306a36Sopenharmony_ci case ORC_TYPE_END_OF_STACK: 4462306a36Sopenharmony_ci return "end"; 4562306a36Sopenharmony_ci case ORC_TYPE_CALL: 4662306a36Sopenharmony_ci return "call"; 4762306a36Sopenharmony_ci case ORC_TYPE_REGS: 4862306a36Sopenharmony_ci return "regs"; 4962306a36Sopenharmony_ci case ORC_TYPE_REGS_PARTIAL: 5062306a36Sopenharmony_ci return "regs (partial)"; 5162306a36Sopenharmony_ci default: 5262306a36Sopenharmony_ci return "?"; 5362306a36Sopenharmony_ci } 5462306a36Sopenharmony_ci} 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_cistatic void print_reg(unsigned int reg, int offset) 5762306a36Sopenharmony_ci{ 5862306a36Sopenharmony_ci if (reg == ORC_REG_BP_INDIRECT) 5962306a36Sopenharmony_ci printf("(bp%+d)", offset); 6062306a36Sopenharmony_ci else if (reg == ORC_REG_SP_INDIRECT) 6162306a36Sopenharmony_ci printf("(sp)%+d", offset); 6262306a36Sopenharmony_ci else if (reg == ORC_REG_UNDEFINED) 6362306a36Sopenharmony_ci printf("(und)"); 6462306a36Sopenharmony_ci else 6562306a36Sopenharmony_ci printf("%s%+d", reg_name(reg), offset); 6662306a36Sopenharmony_ci} 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ciint orc_dump(const char *_objname) 6962306a36Sopenharmony_ci{ 7062306a36Sopenharmony_ci int fd, nr_entries, i, *orc_ip = NULL, orc_size = 0; 7162306a36Sopenharmony_ci struct orc_entry *orc = NULL; 7262306a36Sopenharmony_ci char *name; 7362306a36Sopenharmony_ci size_t nr_sections; 7462306a36Sopenharmony_ci Elf64_Addr orc_ip_addr = 0; 7562306a36Sopenharmony_ci size_t shstrtab_idx, strtab_idx = 0; 7662306a36Sopenharmony_ci Elf *elf; 7762306a36Sopenharmony_ci Elf_Scn *scn; 7862306a36Sopenharmony_ci GElf_Shdr sh; 7962306a36Sopenharmony_ci GElf_Rela rela; 8062306a36Sopenharmony_ci GElf_Sym sym; 8162306a36Sopenharmony_ci Elf_Data *data, *symtab = NULL, *rela_orc_ip = NULL; 8262306a36Sopenharmony_ci struct elf dummy_elf = {}; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci objname = _objname; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci elf_version(EV_CURRENT); 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci fd = open(objname, O_RDONLY); 9062306a36Sopenharmony_ci if (fd == -1) { 9162306a36Sopenharmony_ci perror("open"); 9262306a36Sopenharmony_ci return -1; 9362306a36Sopenharmony_ci } 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci elf = elf_begin(fd, ELF_C_READ_MMAP, NULL); 9662306a36Sopenharmony_ci if (!elf) { 9762306a36Sopenharmony_ci WARN_ELF("elf_begin"); 9862306a36Sopenharmony_ci return -1; 9962306a36Sopenharmony_ci } 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci if (!elf64_getehdr(elf)) { 10262306a36Sopenharmony_ci WARN_ELF("elf64_getehdr"); 10362306a36Sopenharmony_ci return -1; 10462306a36Sopenharmony_ci } 10562306a36Sopenharmony_ci memcpy(&dummy_elf.ehdr, elf64_getehdr(elf), sizeof(dummy_elf.ehdr)); 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci if (elf_getshdrnum(elf, &nr_sections)) { 10862306a36Sopenharmony_ci WARN_ELF("elf_getshdrnum"); 10962306a36Sopenharmony_ci return -1; 11062306a36Sopenharmony_ci } 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci if (elf_getshdrstrndx(elf, &shstrtab_idx)) { 11362306a36Sopenharmony_ci WARN_ELF("elf_getshdrstrndx"); 11462306a36Sopenharmony_ci return -1; 11562306a36Sopenharmony_ci } 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci for (i = 0; i < nr_sections; i++) { 11862306a36Sopenharmony_ci scn = elf_getscn(elf, i); 11962306a36Sopenharmony_ci if (!scn) { 12062306a36Sopenharmony_ci WARN_ELF("elf_getscn"); 12162306a36Sopenharmony_ci return -1; 12262306a36Sopenharmony_ci } 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci if (!gelf_getshdr(scn, &sh)) { 12562306a36Sopenharmony_ci WARN_ELF("gelf_getshdr"); 12662306a36Sopenharmony_ci return -1; 12762306a36Sopenharmony_ci } 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci name = elf_strptr(elf, shstrtab_idx, sh.sh_name); 13062306a36Sopenharmony_ci if (!name) { 13162306a36Sopenharmony_ci WARN_ELF("elf_strptr"); 13262306a36Sopenharmony_ci return -1; 13362306a36Sopenharmony_ci } 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci data = elf_getdata(scn, NULL); 13662306a36Sopenharmony_ci if (!data) { 13762306a36Sopenharmony_ci WARN_ELF("elf_getdata"); 13862306a36Sopenharmony_ci return -1; 13962306a36Sopenharmony_ci } 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci if (!strcmp(name, ".symtab")) { 14262306a36Sopenharmony_ci symtab = data; 14362306a36Sopenharmony_ci } else if (!strcmp(name, ".strtab")) { 14462306a36Sopenharmony_ci strtab_idx = i; 14562306a36Sopenharmony_ci } else if (!strcmp(name, ".orc_unwind")) { 14662306a36Sopenharmony_ci orc = data->d_buf; 14762306a36Sopenharmony_ci orc_size = sh.sh_size; 14862306a36Sopenharmony_ci } else if (!strcmp(name, ".orc_unwind_ip")) { 14962306a36Sopenharmony_ci orc_ip = data->d_buf; 15062306a36Sopenharmony_ci orc_ip_addr = sh.sh_addr; 15162306a36Sopenharmony_ci } else if (!strcmp(name, ".rela.orc_unwind_ip")) { 15262306a36Sopenharmony_ci rela_orc_ip = data; 15362306a36Sopenharmony_ci } 15462306a36Sopenharmony_ci } 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci if (!symtab || !strtab_idx || !orc || !orc_ip) 15762306a36Sopenharmony_ci return 0; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci if (orc_size % sizeof(*orc) != 0) { 16062306a36Sopenharmony_ci WARN("bad .orc_unwind section size"); 16162306a36Sopenharmony_ci return -1; 16262306a36Sopenharmony_ci } 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci nr_entries = orc_size / sizeof(*orc); 16562306a36Sopenharmony_ci for (i = 0; i < nr_entries; i++) { 16662306a36Sopenharmony_ci if (rela_orc_ip) { 16762306a36Sopenharmony_ci if (!gelf_getrela(rela_orc_ip, i, &rela)) { 16862306a36Sopenharmony_ci WARN_ELF("gelf_getrela"); 16962306a36Sopenharmony_ci return -1; 17062306a36Sopenharmony_ci } 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci if (!gelf_getsym(symtab, GELF_R_SYM(rela.r_info), &sym)) { 17362306a36Sopenharmony_ci WARN_ELF("gelf_getsym"); 17462306a36Sopenharmony_ci return -1; 17562306a36Sopenharmony_ci } 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci if (GELF_ST_TYPE(sym.st_info) == STT_SECTION) { 17862306a36Sopenharmony_ci scn = elf_getscn(elf, sym.st_shndx); 17962306a36Sopenharmony_ci if (!scn) { 18062306a36Sopenharmony_ci WARN_ELF("elf_getscn"); 18162306a36Sopenharmony_ci return -1; 18262306a36Sopenharmony_ci } 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci if (!gelf_getshdr(scn, &sh)) { 18562306a36Sopenharmony_ci WARN_ELF("gelf_getshdr"); 18662306a36Sopenharmony_ci return -1; 18762306a36Sopenharmony_ci } 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci name = elf_strptr(elf, shstrtab_idx, sh.sh_name); 19062306a36Sopenharmony_ci if (!name) { 19162306a36Sopenharmony_ci WARN_ELF("elf_strptr"); 19262306a36Sopenharmony_ci return -1; 19362306a36Sopenharmony_ci } 19462306a36Sopenharmony_ci } else { 19562306a36Sopenharmony_ci name = elf_strptr(elf, strtab_idx, sym.st_name); 19662306a36Sopenharmony_ci if (!name) { 19762306a36Sopenharmony_ci WARN_ELF("elf_strptr"); 19862306a36Sopenharmony_ci return -1; 19962306a36Sopenharmony_ci } 20062306a36Sopenharmony_ci } 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci printf("%s+%llx:", name, (unsigned long long)rela.r_addend); 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci } else { 20562306a36Sopenharmony_ci printf("%llx:", (unsigned long long)(orc_ip_addr + (i * sizeof(int)) + orc_ip[i])); 20662306a36Sopenharmony_ci } 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci printf("type:%s", orc_type_name(orc[i].type)); 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci printf(" sp:"); 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci print_reg(orc[i].sp_reg, bswap_if_needed(&dummy_elf, orc[i].sp_offset)); 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci printf(" bp:"); 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci print_reg(orc[i].bp_reg, bswap_if_needed(&dummy_elf, orc[i].bp_offset)); 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci printf(" signal:%d\n", orc[i].signal); 21962306a36Sopenharmony_ci } 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci elf_end(elf); 22262306a36Sopenharmony_ci close(fd); 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci return 0; 22562306a36Sopenharmony_ci} 226