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