1 /* $OpenBSD: ctfdump.c,v 1.27 2022/08/14 15:01:18 millert 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 *, size_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 *, size_t, 62 uint32_t); 63 64 char *decompress(const char *, size_t, size_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; 277 size_t dlen; 278 279 if (filesize < sizeof(struct ctf_header)) { 280 warnx("file too small to be CTF"); 281 return 0; 282 } 283 284 memcpy(&cth, p, sizeof(struct ctf_header)); 285 if (cth.cth_magic != CTF_MAGIC || cth.cth_version != CTF_VERSION) 286 return 0; 287 288 dlen = cth.cth_stroff + cth.cth_strlen; 289 if (dlen > filesize && !(cth.cth_flags & CTF_F_COMPRESS)) { 290 warnx("bogus file size"); 291 return 0; 292 } 293 294 if ((cth.cth_lbloff & 3) || (cth.cth_objtoff & 1) || 295 (cth.cth_funcoff & 1) || (cth.cth_typeoff & 3)) { 296 warnx("wrongly aligned offset"); 297 return 0; 298 } 299 300 if ((cth.cth_lbloff >= dlen) || (cth.cth_objtoff >= dlen) || 301 (cth.cth_funcoff >= dlen) || (cth.cth_typeoff >= dlen)) { 302 warnx("truncated file"); 303 return 0; 304 } 305 306 if ((cth.cth_lbloff > cth.cth_objtoff) || 307 (cth.cth_objtoff > cth.cth_funcoff) || 308 (cth.cth_funcoff > cth.cth_typeoff) || 309 (cth.cth_typeoff > cth.cth_stroff)) { 310 warnx("corrupted file"); 311 return 0; 312 } 313 314 return 1; 315 } 316 317 int 318 ctf_dump(const char *p, size_t size, uint8_t flags) 319 { 320 struct ctf_header cth; 321 size_t dlen; 322 char *data; 323 324 memcpy(&cth, p, sizeof(struct ctf_header)); 325 dlen = cth.cth_stroff + cth.cth_strlen; 326 if (cth.cth_flags & CTF_F_COMPRESS) { 327 data = decompress(p + sizeof(cth), size - sizeof(cth), dlen); 328 if (data == NULL) 329 return 1; 330 } else { 331 data = (char *)p + sizeof(cth); 332 } 333 334 if (flags & DUMP_HEADER) { 335 printf(" cth_magic = 0x%04x\n", cth.cth_magic); 336 printf(" cth_version = %u\n", cth.cth_version); 337 printf(" cth_flags = 0x%02x\n", cth.cth_flags); 338 printf(" cth_parlabel = %s\n", 339 ctf_off2name(&cth, data, dlen, cth.cth_parlabel)); 340 printf(" cth_parname = %s\n", 341 ctf_off2name(&cth, data, dlen, cth.cth_parname)); 342 printf(" cth_lbloff = %u\n", cth.cth_lbloff); 343 printf(" cth_objtoff = %u\n", cth.cth_objtoff); 344 printf(" cth_funcoff = %u\n", cth.cth_funcoff); 345 printf(" cth_typeoff = %u\n", cth.cth_typeoff); 346 printf(" cth_stroff = %u\n", cth.cth_stroff); 347 printf(" cth_strlen = %u\n", cth.cth_strlen); 348 printf("\n"); 349 } 350 351 if (flags & DUMP_LABEL) { 352 uint32_t lbloff = cth.cth_lbloff; 353 struct ctf_lblent *ctl; 354 355 while (lbloff < cth.cth_objtoff) { 356 ctl = (struct ctf_lblent *)(data + lbloff); 357 358 printf(" %5u %s\n", ctl->ctl_typeidx, 359 ctf_off2name(&cth, data, dlen, ctl->ctl_label)); 360 361 lbloff += sizeof(*ctl); 362 } 363 printf("\n"); 364 } 365 366 if (flags & DUMP_OBJECT) { 367 uint32_t objtoff = cth.cth_objtoff; 368 size_t idx = 0, i = 0; 369 uint16_t *dsp; 370 const char *s; 371 int l; 372 373 while (objtoff < cth.cth_funcoff) { 374 dsp = (uint16_t *)(data + objtoff); 375 376 l = printf(" [%zu] %u", i++, *dsp); 377 if ((s = elf_idx2sym(&idx, STT_OBJECT)) != NULL) 378 printf("%*s %s (%zu)\n", (14 - l), "", s, idx); 379 else 380 printf("\n"); 381 382 objtoff += sizeof(*dsp); 383 } 384 printf("\n"); 385 } 386 387 if (flags & DUMP_FUNCTION) { 388 uint16_t *fsp, kind, vlen; 389 uint16_t *fstart, *fend; 390 size_t idx = 0, i = -1; 391 const char *s; 392 int l; 393 394 fstart = (uint16_t *)(data + cth.cth_funcoff); 395 fend = (uint16_t *)(data + cth.cth_typeoff); 396 397 fsp = fstart; 398 while (fsp < fend) { 399 kind = CTF_INFO_KIND(*fsp); 400 vlen = CTF_INFO_VLEN(*fsp); 401 s = elf_idx2sym(&idx, STT_FUNC); 402 fsp++; 403 i++; 404 405 if (kind == CTF_K_UNKNOWN && vlen == 0) 406 continue; 407 408 l = printf(" [%zu] FUNC ", i); 409 if (s != NULL) 410 printf("(%s) ", s); 411 printf("returns: %u args: (", *fsp++); 412 while (vlen-- > 0 && fsp < fend) 413 printf("%u%s", *fsp++, (vlen > 0) ? ", " : ""); 414 printf(")\n"); 415 } 416 printf("\n"); 417 } 418 419 if (flags & DUMP_TYPE) { 420 uint32_t idx = 1, offset = cth.cth_typeoff; 421 uint32_t stroff = cth.cth_stroff; 422 423 while (offset < stroff) { 424 ctf_dump_type(&cth, data, dlen, stroff, &offset, idx++); 425 } 426 printf("\n"); 427 } 428 429 if (flags & DUMP_STRTAB) { 430 uint32_t offset = 0; 431 const char *str; 432 433 while (offset < cth.cth_strlen) { 434 str = ctf_off2name(&cth, data, dlen, offset); 435 436 printf(" [%u] ", offset); 437 if (strcmp(str, "(anon)")) 438 offset += printf("%s\n", str); 439 else { 440 printf("\\0\n"); 441 offset++; 442 } 443 } 444 printf("\n"); 445 } 446 447 if (cth.cth_flags & CTF_F_COMPRESS) 448 free(data); 449 450 return 0; 451 } 452 453 void 454 ctf_dump_type(struct ctf_header *cth, const char *data, size_t dlen, 455 uint32_t stroff, uint32_t *offset, uint32_t idx) 456 { 457 const char *p = data + *offset; 458 const struct ctf_type *ctt = (struct ctf_type *)p; 459 const struct ctf_array *cta; 460 uint16_t *argp, i, kind, vlen, root; 461 uint32_t eob, toff; 462 uint64_t size; 463 const char *name, *kname; 464 465 kind = CTF_INFO_KIND(ctt->ctt_info); 466 vlen = CTF_INFO_VLEN(ctt->ctt_info); 467 root = CTF_INFO_ISROOT(ctt->ctt_info); 468 name = ctf_off2name(cth, data, dlen, ctt->ctt_name); 469 470 if (root) 471 printf(" <%u> ", idx); 472 else 473 printf(" [%u] ", idx); 474 475 if ((kname = ctf_kind2name(kind)) != NULL) 476 printf("%s %s", kname, name); 477 478 if (ctt->ctt_size <= CTF_MAX_SIZE) { 479 size = ctt->ctt_size; 480 toff = sizeof(struct ctf_stype); 481 } else { 482 size = CTF_TYPE_LSIZE(ctt); 483 toff = sizeof(struct ctf_type); 484 } 485 486 switch (kind) { 487 case CTF_K_UNKNOWN: 488 case CTF_K_FORWARD: 489 break; 490 case CTF_K_INTEGER: 491 eob = *((uint32_t *)(p + toff)); 492 toff += sizeof(uint32_t); 493 printf(" encoding=%s offset=%u bits=%u", 494 ctf_enc2name(CTF_INT_ENCODING(eob)), CTF_INT_OFFSET(eob), 495 CTF_INT_BITS(eob)); 496 break; 497 case CTF_K_FLOAT: 498 eob = *((uint32_t *)(p + toff)); 499 toff += sizeof(uint32_t); 500 printf(" encoding=%s offset=%u bits=%u", 501 ctf_fpenc2name(CTF_FP_ENCODING(eob)), CTF_FP_OFFSET(eob), 502 CTF_FP_BITS(eob)); 503 break; 504 case CTF_K_ARRAY: 505 cta = (struct ctf_array *)(p + toff); 506 printf(" content: %u index: %u nelems: %u\n", cta->cta_contents, 507 cta->cta_index, cta->cta_nelems); 508 toff += sizeof(struct ctf_array); 509 break; 510 case CTF_K_FUNCTION: 511 argp = (uint16_t *)(p + toff); 512 printf(" returns: %u args: (%u", ctt->ctt_type, *argp); 513 for (i = 1; i < vlen; i++) { 514 argp++; 515 if ((const char *)argp > data + dlen) 516 errx(1, "offset exceeds CTF section"); 517 518 printf(", %u", *argp); 519 } 520 printf(")"); 521 toff += (vlen + (vlen & 1)) * sizeof(uint16_t); 522 break; 523 case CTF_K_STRUCT: 524 case CTF_K_UNION: 525 printf(" (%llu bytes)\n", size); 526 527 if (size < CTF_LSTRUCT_THRESH) { 528 for (i = 0; i < vlen; i++) { 529 struct ctf_member *ctm; 530 531 if (p + toff > data + dlen) 532 errx(1, "offset exceeds CTF section"); 533 534 if (toff > (stroff - sizeof(*ctm))) 535 break; 536 537 ctm = (struct ctf_member *)(p + toff); 538 toff += sizeof(struct ctf_member); 539 540 printf("\t%s type=%u off=%u\n", 541 ctf_off2name(cth, data, dlen, 542 ctm->ctm_name), 543 ctm->ctm_type, ctm->ctm_offset); 544 } 545 } else { 546 for (i = 0; i < vlen; i++) { 547 struct ctf_lmember *ctlm; 548 549 if (p + toff > data + dlen) 550 errx(1, "offset exceeds CTF section"); 551 552 if (toff > (stroff - sizeof(*ctlm))) 553 break; 554 555 ctlm = (struct ctf_lmember *)(p + toff); 556 toff += sizeof(struct ctf_lmember); 557 558 printf("\t%s type=%u off=%llu\n", 559 ctf_off2name(cth, data, dlen, 560 ctlm->ctlm_name), 561 ctlm->ctlm_type, CTF_LMEM_OFFSET(ctlm)); 562 } 563 } 564 break; 565 case CTF_K_ENUM: 566 printf("\n"); 567 for (i = 0; i < vlen; i++) { 568 struct ctf_enum *cte; 569 570 if (p + toff > data + dlen) 571 errx(1, "offset exceeds CTF section"); 572 573 if (toff > (stroff - sizeof(*cte))) 574 break; 575 576 cte = (struct ctf_enum *)(p + toff); 577 toff += sizeof(struct ctf_enum); 578 579 printf("\t%s = %d\n", 580 ctf_off2name(cth, data, dlen, cte->cte_name), 581 cte->cte_value); 582 } 583 break; 584 case CTF_K_POINTER: 585 case CTF_K_TYPEDEF: 586 case CTF_K_VOLATILE: 587 case CTF_K_CONST: 588 case CTF_K_RESTRICT: 589 printf(" refers to %u", ctt->ctt_type); 590 break; 591 default: 592 errx(1, "incorrect type %u at offset %u", kind, *offset); 593 } 594 595 printf("\n"); 596 597 *offset += toff; 598 } 599 600 const char * 601 ctf_kind2name(uint16_t kind) 602 { 603 static const char *kind_name[] = { NULL, "INTEGER", "FLOAT", "POINTER", 604 "ARRAY", "FUNCTION", "STRUCT", "UNION", "ENUM", "FORWARD", 605 "TYPEDEF", "VOLATILE", "CONST", "RESTRICT" }; 606 607 if (kind >= nitems(kind_name)) 608 return NULL; 609 610 return kind_name[kind]; 611 } 612 613 const char * 614 ctf_enc2name(uint16_t enc) 615 { 616 static const char *enc_name[] = { "SIGNED", "CHAR", "SIGNED CHAR", 617 "BOOL", "SIGNED BOOL" }; 618 static char invalid[7]; 619 620 if (enc == CTF_INT_VARARGS) 621 return "VARARGS"; 622 623 if (enc > 0 && enc <= nitems(enc_name)) 624 return enc_name[enc - 1]; 625 626 snprintf(invalid, sizeof(invalid), "0x%x", enc); 627 return invalid; 628 } 629 630 const char * 631 ctf_fpenc2name(uint16_t enc) 632 { 633 static const char *enc_name[] = { "SINGLE", "DOUBLE", NULL, NULL, 634 NULL, "LDOUBLE" }; 635 static char invalid[7]; 636 637 if (enc > 0 && enc <= nitems(enc_name) && enc_name[enc - 1] != NULL) 638 return enc_name[enc - 1]; 639 640 snprintf(invalid, sizeof(invalid), "0x%x", enc); 641 return invalid; 642 } 643 644 const char * 645 ctf_off2name(struct ctf_header *cth, const char *data, size_t dlen, 646 uint32_t offset) 647 { 648 const char *name; 649 650 if (CTF_NAME_STID(offset) != CTF_STRTAB_0) 651 return "external"; 652 653 if (CTF_NAME_OFFSET(offset) >= cth->cth_strlen) 654 return "exceeds strlab"; 655 656 if (cth->cth_stroff + CTF_NAME_OFFSET(offset) >= dlen) 657 return "invalid"; 658 659 name = data + cth->cth_stroff + CTF_NAME_OFFSET(offset); 660 if (*name == '\0') 661 return "(anon)"; 662 663 return name; 664 } 665 666 char * 667 decompress(const char *buf, size_t size, size_t len) 668 { 669 #ifdef ZLIB 670 z_stream stream; 671 char *data; 672 int error; 673 674 data = malloc(len); 675 if (data == NULL) { 676 warn(NULL); 677 return NULL; 678 } 679 680 memset(&stream, 0, sizeof(stream)); 681 stream.next_in = (void *)buf; 682 stream.avail_in = size; 683 stream.next_out = (uint8_t *)data; 684 stream.avail_out = len; 685 686 if ((error = inflateInit(&stream)) != Z_OK) { 687 warnx("zlib inflateInit failed: %s", zError(error)); 688 goto exit; 689 } 690 691 if ((error = inflate(&stream, Z_FINISH)) != Z_STREAM_END) { 692 warnx("zlib inflate failed: %s", zError(error)); 693 inflateEnd(&stream); 694 goto exit; 695 } 696 697 if ((error = inflateEnd(&stream)) != Z_OK) { 698 warnx("zlib inflateEnd failed: %s", zError(error)); 699 goto exit; 700 } 701 702 if (stream.total_out != len) { 703 warnx("decompression failed: %lu != %zu", 704 stream.total_out, len); 705 goto exit; 706 } 707 708 return data; 709 710 exit: 711 free(data); 712 #endif /* ZLIB */ 713 return NULL; 714 } 715 716 __dead void 717 usage(void) 718 { 719 fprintf(stderr, "usage: %s [-dfhlst] file ...\n", 720 getprogname()); 721 exit(1); 722 } 723