]> sbz's 6dev Repos - ctfdump/.git/blob - ctfdump.c
Skip the header in the non compressed case too.
[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         off_t                    dlen = cth->cth_stroff + cth->cth_strlen;
401         char                    *data;
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         } else {
408                 data = (char *)p + sizeof(*cth);
409         }
410
411         if (flags & DUMP_HEADER) {
412                 printf("cth_magic    = 0x%04x\n", cth->cth_magic);
413                 printf("cth_version  = %d\n", cth->cth_version);
414                 printf("cth_flags    = 0x%02x\n", cth->cth_flags);
415                 printf("cth_parlabel = %s\n",
416                     ctf_off2name(cth, data, dlen, cth->cth_parname));
417                 printf("cth_parname  = %s\n",
418                     ctf_off2name(cth, data, dlen, cth->cth_parname));
419                 printf("cth_lbloff   = %d\n", cth->cth_lbloff);
420                 printf("cth_objtoff  = %d\n", cth->cth_objtoff);
421                 printf("cth_funcoff  = %d\n", cth->cth_funcoff);
422                 printf("cth_typeoff  = %d\n", cth->cth_typeoff);
423                 printf("cth_stroff   = %d\n", cth->cth_stroff);
424                 printf("cth_strlen   = %d\n", cth->cth_strlen);
425         }
426
427         if (flags & DUMP_LABEL) {
428                 unsigned int             lbloff = cth->cth_lbloff;
429                 struct ctf_lblent       *ctl;
430
431                 while (lbloff < cth->cth_objtoff) {
432                         ctl = (struct ctf_lblent *)(data + lbloff);
433
434                         printf("%5u %s\n", ctl->ctl_typeidx,
435                             ctf_off2name(cth, data, dlen, ctl->ctl_label));
436
437                         lbloff += sizeof(*ctl);
438                 }
439         }
440
441         if (flags & DUMP_OBJECT) {
442                 unsigned int             objtoff = cth->cth_objtoff;
443                 size_t                   idx = 0, i = 0;
444                 unsigned short          *dsp;
445                 const char              *s;
446                 int                      l;
447
448                 while (objtoff < cth->cth_funcoff) {
449                         dsp = (unsigned short *)(data + objtoff);
450
451                         l = printf("[%zu] %u", i++, *dsp);
452                         if ((s = elf_idx2sym(&idx, STT_OBJECT)) != NULL)
453                                 printf("%*s %s (%zu)\n", (12 - l), "", s, idx);
454                         else
455                                 printf("\n");
456
457                         objtoff += sizeof(*dsp);
458                 }
459         }
460
461         if (flags & DUMP_FUNCTION) {
462                 unsigned short          *fsp, kind, vlen;
463                 size_t                   idx = 0, i = 0;
464                 const char              *s;
465                 int                      l;
466
467                 fsp = (unsigned short *)(data + cth->cth_funcoff);
468                 while (fsp < (unsigned short *)(data + cth->cth_typeoff)) {
469                         kind = CTF_INFO_KIND(*fsp);
470                         vlen = CTF_INFO_VLEN(*fsp);
471                         fsp++;
472
473                         if (kind == CTF_K_UNKNOWN && vlen == 0)
474                                 continue;
475
476                         l = printf("%u [%zu] FUNC", vlen, i++);
477                         if ((s = elf_idx2sym(&idx, STT_FUNC)) != NULL)
478                                 printf(" (%s)", s);
479                         printf(" returns: %u args: (", *fsp++);
480                         while (vlen-- > 0)
481                                 printf("%u%s", *fsp++, (vlen > 0) ? ", " : "");
482                         printf(")\n");
483                 }
484         }
485
486         if (flags & DUMP_STRTAB) {
487                 unsigned int             offset = 0;
488                 const char              *str;
489
490                 while (offset < cth->cth_strlen) {
491                         str = data + cth->cth_stroff + offset;
492
493                         printf("[%u] ", offset);
494                         if (*str != '\0')
495                                 offset += printf("%s\n", str);
496                         else {
497                                 printf("\\0\n");
498                                 offset++;
499                         }
500                 }
501         }
502
503         if (flags & DUMP_TYPE) {
504                 unsigned int             idx = 1, offset = 0;
505
506                 while (offset < cth->cth_stroff)
507                         offset += ctf_dump_type(cth, data, dlen, offset, idx++);
508
509         }
510
511         if (cth->cth_flags & CTF_F_COMPRESS)
512                 free(data);
513
514         return 0;
515 }
516
517 unsigned int
518 ctf_dump_type(struct ctf_header *cth, const char *data, off_t dlen,
519     unsigned int offset, unsigned int idx)
520 {
521         const struct ctf_type   *ctt;
522         unsigned short           kind, vlen, root;
523         unsigned int             toff, tlen = 0;
524         uint64_t                 size;
525         const char              *name, *kname;
526
527         ctt = (struct ctf_type *)(data + cth->cth_typeoff + offset);
528         kind = CTF_INFO_KIND(ctt->ctt_info);
529         vlen = CTF_INFO_VLEN(ctt->ctt_info);
530         root = CTF_INFO_ISROOT(ctt->ctt_info);
531         name = ctf_off2name(cth, data, dlen, ctt->ctt_name);
532
533         if (root)
534                 printf("<%u> ", idx);
535         else
536                 printf("[%u] ", idx);
537
538         if ((kname = ctf_kind2name(kind)) != NULL)
539                 printf("%s %s", kname, name);
540
541         if (ctt->ctt_size <= CTF_MAX_SIZE) {
542                 size = ctt->ctt_size;
543                 toff = sizeof(struct ctf_stype);
544         } else {
545                 size = CTF_TYPE_LSIZE(ctt);
546                 toff = sizeof(struct ctf_type);
547         }
548
549         switch (kind) {
550         case CTF_K_UNKNOWN:
551         case CTF_K_FORWARD:
552                 break;
553         case CTF_K_INTEGER:
554                 tlen = sizeof(unsigned int);
555                 break;
556         case CTF_K_FLOAT:
557                 break;
558         case CTF_K_ARRAY:
559                 tlen = sizeof(struct ctf_array);
560                 break;
561         case CTF_K_FUNCTION:
562                 tlen = (vlen + (vlen & 1)) * sizeof(unsigned short);
563                 break;
564         case CTF_K_STRUCT:
565         case CTF_K_UNION:
566                 printf(" (%llu bytes)", size);
567                 if (size < CTF_LSTRUCT_THRESH)
568                         tlen = vlen * sizeof(struct ctf_member);
569                 else
570                         tlen = vlen * sizeof(struct ctf_lmember);
571                 break;
572         case CTF_K_ENUM:
573                 tlen = vlen * sizeof(struct ctf_enum);
574                 break;
575         case CTF_K_POINTER:
576                 vlen = sizeof(unsigned int);
577                 /* FALLTHROUGH */
578         case CTF_K_TYPEDEF:
579         case CTF_K_VOLATILE:
580         case CTF_K_CONST:
581         case CTF_K_RESTRICT:
582                 printf(" refers to %u", ctt->ctt_type);
583                 break;
584         default:
585                 errx(1, "incorrect type %u at offset %u", kind, offset);
586         }
587
588         printf("\n");
589
590         return toff + tlen;
591 }
592
593 const char *
594 ctf_kind2name(unsigned short kind)
595 {
596         static const char *kind_name[] = { NULL, "INTEGER", "FLOAT", "POINTER",
597            "ARRAYS", "FUNCTION", "STRUCT", "UNION", "ENUM", "FORWARD",
598            "TYPEDEF", "VOLATILE", "CONST", "RESTRICT" };
599
600         if (kind >= nitems(kind_name))
601                 return NULL;
602
603         return kind_name[kind];
604 }
605
606 const char *
607 ctf_off2name(struct ctf_header *cth, const char *data, off_t dlen,
608     unsigned int offset)
609 {
610         const char              *name;
611
612         if (CTF_NAME_STID(offset) != CTF_STRTAB_0)
613                 return "external";
614
615         if (CTF_NAME_OFFSET(offset) >= cth->cth_strlen)
616                 return "exceeds strlab";
617
618         if (cth->cth_stroff + CTF_NAME_OFFSET(offset) >= dlen)
619                 return "invalid";
620
621         name = data + cth->cth_stroff + CTF_NAME_OFFSET(offset);
622         if (*name == '\0')
623                 return "(anon)";
624
625         return name;
626 }
627
628 char *
629 decompress(const char *buf, size_t size, off_t len)
630 {
631 #ifdef ZLIB
632         z_stream                 stream;
633         char                    *data;
634         int                      error;
635
636         data = malloc(len);
637         if (data == NULL) {
638                 warn(NULL);
639                 return NULL;
640         }
641
642         memset(&stream, 0, sizeof(stream));
643         stream.next_in = (void *)buf;
644         stream.avail_in = size;
645         stream.next_out = data;
646         stream.avail_out = len;
647
648         if ((error = inflateInit(&stream)) != Z_OK) {
649                 warnx("zlib inflateInit failed: %s", zError(error));
650                 goto exit;
651         }
652
653         if ((error = inflate(&stream, Z_FINISH)) != Z_STREAM_END) {
654                 warnx("zlib inflate failed: %s", zError(error));
655                 goto exit;
656         }
657
658         if ((error = inflateEnd(&stream)) != Z_OK) {
659                 warnx("zlib inflateEnd failed: %s", zError(error));
660                 goto exit;
661         }
662
663         if (stream.total_out != len) {
664                 warnx("decompression failed: %llu != %llu",
665                     stream.total_out, len);
666                 goto exit;
667         }
668
669         return data;
670
671 exit:
672         free(data);
673 #endif /* ZLIB */
674         return NULL;
675 }
676
677 __dead void
678 usage(void)
679 {
680         extern char             *__progname;
681
682         fprintf(stderr, "usage: %s [-dfhlst] [file ...]\n",
683             __progname);
684         exit(1);
685 }
686