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