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 unsigned long nr_sections, orc_ip_addr = 0; 80 size_t shstrtab_idx; 81 Elf *elf; 82 Elf_Scn *scn; 83 GElf_Shdr sh; 84 GElf_Rela rela; 85 GElf_Sym sym; 86 Elf_Data *data, *symtab = NULL, *rela_orc_ip = NULL; 87 88 89 objname = _objname; 90 91 elf_version(EV_CURRENT); 92 93 fd = open(objname, O_RDONLY); 94 if (fd == -1) { 95 perror("open"); 96 return -1; 97 } 98 99 elf = elf_begin(fd, ELF_C_READ_MMAP, NULL); 100 if (!elf) { 101 WARN_ELF("elf_begin"); 102 return -1; 103 } 104 105 if (elf_getshdrnum(elf, &nr_sections)) { 106 WARN_ELF("elf_getshdrnum"); 107 return -1; 108 } 109 110 if (elf_getshdrstrndx(elf, &shstrtab_idx)) { 111 WARN_ELF("elf_getshdrstrndx"); 112 return -1; 113 } 114 115 for (i = 0; i < nr_sections; i++) { 116 scn = elf_getscn(elf, i); 117 if (!scn) { 118 WARN_ELF("elf_getscn"); 119 return -1; 120 } 121 122 if (!gelf_getshdr(scn, &sh)) { 123 WARN_ELF("gelf_getshdr"); 124 return -1; 125 } 126 127 name = elf_strptr(elf, shstrtab_idx, sh.sh_name); 128 if (!name) { 129 WARN_ELF("elf_strptr"); 130 return -1; 131 } 132 133 data = elf_getdata(scn, NULL); 134 if (!data) { 135 WARN_ELF("elf_getdata"); 136 return -1; 137 } 138 139 if (!strcmp(name, ".symtab")) { 140 symtab = data; 141 } else if (!strcmp(name, ".orc_unwind")) { 142 orc = data->d_buf; 143 orc_size = sh.sh_size; 144 } else if (!strcmp(name, ".orc_unwind_ip")) { 145 orc_ip = data->d_buf; 146 orc_ip_addr = sh.sh_addr; 147 } else if (!strcmp(name, ".rela.orc_unwind_ip")) { 148 rela_orc_ip = data; 149 } 150 } 151 152 if (!symtab || !orc || !orc_ip) 153 return 0; 154 155 if (orc_size % sizeof(*orc) != 0) { 156 WARN("bad .orc_unwind section size"); 157 return -1; 158 } 159 160 nr_entries = orc_size / sizeof(*orc); 161 for (i = 0; i < nr_entries; i++) { 162 if (rela_orc_ip) { 163 if (!gelf_getrela(rela_orc_ip, i, &rela)) { 164 WARN_ELF("gelf_getrela"); 165 return -1; 166 } 167 168 if (!gelf_getsym(symtab, GELF_R_SYM(rela.r_info), &sym)) { 169 WARN_ELF("gelf_getsym"); 170 return -1; 171 } 172 173 scn = elf_getscn(elf, sym.st_shndx); 174 if (!scn) { 175 WARN_ELF("elf_getscn"); 176 return -1; 177 } 178 179 if (!gelf_getshdr(scn, &sh)) { 180 WARN_ELF("gelf_getshdr"); 181 return -1; 182 } 183 184 name = elf_strptr(elf, shstrtab_idx, sh.sh_name); 185 if (!name || !*name) { 186 WARN_ELF("elf_strptr"); 187 return -1; 188 } 189 190 printf("%s+%lx:", name, rela.r_addend); 191 192 } else { 193 printf("%lx:", orc_ip_addr + (i * sizeof(int)) + orc_ip[i]); 194 } 195 196 197 printf(" sp:"); 198 199 print_reg(orc[i].sp_reg, orc[i].sp_offset); 200 201 printf(" bp:"); 202 203 print_reg(orc[i].bp_reg, orc[i].bp_offset); 204 205 printf(" type:%s\n", orc_type_name(orc[i].type)); 206 } 207 208 elf_end(elf); 209 close(fd); 210 211 return 0; 212 } 213