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