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