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