inlinecall.c (11224B)
1 #include <sys/param.h> 2 #include <sys/queue.h> 3 4 #include <dwarf.h> 5 #include <err.h> 6 #include <fcntl.h> 7 #include <gelf.h> 8 #include <libdwarf.h> 9 #include <libelf.h> 10 #include <stdio.h> 11 #include <stdlib.h> 12 #include <string.h> 13 #include <unistd.h> 14 15 struct elf_info { 16 Elf *elf; 17 struct section { 18 Elf_Scn *scn; 19 GElf_Shdr sh; 20 } *sl; 21 size_t shnum; 22 }; 23 24 struct entry { 25 int naddrs; 26 struct addr_pair { 27 uint64_t lo; 28 uint64_t hi; 29 } *addr; 30 uint64_t line; 31 const char *file; 32 enum { 33 F_SUBPROGRAM, 34 F_INLINE_COPY, 35 } flag; 36 TAILQ_ENTRY(entry) next; 37 }; 38 39 static void *emalloc(size_t); 40 static void parse_die(Dwarf_Debug, Dwarf_Die, int, int); 41 static const char *find_caller_func(struct addr_pair); 42 static void dump_results(void); 43 44 static char **srcfiles; 45 static char *func; 46 static Dwarf_Off g_dieoff; 47 static struct elf_info ei; 48 static TAILQ_HEAD(, entry) head = TAILQ_HEAD_INITIALIZER(head); 49 50 static void * 51 emalloc(size_t nb) 52 { 53 void *p; 54 55 if ((p = malloc(nb)) == NULL) 56 err(1, "malloc"); 57 58 return (p); 59 } 60 61 static void 62 parse_die(Dwarf_Debug dbg, Dwarf_Die die, int level, int flag) 63 { 64 static Dwarf_Die die_root; 65 Dwarf_Die die_next; 66 Dwarf_Ranges *ranges, *rp; 67 Dwarf_Attribute attp; 68 Dwarf_Addr base0, lowpc, highpc; 69 Dwarf_Off dieoff, cuoff, culen, v_off; 70 Dwarf_Unsigned line, nbytes, v_udata; 71 Dwarf_Signed nranges; 72 Dwarf_Half attr, tag; 73 Dwarf_Bool v_flag; 74 Dwarf_Error error; 75 struct entry *e; 76 struct addr_pair *addr; 77 const char *str; 78 char *v_str; 79 char *file = NULL; 80 int naddrs; 81 int res, i, found = 0; 82 83 if (level == 0) 84 die_root = die; 85 86 if (dwarf_dieoffset(die, &dieoff, &error) != DW_DLV_OK) { 87 warnx("%s", dwarf_errmsg(error)); 88 goto cont; 89 } 90 if (dwarf_die_CU_offset_range(die, &cuoff, &culen, &error) != DW_DLV_OK) { 91 warnx("%s", dwarf_errmsg(error)); 92 cuoff = 0; 93 } 94 if (dwarf_tag(die, &tag, &error) != DW_DLV_OK) { 95 warnx("%s", dwarf_errmsg(error)); 96 goto cont; 97 } 98 if (tag != DW_TAG_subprogram && tag != DW_TAG_inlined_subroutine) 99 goto cont; 100 if (flag == F_SUBPROGRAM && tag == DW_TAG_subprogram) { 101 if (dwarf_hasattr(die, DW_AT_inline, &v_flag, &error) != 102 DW_DLV_OK) { 103 warnx("%s", dwarf_errmsg(error)); 104 goto cont; 105 } 106 if (!v_flag) 107 goto cont; 108 res = dwarf_diename(die, &v_str, &error); 109 if (res != DW_DLV_OK) { 110 warnx("%s", dwarf_errmsg(error)); 111 goto cont; 112 } 113 if (strcmp(v_str, func) != 0) 114 goto cont; 115 /* 116 * The function name we're searching for has an inline 117 * definition. 118 */ 119 found = 1; 120 } else if (flag == F_INLINE_COPY && tag == DW_TAG_inlined_subroutine) { 121 res = dwarf_attr(die, DW_AT_abstract_origin, &attp, &error); 122 if (res != DW_DLV_OK) { 123 if (res == DW_DLV_ERROR) 124 warnx("%s", dwarf_errmsg(error)); 125 goto cont; 126 } 127 if (dwarf_formref(attp, &v_off, &error) != DW_DLV_OK) { 128 warnx("%s", dwarf_errmsg(error)); 129 goto cont; 130 } 131 v_off += cuoff; 132 /* Doesn't point to the definition's DIE offset. */ 133 if (v_off != g_dieoff) 134 goto cont; 135 136 if (dwarf_hasattr(die, DW_AT_ranges, &v_flag, &error) != 137 DW_DLV_OK) { 138 warnx("%s", dwarf_errmsg(error)); 139 goto cont; 140 } 141 if (v_flag) { 142 /* DIE has ranges */ 143 res = dwarf_attr(die, DW_AT_ranges, &attp, &error); 144 if (res != DW_DLV_OK) { 145 if (res == DW_DLV_ERROR) 146 warnx("%s", dwarf_errmsg(error)); 147 goto cont; 148 } 149 if (dwarf_global_formref(attp, &v_off, &error) != 150 DW_DLV_OK) { 151 warnx("%s", dwarf_errmsg(error)); 152 goto cont; 153 } 154 if (dwarf_get_ranges(dbg, v_off, &ranges, &nranges, 155 &nbytes, &error) != DW_DLV_OK) { 156 warnx("%s", dwarf_errmsg(error)); 157 goto cont; 158 } 159 160 res = dwarf_lowpc(die_root, &lowpc, &error); 161 if (res != DW_DLV_OK) { 162 warnx("%s", dwarf_errmsg(error)); 163 goto cont; 164 } 165 base0 = lowpc; 166 167 naddrs = nranges - 1; 168 addr = emalloc(naddrs * sizeof(struct addr_pair)); 169 for (i = 0; i < naddrs; i++) { 170 rp = &ranges[i]; 171 if (rp->dwr_type == DW_RANGES_ADDRESS_SELECTION) 172 base0 = rp->dwr_addr2; 173 addr[i].lo = rp->dwr_addr1 + base0; 174 addr[i].hi = rp->dwr_addr2 + base0; 175 } 176 dwarf_ranges_dealloc(dbg, ranges, nranges); 177 } else { 178 /* DIE has high/low PC boundaries */ 179 res = dwarf_lowpc(die, &lowpc, &error); 180 if (res != DW_DLV_OK) { 181 warnx("%s", dwarf_errmsg(error)); 182 goto cont; 183 } 184 res = dwarf_highpc(die, &highpc, &error); 185 if (res != DW_DLV_OK) { 186 warnx("%s", dwarf_errmsg(error)); 187 goto cont; 188 } 189 naddrs = 1; 190 addr = emalloc(sizeof(struct addr_pair)); 191 addr[0].lo = lowpc; 192 addr[0].hi = lowpc + highpc; 193 } 194 } else 195 goto cont; 196 197 /* Get file:line */ 198 attr = (flag == F_SUBPROGRAM) ? DW_AT_decl_file : DW_AT_call_file; 199 res = dwarf_attr(die, attr, &attp, &error); 200 if (res != DW_DLV_OK) { 201 if (res == DW_DLV_ERROR) 202 warnx("%s", dwarf_errmsg(error)); 203 goto skip; 204 } 205 if (dwarf_formudata(attp, &v_udata, &error) != DW_DLV_OK) { 206 warnx("%s", dwarf_errmsg(error)); 207 goto cont; 208 } 209 file = srcfiles[v_udata - 1]; 210 211 attr = (flag == F_SUBPROGRAM) ? DW_AT_decl_line: DW_AT_call_line; 212 res = dwarf_attr(die, attr, &attp, &error); 213 if (res != DW_DLV_OK) { 214 if (res == DW_DLV_ERROR) 215 warnx("%s", dwarf_errmsg(error)); 216 goto skip; 217 } 218 if (dwarf_formudata(attp, &line, &error) != DW_DLV_OK) { 219 warnx("%s", dwarf_errmsg(error)); 220 goto cont; 221 } 222 skip: 223 e = emalloc(sizeof(struct entry)); 224 e->flag = flag; 225 if (file != NULL) { 226 e->file = file; 227 e->line = line; 228 } else 229 e->file = NULL; 230 if (e->flag == F_INLINE_COPY) { 231 e->naddrs = naddrs; 232 e->addr = addr; 233 } 234 TAILQ_INSERT_TAIL(&head, e, next); 235 cont: 236 /* 237 * Inline copies might appear before the declaration, so we need to 238 * re-parse the CU. 239 * 240 * The rationale for choosing to re-parse the CU instead of using a 241 * hash table of DIEs is that, because we re-parse only when an inline 242 * definition of the function we want is found, statistically, we won't 243 * have to re-parse many times at all considering that only a handful 244 * of CUs will define the same function, whereas if we have used a hash 245 * table, we would first need to parse the whole CU at once and store 246 * all DW_TAG_inlined_subroutine DIEs (so that we can match them 247 * afterwards). In this case, we always have to "parse" twice -- first 248 * the CU, then the DIE table -- and also, the program would use much 249 * more memory since we would have allocated DIEs, which most of them 250 * would never be used. 251 */ 252 if (found) { 253 die = die_root; 254 level = 0; 255 /* 256 * We'll be checking against the DIE offset of the definition 257 * to determine if the inline copy's DW_AT_abstract_origin 258 * points to it. 259 */ 260 g_dieoff = dieoff; 261 flag = F_INLINE_COPY; 262 } 263 264 res = dwarf_child(die, &die_next, &error); 265 if (res == DW_DLV_ERROR) 266 warnx("%s", dwarf_errmsg(error)); 267 else if (res == DW_DLV_OK) 268 parse_die(dbg, die_next, level + 1, flag); 269 270 res = dwarf_siblingof(dbg, die, &die_next, &error); 271 if (res == DW_DLV_ERROR) 272 warnx("%s", dwarf_errmsg(error)); 273 else if (res == DW_DLV_OK) 274 parse_die(dbg, die_next, level, flag); 275 276 /* 277 * Deallocating on level 0 will attempt to double-free, since die_root 278 * points to the first DIE. We'll deallocate the root DIE in main(). 279 */ 280 if (level > 0) 281 dwarf_dealloc(dbg, die, DW_DLA_DIE); 282 } 283 284 static const char * 285 find_caller_func(struct addr_pair addr) 286 { 287 Elf_Data *d; 288 GElf_Sym sym; 289 struct section *s; 290 const char *name; 291 uint64_t lo, hi; 292 uint32_t stab; 293 int len, i, j; 294 295 for (i = 0; i < ei.shnum; i++) { 296 s = &ei.sl[i]; 297 if (s->sh.sh_type != SHT_SYMTAB && s->sh.sh_type != SHT_DYNSYM) 298 continue; 299 if (s->sh.sh_link >= ei.shnum) 300 continue; 301 stab = s->sh.sh_link; 302 len = (int)(s->sh.sh_size / s->sh.sh_entsize); 303 (void)elf_errno(); 304 if ((d = elf_getdata(s->scn, NULL)) == NULL) { 305 if (elf_errno() != 0) 306 warnx("elf_getdata(): %s", elf_errmsg(-1)); 307 continue; 308 } 309 if (d->d_size <= 0) 310 continue; 311 if (s->sh.sh_entsize == 0) 312 continue; 313 else if (len > INT_MAX) 314 continue; 315 for (j = 0; j < len; j++) { 316 if (gelf_getsym(d, j, &sym) != &sym) { 317 warnx("gelf_getsym(): %s", elf_errmsg(-1)); 318 continue; 319 } 320 if (GELF_ST_TYPE(sym.st_info) != STT_FUNC) 321 continue; 322 lo = sym.st_value; 323 hi = sym.st_value + sym.st_size; 324 if (addr.lo < lo || addr.hi > hi) 325 continue; 326 if ((name = elf_strptr(ei.elf, stab, sym.st_name)) != NULL) 327 return (name); 328 } 329 } 330 331 return (NULL); 332 } 333 334 static void 335 dump_results(void) 336 { 337 struct entry *e; 338 const char *str; 339 int i; 340 341 /* Clean up as well */ 342 while (!TAILQ_EMPTY(&head)) { 343 e = TAILQ_FIRST(&head); 344 TAILQ_REMOVE(&head, e, next); 345 if (e->flag == F_INLINE_COPY) { 346 for (i = 0; i < e->naddrs; i++) { 347 printf("\t[0x%jx - 0x%jx]", e->addr[i].lo, 348 e->addr[i].hi); 349 if (e->file != NULL) 350 printf("\t%s:%lu", e->file, e->line); 351 str = find_caller_func(e->addr[i]); 352 if (str != NULL) 353 printf("\t%s()\n", str); 354 else 355 putchar('\n'); 356 } 357 free(e->addr); 358 } else if (e->flag == F_SUBPROGRAM) { 359 printf("%s:%lu\n", e->file, e->line); 360 } 361 free(e); 362 } 363 } 364 365 int 366 main(int argc, char *argv[]) 367 { 368 Elf_Scn *scn; 369 GElf_Shdr sh; 370 Dwarf_Debug dbg; 371 Dwarf_Die die; 372 Dwarf_Signed nfiles; 373 Dwarf_Error error; 374 struct section *s; 375 char *file; 376 size_t shstrndx, ndx; 377 int fd, res = DW_DLV_OK; 378 379 if (argc < 3) { 380 fprintf(stderr, "usage: %s function debug_file\n", *argv); 381 return (1); 382 } 383 func = argv[1]; 384 file = argv[2]; 385 386 if (elf_version(EV_CURRENT) == EV_NONE) 387 errx(1, "elf_version(): %s", elf_errmsg(-1)); 388 if ((fd = open(file, O_RDONLY)) < 0) 389 err(1, "open(%s)", file); 390 if ((ei.elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL) 391 errx(1, "elf_begin(): %s", elf_errmsg(-1)); 392 if (elf_kind(ei.elf) == ELF_K_NONE) 393 errx(1, "not an ELF file: %s", file); 394 if (dwarf_elf_init(ei.elf, DW_DLC_READ, NULL, NULL, &dbg, &error) != 395 DW_DLV_OK) 396 errx(1, "dwarf_elf_init(): %s", dwarf_errmsg(error)); 397 398 /* Load ELF sections */ 399 if (!elf_getshnum(ei.elf, &ei.shnum)) 400 errx(1, "elf_getshnum(): %s", elf_errmsg(-1)); 401 if ((ei.sl = calloc(ei.shnum, sizeof(struct section))) == NULL) 402 err(1, "calloc"); 403 if (!elf_getshstrndx(ei.elf, &shstrndx)) 404 errx(1, "elf_getshstrndx(): %s", elf_errmsg(-1)); 405 if ((scn = elf_getscn(ei.elf, 0)) == NULL) 406 err(1, "elf_getscn(): %s", elf_errmsg(-1)); 407 (void)elf_errno(); 408 409 do { 410 if (gelf_getshdr(scn, &sh) == NULL) { 411 warnx("gelf_getshdr(): %s", elf_errmsg(-1)); 412 (void)elf_errno(); 413 continue; 414 } 415 if ((ndx = elf_ndxscn(scn)) == SHN_UNDEF && elf_errno() != 0) { 416 warnx("elf_ndxscn(): %s", elf_errmsg(-1)); 417 continue; 418 } 419 if (ndx >= ei.shnum) 420 continue; 421 s = &ei.sl[ndx]; 422 s->scn = scn; 423 s->sh = sh; 424 } while ((scn = elf_nextscn(ei.elf, scn)) != NULL); 425 if (elf_errno() != 0) 426 warnx("elf_nextscn(): %s", elf_errmsg(-1)); 427 428 while ((res = dwarf_next_cu_header(dbg, NULL, NULL, NULL, NULL, NULL, 429 &error)) == DW_DLV_OK) { 430 die = NULL; 431 TAILQ_INIT(&head); 432 while (dwarf_siblingof(dbg, die, &die, &error) == DW_DLV_OK) { 433 srcfiles = NULL; 434 if (dwarf_srcfiles(die, &srcfiles, &nfiles, &error) != 435 DW_DLV_OK) 436 warnx("%s", dwarf_errmsg(error)); 437 parse_die(dbg, die, 0, F_SUBPROGRAM); 438 } 439 dwarf_dealloc(dbg, die, DW_DLA_DIE); 440 dump_results(); 441 } 442 if (res == DW_DLV_ERROR) 443 warnx("%s", dwarf_errmsg(error)); 444 445 free(ei.sl); 446 elf_end(ei.elf); 447 dwarf_finish(dbg, &error); 448 close(fd); 449 450 return (0); 451 }