]> sbz's 6dev Repos - ctfdump/.git/blob - ctfdump.c
Initialize offset correctly when dumping types.
[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_off2name(struct ctf_header *, const char *, off_t,
58                      unsigned int);
59
60 int              elf_dump(const char *, size_t, uint8_t);
61 const char      *elf_idx2sym(size_t *, unsigned char);
62
63 /* elf.c */
64 int              iself(const char *, size_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_getsection(const char *, const char *, const char *,
69                      size_t, 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 const char              *strtab;
164 const Elf_Sym           *symtab;
165 size_t                   strtabsize, nsymb;
166
167 const char *
168 elf_idx2sym(size_t *idx, unsigned char type)
169 {
170         const Elf_Sym   *st;
171         size_t           i;
172
173         for (i = *idx + 1; i < nsymb; i++) {
174                 st = &symtab[i];
175
176                 if (ELF_ST_TYPE(st->st_info) != type)
177                         continue;
178
179                 *idx = i;
180                 return strtab + st->st_name;
181         }
182
183         return NULL;
184 }
185
186 int
187 elf_dump(const char *p, size_t filesize, uint8_t flags)
188 {
189         Elf_Ehdr                *eh = (Elf_Ehdr *)p;
190         Elf_Shdr                *sh;
191         const char              *shstrtab;
192         size_t                   i, shstrtabsize;
193
194         /* Find section header string table location and size. */
195         if (elf_getshstrtab(p, filesize, &shstrtab, &shstrtabsize))
196                 return 1;
197
198         /* Find symbol table location and number of symbols. */
199         if (elf_getsymtab(p, shstrtab, shstrtabsize, &symtab, &nsymb))
200                 warnx("symbol table not found");
201
202         /* Find string table location and size. */
203         if (elf_getsection(p, ELF_STRTAB, shstrtab, shstrtabsize, &strtab, &strtabsize))
204                 warnx("string table not found");
205
206         /* Find CTF section and dump it. */
207         for (i = 0; i < eh->e_shnum; i++) {
208                 sh = (Elf_Shdr *)(p + eh->e_shoff + i * eh->e_shentsize);
209
210                 if ((sh->sh_link >= eh->e_shnum) ||
211                     (sh->sh_name >= shstrtabsize))
212                         continue;
213
214                 if (strncmp(shstrtab + sh->sh_name, ELF_CTF, strlen(ELF_CTF)))
215                         continue;
216
217                 if (!isctf(p + sh->sh_offset, sh->sh_size))
218                         break;
219
220                 return ctf_dump(p + sh->sh_offset, sh->sh_size, flags);
221         }
222
223         warnx("%s section not found", ELF_CTF);
224         return 1;
225 }
226
227 int
228 isctf(const char *p, size_t filesize)
229 {
230         struct ctf_header       *cth = (struct ctf_header *)p;
231         off_t                    dlen = cth->cth_stroff + cth->cth_strlen;
232
233         if (cth->cth_magic != CTF_MAGIC || cth->cth_version != CTF_VERSION)
234                 return 0;
235
236         if (dlen > filesize && !(cth->cth_flags & CTF_F_COMPRESS)) {
237                 warnx("bogus file size");
238                 return 0;
239         }
240
241         if ((cth->cth_lbloff & 3) || (cth->cth_objtoff & 1) ||
242             (cth->cth_funcoff & 1) || (cth->cth_typeoff & 3)) {
243                 warnx("wrongly aligned offset");
244                 return 0;
245         }
246
247         if ((cth->cth_lbloff >= dlen) || (cth->cth_objtoff >= dlen) ||
248             (cth->cth_funcoff >= dlen) || (cth->cth_typeoff >= dlen)) {
249                 warnx("truncated file");
250                 return 0;
251         }
252
253         if ((cth->cth_lbloff > cth->cth_objtoff) ||
254             (cth->cth_objtoff > cth->cth_funcoff) ||
255             (cth->cth_funcoff > cth->cth_typeoff) ||
256             (cth->cth_typeoff > cth->cth_stroff)) {
257                 warnx("corrupted file");
258                 return 0;
259         }
260
261         return 1;
262 }
263
264 int
265 ctf_dump(const char *p, size_t size, uint8_t flags)
266 {
267         struct ctf_header       *cth = (struct ctf_header *)p;
268         off_t                    dlen = cth->cth_stroff + cth->cth_strlen;
269         char                    *data;
270
271         if (cth->cth_flags & CTF_F_COMPRESS) {
272                 data = decompress(p + sizeof(*cth), size - sizeof(*cth), dlen);
273                 if (data == NULL)
274                         return 1;
275         } else {
276                 data = (char *)p + sizeof(*cth);
277         }
278
279         if (flags & DUMP_HEADER) {
280                 printf("  cth_magic    = 0x%04x\n", cth->cth_magic);
281                 printf("  cth_version  = %d\n", cth->cth_version);
282                 printf("  cth_flags    = 0x%02x\n", cth->cth_flags);
283                 printf("  cth_parlabel = %s\n",
284                     ctf_off2name(cth, data, dlen, cth->cth_parname));
285                 printf("  cth_parname  = %s\n",
286                     ctf_off2name(cth, data, dlen, cth->cth_parname));
287                 printf("  cth_lbloff   = %d\n", cth->cth_lbloff);
288                 printf("  cth_objtoff  = %d\n", cth->cth_objtoff);
289                 printf("  cth_funcoff  = %d\n", cth->cth_funcoff);
290                 printf("  cth_typeoff  = %d\n", cth->cth_typeoff);
291                 printf("  cth_stroff   = %d\n", cth->cth_stroff);
292                 printf("  cth_strlen   = %d\n", cth->cth_strlen);
293                 printf("\n");
294         }
295
296         if (flags & DUMP_LABEL) {
297                 unsigned int             lbloff = cth->cth_lbloff;
298                 struct ctf_lblent       *ctl;
299
300                 while (lbloff < cth->cth_objtoff) {
301                         ctl = (struct ctf_lblent *)(data + lbloff);
302
303                         printf("  %5u %s\n", ctl->ctl_typeidx,
304                             ctf_off2name(cth, data, dlen, ctl->ctl_label));
305
306                         lbloff += sizeof(*ctl);
307                 }
308                 printf("\n");
309         }
310
311         if (flags & DUMP_OBJECT) {
312                 unsigned int             objtoff = cth->cth_objtoff;
313                 size_t                   idx = 0, i = 0;
314                 unsigned short          *dsp;
315                 const char              *s;
316                 int                      l;
317
318                 while (objtoff < cth->cth_funcoff) {
319                         dsp = (unsigned short *)(data + objtoff);
320
321                         l = printf("  [%zu] %u", i++, *dsp);
322                         if ((s = elf_idx2sym(&idx, STT_OBJECT)) != NULL)
323                                 printf("%*s %s (%zu)\n", (14 - l), "", s, idx);
324                         else
325                                 printf("\n");
326
327                         objtoff += sizeof(*dsp);
328                 }
329                 printf("\n");
330         }
331
332         if (flags & DUMP_FUNCTION) {
333                 unsigned short          *fsp, kind, vlen;
334                 size_t                   idx = 0, i = -1;
335                 const char              *s;
336                 int                      l;
337
338                 fsp = (unsigned short *)(data + cth->cth_funcoff);
339                 while (fsp < (unsigned short *)(data + cth->cth_typeoff)) {
340                         kind = CTF_INFO_KIND(*fsp);
341                         vlen = CTF_INFO_VLEN(*fsp);
342                         s = elf_idx2sym(&idx, STT_FUNC);
343                         fsp++;
344                         i++;
345
346                         if (kind == CTF_K_UNKNOWN && vlen == 0)
347                                 continue;
348
349                         l = printf("  [%zu] FUNC ", i);
350                         if (s != NULL)
351                                 printf("(%s)", s);
352                         printf(" returns: %u args: (", *fsp++);
353                         while (vlen-- > 0)
354                                 printf("%u%s", *fsp++, (vlen > 0) ? ", " : "");
355                         printf(")\n");
356                 }
357                 printf("\n");
358         }
359
360         if (flags & DUMP_STRTAB) {
361                 unsigned int             offset = 0;
362                 const char              *str;
363
364                 while (offset < cth->cth_strlen) {
365                         str = ctf_off2name(cth, data, dlen, offset);
366
367                         printf("  [%u] ", offset);
368                         if (strcmp(str, "(anon)"))
369                                 offset += printf("%s\n", str);
370                         else {
371                                 printf("\\0\n");
372                                 offset++;
373                         }
374                 }
375                 printf("\n");
376         }
377
378         if (flags & DUMP_TYPE) {
379                 unsigned int             idx = 1, offset = cth->cth_typeoff;
380
381                 while (offset < cth->cth_stroff) {
382                         offset += ctf_dump_type(cth, data, dlen, offset, idx++);
383                 }
384                 printf("\n");
385         }
386
387         if (cth->cth_flags & CTF_F_COMPRESS)
388                 free(data);
389
390         return 0;
391 }
392
393 unsigned int
394 ctf_dump_type(struct ctf_header *cth, const char *data, off_t dlen,
395     unsigned int offset, unsigned int idx)
396 {
397         const char              *p = data + offset;
398         const struct ctf_type   *ctt = (struct ctf_type *)p;
399         unsigned short           i, kind, vlen, root;
400         unsigned int             eob, toff;
401         uint64_t                 size;
402         const char              *name, *kname;
403
404         kind = CTF_INFO_KIND(ctt->ctt_info);
405         vlen = CTF_INFO_VLEN(ctt->ctt_info);
406         root = CTF_INFO_ISROOT(ctt->ctt_info);
407         name = ctf_off2name(cth, data, dlen, ctt->ctt_name);
408
409         if (root)
410                 printf("  <%u> ", idx);
411         else
412                 printf("  [%u] ", idx);
413
414         if ((kname = ctf_kind2name(kind)) != NULL)
415                 printf("%s %s", kname, name);
416
417         if (ctt->ctt_size <= CTF_MAX_SIZE) {
418                 size = ctt->ctt_size;
419                 toff = sizeof(struct ctf_stype);
420         } else {
421                 size = CTF_TYPE_LSIZE(ctt);
422                 toff = sizeof(struct ctf_type);
423         }
424
425         switch (kind) {
426         case CTF_K_UNKNOWN:
427         case CTF_K_FORWARD:
428                 break;
429         case CTF_K_INTEGER:
430                 eob = *((unsigned int *)((char *)ctt + toff));
431                 toff += sizeof(unsigned int);
432                 printf(" encoding=0x%x offset=%u bits=%u",
433                     CTF_INT_ENCODING(eob), CTF_INT_OFFSET(eob),
434                     CTF_INT_BITS(eob));
435                 break;
436         case CTF_K_FLOAT:
437                 eob = *((unsigned int *)((char *)ctt + toff));
438                 toff += sizeof(unsigned int);
439                 printf(" encoding=0x%x offset=%u bits=%u",
440                     CTF_FP_ENCODING(eob), CTF_FP_OFFSET(eob), CTF_FP_BITS(eob));
441                 break;
442         case CTF_K_ARRAY:
443                 toff += sizeof(struct ctf_array);
444                 break;
445         case CTF_K_FUNCTION:
446                 toff += (vlen + (vlen & 1)) * sizeof(unsigned short);
447                 break;
448         case CTF_K_STRUCT:
449         case CTF_K_UNION:
450                 printf(" (%llu bytes)\n", size);
451
452                 if (size < CTF_LSTRUCT_THRESH) {
453                         for (i = 0; i < vlen; i++) {
454                                 struct ctf_member       *ctm;
455
456                                 ctm = (struct ctf_member *)(p + toff);
457                                 toff += sizeof(struct ctf_member);
458
459                                 printf("\t%s type=%u off=%u\n",
460                                     ctf_off2name(cth, data, dlen,
461                                         ctm->ctm_name),
462                                     ctm->ctm_type, ctm->ctm_offset);
463                         }
464                 } else {
465                         for (i = 0; i < vlen; i++) {
466                                 struct ctf_lmember      *ctlm;
467
468                                 ctlm = (struct ctf_lmember *)(p + toff);
469                                 toff += sizeof(struct ctf_lmember);
470
471                                 printf("\t%s type=%u off=%llu\n",
472                                     ctf_off2name(cth, data, dlen,
473                                         ctlm->ctlm_name),
474                                     ctlm->ctlm_type, CTF_LMEM_OFFSET(ctlm));
475                         }
476                 }
477                 break;
478         case CTF_K_ENUM:
479                 printf("\n");
480                 for (i = 0; i < vlen; i++) {
481                         struct ctf_enum *cte;
482
483                         cte = (struct ctf_enum *)(p + toff);
484                         toff += sizeof(struct ctf_enum);
485
486                         printf("\t%s = %u\n",
487                             ctf_off2name(cth, data, dlen, cte->cte_name),
488                             cte->cte_value);
489                 }
490                 break;
491         case CTF_K_POINTER:
492         case CTF_K_TYPEDEF:
493         case CTF_K_VOLATILE:
494         case CTF_K_CONST:
495         case CTF_K_RESTRICT:
496                 printf(" refers to %u", ctt->ctt_type);
497                 break;
498         default:
499                 errx(1, "incorrect type %u at offset %u", kind, offset);
500         }
501
502         printf("\n");
503
504         return toff;
505 }
506
507 const char *
508 ctf_kind2name(unsigned short kind)
509 {
510         static const char *kind_name[] = { NULL, "INTEGER", "FLOAT", "POINTER",
511            "ARRAYS", "FUNCTION", "STRUCT", "UNION", "ENUM", "FORWARD",
512            "TYPEDEF", "VOLATILE", "CONST", "RESTRICT" };
513
514         if (kind >= nitems(kind_name))
515                 return NULL;
516
517         return kind_name[kind];
518 }
519
520 const char *
521 ctf_off2name(struct ctf_header *cth, const char *data, off_t dlen,
522     unsigned int offset)
523 {
524         const char              *name;
525
526         if (CTF_NAME_STID(offset) != CTF_STRTAB_0)
527                 return "external";
528
529         if (CTF_NAME_OFFSET(offset) >= cth->cth_strlen)
530                 return "exceeds strlab";
531
532         if (cth->cth_stroff + CTF_NAME_OFFSET(offset) >= dlen)
533                 return "invalid";
534
535         name = data + cth->cth_stroff + CTF_NAME_OFFSET(offset);
536         if (*name == '\0')
537                 return "(anon)";
538
539         return name;
540 }
541
542 char *
543 decompress(const char *buf, size_t size, off_t len)
544 {
545 #ifdef ZLIB
546         z_stream                 stream;
547         char                    *data;
548         int                      error;
549
550         data = malloc(len);
551         if (data == NULL) {
552                 warn(NULL);
553                 return NULL;
554         }
555
556         memset(&stream, 0, sizeof(stream));
557         stream.next_in = (void *)buf;
558         stream.avail_in = size;
559         stream.next_out = data;
560         stream.avail_out = len;
561
562         if ((error = inflateInit(&stream)) != Z_OK) {
563                 warnx("zlib inflateInit failed: %s", zError(error));
564                 goto exit;
565         }
566
567         if ((error = inflate(&stream, Z_FINISH)) != Z_STREAM_END) {
568                 warnx("zlib inflate failed: %s", zError(error));
569                 inflateEnd(&stream);
570                 goto exit;
571         }
572
573         if ((error = inflateEnd(&stream)) != Z_OK) {
574                 warnx("zlib inflateEnd failed: %s", zError(error));
575                 goto exit;
576         }
577
578         if (stream.total_out != len) {
579                 warnx("decompression failed: %llu != %llu",
580                     stream.total_out, len);
581                 goto exit;
582         }
583
584         return data;
585
586 exit:
587         free(data);
588 #endif /* ZLIB */
589         return NULL;
590 }
591
592 __dead void
593 usage(void)
594 {
595         extern char             *__progname;
596
597         fprintf(stderr, "usage: %s [-dfhlst] [file ...]\n",
598             __progname);
599         exit(1);
600 }
601