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