1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * Copyright (C) 2017 Josh Poimboeuf <jpoimboe@redhat.com> 4 */ 5 6 #include <unistd.h> 7 #include <asm/orc_types.h> 8 #include <objtool/objtool.h> 9 #include <objtool/orc.h> 10 #include <objtool/warn.h> 11 #include <objtool/endianness.h> 12 13 int orc_dump(const char *_objname) 14 { 15 int fd, nr_entries, i, *orc_ip = NULL, orc_size = 0; 16 struct orc_entry *orc = NULL; 17 char *name; 18 size_t nr_sections; 19 Elf64_Addr orc_ip_addr = 0; 20 size_t shstrtab_idx, strtab_idx = 0; 21 Elf *elf; 22 Elf_Scn *scn; 23 GElf_Shdr sh; 24 GElf_Rela rela; 25 GElf_Sym sym; 26 Elf_Data *data, *symtab = NULL, *rela_orc_ip = NULL; 27 struct elf dummy_elf = {}; 28 29 30 objname = _objname; 31 32 elf_version(EV_CURRENT); 33 34 fd = open(objname, O_RDONLY); 35 if (fd == -1) { 36 perror("open"); 37 return -1; 38 } 39 40 elf = elf_begin(fd, ELF_C_READ_MMAP, NULL); 41 if (!elf) { 42 WARN_ELF("elf_begin"); 43 return -1; 44 } 45 46 if (!elf64_getehdr(elf)) { 47 WARN_ELF("elf64_getehdr"); 48 return -1; 49 } 50 memcpy(&dummy_elf.ehdr, elf64_getehdr(elf), sizeof(dummy_elf.ehdr)); 51 52 if (elf_getshdrnum(elf, &nr_sections)) { 53 WARN_ELF("elf_getshdrnum"); 54 return -1; 55 } 56 57 if (elf_getshdrstrndx(elf, &shstrtab_idx)) { 58 WARN_ELF("elf_getshdrstrndx"); 59 return -1; 60 } 61 62 for (i = 0; i < nr_sections; i++) { 63 scn = elf_getscn(elf, i); 64 if (!scn) { 65 WARN_ELF("elf_getscn"); 66 return -1; 67 } 68 69 if (!gelf_getshdr(scn, &sh)) { 70 WARN_ELF("gelf_getshdr"); 71 return -1; 72 } 73 74 name = elf_strptr(elf, shstrtab_idx, sh.sh_name); 75 if (!name) { 76 WARN_ELF("elf_strptr"); 77 return -1; 78 } 79 80 data = elf_getdata(scn, NULL); 81 if (!data) { 82 WARN_ELF("elf_getdata"); 83 return -1; 84 } 85 86 if (!strcmp(name, ".symtab")) { 87 symtab = data; 88 } else if (!strcmp(name, ".strtab")) { 89 strtab_idx = i; 90 } else if (!strcmp(name, ".orc_unwind")) { 91 orc = data->d_buf; 92 orc_size = sh.sh_size; 93 } else if (!strcmp(name, ".orc_unwind_ip")) { 94 orc_ip = data->d_buf; 95 orc_ip_addr = sh.sh_addr; 96 } else if (!strcmp(name, ".rela.orc_unwind_ip")) { 97 rela_orc_ip = data; 98 } 99 } 100 101 if (!symtab || !strtab_idx || !orc || !orc_ip) 102 return 0; 103 104 if (orc_size % sizeof(*orc) != 0) { 105 WARN("bad .orc_unwind section size"); 106 return -1; 107 } 108 109 nr_entries = orc_size / sizeof(*orc); 110 for (i = 0; i < nr_entries; i++) { 111 if (rela_orc_ip) { 112 if (!gelf_getrela(rela_orc_ip, i, &rela)) { 113 WARN_ELF("gelf_getrela"); 114 return -1; 115 } 116 117 if (!gelf_getsym(symtab, GELF_R_SYM(rela.r_info), &sym)) { 118 WARN_ELF("gelf_getsym"); 119 return -1; 120 } 121 122 if (GELF_ST_TYPE(sym.st_info) == STT_SECTION) { 123 scn = elf_getscn(elf, sym.st_shndx); 124 if (!scn) { 125 WARN_ELF("elf_getscn"); 126 return -1; 127 } 128 129 if (!gelf_getshdr(scn, &sh)) { 130 WARN_ELF("gelf_getshdr"); 131 return -1; 132 } 133 134 name = elf_strptr(elf, shstrtab_idx, sh.sh_name); 135 if (!name) { 136 WARN_ELF("elf_strptr"); 137 return -1; 138 } 139 } else { 140 name = elf_strptr(elf, strtab_idx, sym.st_name); 141 if (!name) { 142 WARN_ELF("elf_strptr"); 143 return -1; 144 } 145 } 146 147 printf("%s+%llx:", name, (unsigned long long)rela.r_addend); 148 149 } else { 150 printf("%llx:", (unsigned long long)(orc_ip_addr + (i * sizeof(int)) + orc_ip[i])); 151 } 152 153 orc_print_dump(&dummy_elf, orc, i); 154 } 155 156 elf_end(elf); 157 close(fd); 158 159 return 0; 160 } 161