1 /* 2 * Copyright (C) 2017 Josh Poimboeuf <jpoimboe@redhat.com> 3 * 4 * This program is free software; you can redistribute it and/or 5 * modify it under the terms of the GNU General Public License 6 * as published by the Free Software Foundation; either version 2 7 * of the License, or (at your option) any later version. 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with this program; if not, see <http://www.gnu.org/licenses/>. 16 */ 17 18 #include <unistd.h> 19 #include "orc.h" 20 #include "warn.h" 21 22 static const char *reg_name(unsigned int reg) 23 { 24 switch (reg) { 25 case ORC_REG_PREV_SP: 26 return "prevsp"; 27 case ORC_REG_DX: 28 return "dx"; 29 case ORC_REG_DI: 30 return "di"; 31 case ORC_REG_BP: 32 return "bp"; 33 case ORC_REG_SP: 34 return "sp"; 35 case ORC_REG_R10: 36 return "r10"; 37 case ORC_REG_R13: 38 return "r13"; 39 case ORC_REG_BP_INDIRECT: 40 return "bp(ind)"; 41 case ORC_REG_SP_INDIRECT: 42 return "sp(ind)"; 43 default: 44 return "?"; 45 } 46 } 47 48 static const char *orc_type_name(unsigned int type) 49 { 50 switch (type) { 51 case ORC_TYPE_CALL: 52 return "call"; 53 case ORC_TYPE_REGS: 54 return "regs"; 55 case ORC_TYPE_REGS_IRET: 56 return "iret"; 57 default: 58 return "?"; 59 } 60 } 61 62 static void print_reg(unsigned int reg, int offset) 63 { 64 if (reg == ORC_REG_BP_INDIRECT) 65 printf("(bp%+d)", offset); 66 else if (reg == ORC_REG_SP_INDIRECT) 67 printf("(sp%+d)", offset); 68 else if (reg == ORC_REG_UNDEFINED) 69 printf("(und)"); 70 else 71 printf("%s%+d", reg_name(reg), offset); 72 } 73 74 int orc_dump(const char *_objname) 75 { 76 int fd, nr_entries, i, *orc_ip = NULL, orc_size = 0; 77 struct orc_entry *orc = NULL; 78 char *name; 79 size_t nr_sections; 80 Elf64_Addr orc_ip_addr = 0; 81 size_t shstrtab_idx; 82 Elf *elf; 83 Elf_Scn *scn; 84 GElf_Shdr sh; 85 GElf_Rela rela; 86 GElf_Sym sym; 87 Elf_Data *data, *symtab = NULL, *rela_orc_ip = NULL; 88 89 90 objname = _objname; 91 92 elf_version(EV_CURRENT); 93 94 fd = open(objname, O_RDONLY); 95 if (fd == -1) { 96 perror("open"); 97 return -1; 98 } 99 100 elf = elf_begin(fd, ELF_C_READ_MMAP, NULL); 101 if (!elf) { 102 WARN_ELF("elf_begin"); 103 return -1; 104 } 105 106 if (elf_getshdrnum(elf, &nr_sections)) { 107 WARN_ELF("elf_getshdrnum"); 108 return -1; 109 } 110 111 if (elf_getshdrstrndx(elf, &shstrtab_idx)) { 112 WARN_ELF("elf_getshdrstrndx"); 113 return -1; 114 } 115 116 for (i = 0; i < nr_sections; i++) { 117 scn = elf_getscn(elf, i); 118 if (!scn) { 119 WARN_ELF("elf_getscn"); 120 return -1; 121 } 122 123 if (!gelf_getshdr(scn, &sh)) { 124 WARN_ELF("gelf_getshdr"); 125 return -1; 126 } 127 128 name = elf_strptr(elf, shstrtab_idx, sh.sh_name); 129 if (!name) { 130 WARN_ELF("elf_strptr"); 131 return -1; 132 } 133 134 data = elf_getdata(scn, NULL); 135 if (!data) { 136 WARN_ELF("elf_getdata"); 137 return -1; 138 } 139 140 if (!strcmp(name, ".symtab")) { 141 symtab = data; 142 } else if (!strcmp(name, ".orc_unwind")) { 143 orc = data->d_buf; 144 orc_size = sh.sh_size; 145 } else if (!strcmp(name, ".orc_unwind_ip")) { 146 orc_ip = data->d_buf; 147 orc_ip_addr = sh.sh_addr; 148 } else if (!strcmp(name, ".rela.orc_unwind_ip")) { 149 rela_orc_ip = data; 150 } 151 } 152 153 if (!symtab || !orc || !orc_ip) 154 return 0; 155 156 if (orc_size % sizeof(*orc) != 0) { 157 WARN("bad .orc_unwind section size"); 158 return -1; 159 } 160 161 nr_entries = orc_size / sizeof(*orc); 162 for (i = 0; i < nr_entries; i++) { 163 if (rela_orc_ip) { 164 if (!gelf_getrela(rela_orc_ip, i, &rela)) { 165 WARN_ELF("gelf_getrela"); 166 return -1; 167 } 168 169 if (!gelf_getsym(symtab, GELF_R_SYM(rela.r_info), &sym)) { 170 WARN_ELF("gelf_getsym"); 171 return -1; 172 } 173 174 scn = elf_getscn(elf, sym.st_shndx); 175 if (!scn) { 176 WARN_ELF("elf_getscn"); 177 return -1; 178 } 179 180 if (!gelf_getshdr(scn, &sh)) { 181 WARN_ELF("gelf_getshdr"); 182 return -1; 183 } 184 185 name = elf_strptr(elf, shstrtab_idx, sh.sh_name); 186 if (!name || !*name) { 187 WARN_ELF("elf_strptr"); 188 return -1; 189 } 190 191 printf("%s+%llx:", name, (unsigned long long)rela.r_addend); 192 193 } else { 194 printf("%llx:", (unsigned long long)(orc_ip_addr + (i * sizeof(int)) + orc_ip[i])); 195 } 196 197 198 printf(" sp:"); 199 200 print_reg(orc[i].sp_reg, orc[i].sp_offset); 201 202 printf(" bp:"); 203 204 print_reg(orc[i].bp_reg, orc[i].bp_offset); 205 206 printf(" type:%s\n", orc_type_name(orc[i].type)); 207 } 208 209 elf_end(elf); 210 close(fd); 211 212 return 0; 213 } 214