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