1 /* $OpenBSD: ctfdump.c,v 1.22 2019/03/16 16:35:03 sunil Exp $ */ 2 3 /* 4 * Copyright (c) 2016 Martin Pieuchot <mpi@openbsd.org> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include <sys/types.h> 20 #include <sys/stat.h> 21 #include <sys/mman.h> 22 #include <sys/ctf.h> 23 24 #include <elf.h> 25 #include <err.h> 26 #include <fcntl.h> 27 #include <locale.h> 28 #include <stdio.h> 29 #include <stdint.h> 30 #include <stdlib.h> 31 #include <string.h> 32 #include <unistd.h> 33 34 #ifdef ZLIB 35 #include <zlib.h> 36 #endif /* ZLIB */ 37 38 #ifndef nitems 39 #define nitems(_a) (sizeof((_a)) / sizeof((_a)[0])) 40 #endif 41 42 #define DUMP_OBJECT (1 << 0) 43 #define DUMP_FUNCTION (1 << 1) 44 #define DUMP_HEADER (1 << 2) 45 #define DUMP_LABEL (1 << 3) 46 #define DUMP_STRTAB (1 << 4) 47 #define DUMP_STATISTIC (1 << 5) 48 #define DUMP_TYPE (1 << 6) 49 50 int dump(const char *, uint8_t); 51 int isctf(const char *, size_t); 52 __dead void usage(void); 53 54 int ctf_dump(const char *, size_t, uint8_t); 55 void ctf_dump_type(struct ctf_header *, const char *, off_t, 56 uint32_t, uint32_t *, uint32_t); 57 const char *ctf_kind2name(uint16_t); 58 const char *ctf_enc2name(uint16_t); 59 const char *ctf_fpenc2name(uint16_t); 60 const char *ctf_off2name(struct ctf_header *, const char *, off_t, 61 uint32_t); 62 63 int elf_dump(char *, size_t, uint8_t); 64 const char *elf_idx2sym(size_t *, uint8_t); 65 66 /* elf.c */ 67 int iself(const char *, size_t); 68 int elf_getshstab(const char *, size_t, const char **, size_t *); 69 ssize_t elf_getsymtab(const char *, size_t filesize, const char *, 70 size_t, const Elf_Sym **, size_t *, const char **, 71 size_t *); 72 ssize_t elf_getsection(char *, size_t, const char *, const char *, 73 size_t, const char **, size_t *); 74 75 char *decompress(const char *, size_t, off_t); 76 77 int 78 main(int argc, char *argv[]) 79 { 80 const char *filename; 81 uint8_t flags = 0; 82 int ch, error = 0; 83 84 setlocale(LC_ALL, ""); 85 86 if (pledge("stdio rpath", NULL) == -1) 87 err(1, "pledge"); 88 89 while ((ch = getopt(argc, argv, "dfhlst")) != -1) { 90 switch (ch) { 91 case 'd': 92 flags |= DUMP_OBJECT; 93 break; 94 case 'f': 95 flags |= DUMP_FUNCTION; 96 break; 97 case 'h': 98 flags |= DUMP_HEADER; 99 break; 100 case 'l': 101 flags |= DUMP_LABEL; 102 break; 103 case 's': 104 flags |= DUMP_STRTAB; 105 break; 106 case 't': 107 flags |= DUMP_TYPE; 108 break; 109 default: 110 usage(); 111 } 112 } 113 114 argc -= optind; 115 argv += optind; 116 117 if (argc <= 0) 118 usage(); 119 120 /* Dump everything by default */ 121 if (flags == 0) 122 flags = 0xff; 123 124 while ((filename = *argv++) != NULL) 125 error |= dump(filename, flags); 126 127 return error; 128 } 129 130 int 131 dump(const char *path, uint8_t flags) 132 { 133 struct stat st; 134 int fd, error = 1; 135 char *p; 136 137 fd = open(path, O_RDONLY); 138 if (fd == -1) { 139 warn("open"); 140 return 1; 141 } 142 if (fstat(fd, &st) == -1) { 143 warn("fstat"); 144 close(fd); 145 return 1; 146 } 147 if ((uintmax_t)st.st_size > SIZE_MAX) { 148 warnx("file too big to fit memory"); 149 close(fd); 150 return 1; 151 } 152 153 p = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0); 154 if (p == MAP_FAILED) 155 err(1, "mmap"); 156 157 if (iself(p, st.st_size)) { 158 error = elf_dump(p, st.st_size, flags); 159 } else if (isctf(p, st.st_size)) { 160 error = ctf_dump(p, st.st_size, flags); 161 } 162 163 munmap(p, st.st_size); 164 close(fd); 165 166 return error; 167 } 168 169 const char *strtab; 170 const Elf_Sym *symtab; 171 size_t strtabsz, nsymb; 172 173 const char * 174 elf_idx2sym(size_t *idx, uint8_t type) 175 { 176 const Elf_Sym *st; 177 size_t i; 178 179 if (strtab == NULL) 180 return NULL; 181 182 for (i = *idx + 1; i < nsymb; i++) { 183 st = &symtab[i]; 184 185 if (ELF_ST_TYPE(st->st_info) != type) 186 continue; 187 188 if (st->st_name >= strtabsz) 189 break; 190 191 *idx = i; 192 return strtab + st->st_name; 193 } 194 195 return NULL; 196 } 197 198 int 199 elf_dump(char *p, size_t filesize, uint8_t flags) 200 { 201 Elf_Ehdr *eh = (Elf_Ehdr *)p; 202 Elf_Shdr *sh; 203 const char *shstab; 204 size_t i, shstabsz; 205 206 /* Find section header string table location and size. */ 207 if (elf_getshstab(p, filesize, &shstab, &shstabsz)) 208 return 1; 209 210 /* Find symbol table and associated string table. */ 211 if (elf_getsymtab(p, filesize, shstab, shstabsz, &symtab, &nsymb, 212 &strtab, &strtabsz) == -1) 213 warnx("symbol table not found"); 214 215 /* Find CTF section and dump it. */ 216 for (i = 0; i < eh->e_shnum; i++) { 217 sh = (Elf_Shdr *)(p + eh->e_shoff + i * eh->e_shentsize); 218 219 if ((sh->sh_link >= eh->e_shnum) || 220 (sh->sh_name >= shstabsz)) 221 continue; 222 223 if (strncmp(shstab + sh->sh_name, ELF_CTF, strlen(ELF_CTF))) 224 continue; 225 226 if ((sh->sh_offset + sh->sh_size) > filesize) 227 continue; 228 229 if (!isctf(p + sh->sh_offset, sh->sh_size)) 230 break; 231 232 return ctf_dump(p + sh->sh_offset, sh->sh_size, flags); 233 } 234 235 warnx("%s section not found", ELF_CTF); 236 return 1; 237 } 238 239 int 240 isctf(const char *p, size_t filesize) 241 { 242 struct ctf_header *cth = (struct ctf_header *)p; 243 off_t dlen; 244 245 if (filesize < sizeof(struct ctf_header)) { 246 warnx("file too small to be CTF"); 247 return 0; 248 } 249 250 if (cth->cth_magic != CTF_MAGIC || cth->cth_version != CTF_VERSION) 251 return 0; 252 253 dlen = (off_t)cth->cth_stroff + cth->cth_strlen; 254 if (dlen > (off_t)filesize && !(cth->cth_flags & CTF_F_COMPRESS)) { 255 warnx("bogus file size"); 256 return 0; 257 } 258 259 if ((cth->cth_lbloff & 3) || (cth->cth_objtoff & 1) || 260 (cth->cth_funcoff & 1) || (cth->cth_typeoff & 3)) { 261 warnx("wrongly aligned offset"); 262 return 0; 263 } 264 265 if ((cth->cth_lbloff >= dlen) || (cth->cth_objtoff >= dlen) || 266 (cth->cth_funcoff >= dlen) || (cth->cth_typeoff >= dlen)) { 267 warnx("truncated file"); 268 return 0; 269 } 270 271 if ((cth->cth_lbloff > cth->cth_objtoff) || 272 (cth->cth_objtoff > cth->cth_funcoff) || 273 (cth->cth_funcoff > cth->cth_typeoff) || 274 (cth->cth_typeoff > cth->cth_stroff)) { 275 warnx("corrupted file"); 276 return 0; 277 } 278 279 return 1; 280 } 281 282 int 283 ctf_dump(const char *p, size_t size, uint8_t flags) 284 { 285 struct ctf_header *cth = (struct ctf_header *)p; 286 off_t dlen; 287 char *data; 288 289 dlen = (off_t)cth->cth_stroff + cth->cth_strlen; 290 if (cth->cth_flags & CTF_F_COMPRESS) { 291 data = decompress(p + sizeof(*cth), size - sizeof(*cth), dlen); 292 if (data == NULL) 293 return 1; 294 } else { 295 data = (char *)p + sizeof(*cth); 296 } 297 298 if (flags & DUMP_HEADER) { 299 printf(" cth_magic = 0x%04x\n", cth->cth_magic); 300 printf(" cth_version = %u\n", cth->cth_version); 301 printf(" cth_flags = 0x%02x\n", cth->cth_flags); 302 printf(" cth_parlabel = %s\n", 303 ctf_off2name(cth, data, dlen, cth->cth_parlabel)); 304 printf(" cth_parname = %s\n", 305 ctf_off2name(cth, data, dlen, cth->cth_parname)); 306 printf(" cth_lbloff = %u\n", cth->cth_lbloff); 307 printf(" cth_objtoff = %u\n", cth->cth_objtoff); 308 printf(" cth_funcoff = %u\n", cth->cth_funcoff); 309 printf(" cth_typeoff = %u\n", cth->cth_typeoff); 310 printf(" cth_stroff = %u\n", cth->cth_stroff); 311 printf(" cth_strlen = %u\n", cth->cth_strlen); 312 printf("\n"); 313 } 314 315 if (flags & DUMP_LABEL) { 316 uint32_t lbloff = cth->cth_lbloff; 317 struct ctf_lblent *ctl; 318 319 while (lbloff < cth->cth_objtoff) { 320 ctl = (struct ctf_lblent *)(data + lbloff); 321 322 printf(" %5u %s\n", ctl->ctl_typeidx, 323 ctf_off2name(cth, data, dlen, ctl->ctl_label)); 324 325 lbloff += sizeof(*ctl); 326 } 327 printf("\n"); 328 } 329 330 if (flags & DUMP_OBJECT) { 331 uint32_t objtoff = cth->cth_objtoff; 332 size_t idx = 0, i = 0; 333 uint16_t *dsp; 334 const char *s; 335 int l; 336 337 while (objtoff < cth->cth_funcoff) { 338 dsp = (uint16_t *)(data + objtoff); 339 340 l = printf(" [%zu] %u", i++, *dsp); 341 if ((s = elf_idx2sym(&idx, STT_OBJECT)) != NULL) 342 printf("%*s %s (%zu)\n", (14 - l), "", s, idx); 343 else 344 printf("\n"); 345 346 objtoff += sizeof(*dsp); 347 } 348 printf("\n"); 349 } 350 351 if (flags & DUMP_FUNCTION) { 352 uint16_t *fsp, kind, vlen; 353 uint16_t *fstart, *fend; 354 size_t idx = 0, i = -1; 355 const char *s; 356 int l; 357 358 fstart = (uint16_t *)(data + cth->cth_funcoff); 359 fend = (uint16_t *)(data + cth->cth_typeoff); 360 361 fsp = fstart; 362 while (fsp < fend) { 363 kind = CTF_INFO_KIND(*fsp); 364 vlen = CTF_INFO_VLEN(*fsp); 365 s = elf_idx2sym(&idx, STT_FUNC); 366 fsp++; 367 i++; 368 369 if (kind == CTF_K_UNKNOWN && vlen == 0) 370 continue; 371 372 l = printf(" [%zu] FUNC ", i); 373 if (s != NULL) 374 printf("(%s) ", s); 375 printf("returns: %u args: (", *fsp++); 376 while (vlen-- > 0 && fsp < fend) 377 printf("%u%s", *fsp++, (vlen > 0) ? ", " : ""); 378 printf(")\n"); 379 } 380 printf("\n"); 381 } 382 383 if (flags & DUMP_TYPE) { 384 uint32_t idx = 1, offset = cth->cth_typeoff; 385 uint32_t stroff = cth->cth_stroff; 386 387 while (offset < stroff) { 388 ctf_dump_type(cth, data, dlen, stroff, &offset, idx++); 389 } 390 printf("\n"); 391 } 392 393 if (flags & DUMP_STRTAB) { 394 uint32_t offset = 0; 395 const char *str; 396 397 while (offset < cth->cth_strlen) { 398 str = ctf_off2name(cth, data, dlen, offset); 399 400 printf(" [%u] ", offset); 401 if (strcmp(str, "(anon)")) 402 offset += printf("%s\n", str); 403 else { 404 printf("\\0\n"); 405 offset++; 406 } 407 } 408 printf("\n"); 409 } 410 411 if (cth->cth_flags & CTF_F_COMPRESS) 412 free(data); 413 414 return 0; 415 } 416 417 void 418 ctf_dump_type(struct ctf_header *cth, const char *data, off_t dlen, 419 uint32_t stroff, uint32_t *offset, uint32_t idx) 420 { 421 const char *p = data + *offset; 422 const struct ctf_type *ctt = (struct ctf_type *)p; 423 const struct ctf_array *cta; 424 uint16_t *argp, i, kind, vlen, root; 425 uint32_t eob, toff; 426 uint64_t size; 427 const char *name, *kname; 428 429 kind = CTF_INFO_KIND(ctt->ctt_info); 430 vlen = CTF_INFO_VLEN(ctt->ctt_info); 431 root = CTF_INFO_ISROOT(ctt->ctt_info); 432 name = ctf_off2name(cth, data, dlen, ctt->ctt_name); 433 434 if (root) 435 printf(" <%u> ", idx); 436 else 437 printf(" [%u] ", idx); 438 439 if ((kname = ctf_kind2name(kind)) != NULL) 440 printf("%s %s", kname, name); 441 442 if (ctt->ctt_size <= CTF_MAX_SIZE) { 443 size = ctt->ctt_size; 444 toff = sizeof(struct ctf_stype); 445 } else { 446 size = CTF_TYPE_LSIZE(ctt); 447 toff = sizeof(struct ctf_type); 448 } 449 450 switch (kind) { 451 case CTF_K_UNKNOWN: 452 case CTF_K_FORWARD: 453 break; 454 case CTF_K_INTEGER: 455 eob = *((uint32_t *)(p + toff)); 456 toff += sizeof(uint32_t); 457 printf(" encoding=%s offset=%u bits=%u", 458 ctf_enc2name(CTF_INT_ENCODING(eob)), CTF_INT_OFFSET(eob), 459 CTF_INT_BITS(eob)); 460 break; 461 case CTF_K_FLOAT: 462 eob = *((uint32_t *)(p + toff)); 463 toff += sizeof(uint32_t); 464 printf(" encoding=%s offset=%u bits=%u", 465 ctf_fpenc2name(CTF_FP_ENCODING(eob)), CTF_FP_OFFSET(eob), 466 CTF_FP_BITS(eob)); 467 break; 468 case CTF_K_ARRAY: 469 cta = (struct ctf_array *)(p + toff); 470 printf(" content: %u index: %u nelems: %u\n", cta->cta_contents, 471 cta->cta_index, cta->cta_nelems); 472 toff += sizeof(struct ctf_array); 473 break; 474 case CTF_K_FUNCTION: 475 argp = (uint16_t *)(p + toff); 476 printf(" returns: %u args: (%u", ctt->ctt_type, *argp); 477 for (i = 1; i < vlen; i++) { 478 argp++; 479 if ((const char *)argp > data + dlen) 480 errx(1, "offset exceeds CTF section"); 481 482 printf(", %u", *argp); 483 } 484 printf(")"); 485 toff += (vlen + (vlen & 1)) * sizeof(uint16_t); 486 break; 487 case CTF_K_STRUCT: 488 case CTF_K_UNION: 489 printf(" (%llu bytes)\n", size); 490 491 if (size < CTF_LSTRUCT_THRESH) { 492 for (i = 0; i < vlen; i++) { 493 struct ctf_member *ctm; 494 495 if (p + toff > data + dlen) 496 errx(1, "offset exceeds CTF section"); 497 498 if (toff > (stroff - sizeof(*ctm))) 499 break; 500 501 ctm = (struct ctf_member *)(p + toff); 502 toff += sizeof(struct ctf_member); 503 504 printf("\t%s type=%u off=%u\n", 505 ctf_off2name(cth, data, dlen, 506 ctm->ctm_name), 507 ctm->ctm_type, ctm->ctm_offset); 508 } 509 } else { 510 for (i = 0; i < vlen; i++) { 511 struct ctf_lmember *ctlm; 512 513 if (p + toff > data + dlen) 514 errx(1, "offset exceeds CTF section"); 515 516 if (toff > (stroff - sizeof(*ctlm))) 517 break; 518 519 ctlm = (struct ctf_lmember *)(p + toff); 520 toff += sizeof(struct ctf_lmember); 521 522 printf("\t%s type=%u off=%llu\n", 523 ctf_off2name(cth, data, dlen, 524 ctlm->ctlm_name), 525 ctlm->ctlm_type, CTF_LMEM_OFFSET(ctlm)); 526 } 527 } 528 break; 529 case CTF_K_ENUM: 530 printf("\n"); 531 for (i = 0; i < vlen; i++) { 532 struct ctf_enum *cte; 533 534 if (p + toff > data + dlen) 535 errx(1, "offset exceeds CTF section"); 536 537 if (toff > (stroff - sizeof(*cte))) 538 break; 539 540 cte = (struct ctf_enum *)(p + toff); 541 toff += sizeof(struct ctf_enum); 542 543 printf("\t%s = %d\n", 544 ctf_off2name(cth, data, dlen, cte->cte_name), 545 cte->cte_value); 546 } 547 break; 548 case CTF_K_POINTER: 549 case CTF_K_TYPEDEF: 550 case CTF_K_VOLATILE: 551 case CTF_K_CONST: 552 case CTF_K_RESTRICT: 553 printf(" refers to %u", ctt->ctt_type); 554 break; 555 default: 556 errx(1, "incorrect type %u at offset %u", kind, *offset); 557 } 558 559 printf("\n"); 560 561 *offset += toff; 562 } 563 564 const char * 565 ctf_kind2name(uint16_t kind) 566 { 567 static const char *kind_name[] = { NULL, "INTEGER", "FLOAT", "POINTER", 568 "ARRAY", "FUNCTION", "STRUCT", "UNION", "ENUM", "FORWARD", 569 "TYPEDEF", "VOLATILE", "CONST", "RESTRICT" }; 570 571 if (kind >= nitems(kind_name)) 572 return NULL; 573 574 return kind_name[kind]; 575 } 576 577 const char * 578 ctf_enc2name(uint16_t enc) 579 { 580 static const char *enc_name[] = { "SIGNED", "CHAR", "SIGNED CHAR", 581 "BOOL", "SIGNED BOOL" }; 582 static char invalid[7]; 583 584 if (enc == CTF_INT_VARARGS) 585 return "VARARGS"; 586 587 if (enc > 0 && enc <= nitems(enc_name)) 588 return enc_name[enc - 1]; 589 590 snprintf(invalid, sizeof(invalid), "0x%x", enc); 591 return invalid; 592 } 593 594 const char * 595 ctf_fpenc2name(uint16_t enc) 596 { 597 static const char *enc_name[] = { "SINGLE", "DOUBLE", NULL, NULL, 598 NULL, "LDOUBLE" }; 599 static char invalid[7]; 600 601 if (enc > 0 && enc <= nitems(enc_name) && enc_name[enc - 1] != NULL) 602 return enc_name[enc - 1]; 603 604 snprintf(invalid, sizeof(invalid), "0x%x", enc); 605 return invalid; 606 } 607 608 const char * 609 ctf_off2name(struct ctf_header *cth, const char *data, off_t dlen, 610 uint32_t offset) 611 { 612 const char *name; 613 614 if (CTF_NAME_STID(offset) != CTF_STRTAB_0) 615 return "external"; 616 617 if (CTF_NAME_OFFSET(offset) >= cth->cth_strlen) 618 return "exceeds strlab"; 619 620 if (cth->cth_stroff + CTF_NAME_OFFSET(offset) >= dlen) 621 return "invalid"; 622 623 name = data + cth->cth_stroff + CTF_NAME_OFFSET(offset); 624 if (*name == '\0') 625 return "(anon)"; 626 627 return name; 628 } 629 630 char * 631 decompress(const char *buf, size_t size, off_t len) 632 { 633 #ifdef ZLIB 634 z_stream stream; 635 char *data; 636 int error; 637 638 data = malloc(len); 639 if (data == NULL) { 640 warn(NULL); 641 return NULL; 642 } 643 644 memset(&stream, 0, sizeof(stream)); 645 stream.next_in = (void *)buf; 646 stream.avail_in = size; 647 stream.next_out = (uint8_t *)data; 648 stream.avail_out = len; 649 650 if ((error = inflateInit(&stream)) != Z_OK) { 651 warnx("zlib inflateInit failed: %s", zError(error)); 652 goto exit; 653 } 654 655 if ((error = inflate(&stream, Z_FINISH)) != Z_STREAM_END) { 656 warnx("zlib inflate failed: %s", zError(error)); 657 inflateEnd(&stream); 658 goto exit; 659 } 660 661 if ((error = inflateEnd(&stream)) != Z_OK) { 662 warnx("zlib inflateEnd failed: %s", zError(error)); 663 goto exit; 664 } 665 666 if (stream.total_out != len) { 667 warnx("decompression failed: %llu != %llu", 668 stream.total_out, len); 669 goto exit; 670 } 671 672 return data; 673 674 exit: 675 free(data); 676 #endif /* ZLIB */ 677 return NULL; 678 } 679 680 __dead void 681 usage(void) 682 { 683 fprintf(stderr, "usage: %s [-dfhlst] file ...\n", 684 getprogname()); 685 exit(1); 686 } 687