]> sbz's 6dev Repos - ctfdump/.git/blob - ctfdump.c
7ccf9bf9cdbf897ed38866d3c88b39cb3f18b317
[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 #define SUNW_CTF        ".SUNW_ctf"
39
40 #define DUMP_OBJECT     (1 << 0)
41 #define DUMP_FUNCTION   (1 << 1)
42 #define DUMP_HEADER     (1 << 2)
43 #define DUMP_LABEL      (1 << 3)
44
45 int              dump(const char *, uint32_t);
46 int              iself(const char *, size_t);
47 int              isctf(const char *, size_t);
48 __dead void      usage(void);
49
50 int              ctf_dump(const char *, size_t, uint32_t);
51 const char      *ctf_off2name(struct ctf_header *, const char *, off_t,
52                      unsigned int);
53
54 int              elf_dump(const char *, size_t, uint32_t);
55 int              elf_getshstrtab(const char *, size_t, const char **, size_t *);
56 int              elf_getsymtab(const char *, const char *, size_t,
57                      const Elf_Sym **, size_t *);
58 int              elf_getstrtab(const char *, const char *, size_t,
59                      const char **, size_t *);
60
61 #ifdef ZLIB
62 char            *decompress(const char *, size_t, off_t);
63 #endif /* ZLIB */
64
65 int
66 main(int argc, char *argv[])
67 {
68         const char *filename;
69         uint32_t flags = 0;
70         int ch, error = 0;
71
72         setlocale(LC_ALL, "");
73
74         while ((ch = getopt(argc, argv, "dfhlsSt")) != -1) {
75                 switch (ch) {
76                 case 'd':
77                         flags |= DUMP_OBJECT;
78                         break;
79                 case 'h':
80                         flags |= DUMP_HEADER;
81                         break;
82                 case 'l':
83                         flags |= DUMP_LABEL;
84                         break;
85                 default:
86                         usage();
87                 }
88         }
89
90         argc -= optind;
91         argv += optind;
92
93         while ((filename = *argv++) != NULL)
94                 error |= dump(filename, flags);
95
96         return error;
97 }
98
99 int
100 dump(const char *path, uint32_t flags)
101 {
102         struct stat              st;
103         int                      fd, error = 1;
104         char                    *p;
105
106         fd = open(path, O_RDONLY);
107         if (fd == -1) {
108                 warn("open");
109                 return 1;
110         }
111         if (fstat(fd, &st) == -1) {
112                 warn("fstat");
113                 return 1;
114         }
115         if (st.st_size < (off_t)sizeof(struct ctf_header)) {
116                 warnx("file too small to be CTF");
117                 return 1;
118         }
119         if ((uintmax_t)st.st_size > SIZE_MAX) {
120                 warnx("file too big to fit memory");
121                 return 1;
122         }
123
124         p = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
125         if (p == MAP_FAILED)
126                 err(1, "mmap");
127
128         if (iself(p, st.st_size)) {
129                 error = elf_dump(p, st.st_size, flags);
130         } else if (isctf(p, st.st_size)) {
131                 error = ctf_dump(p, st.st_size, flags);
132         }
133
134         munmap(p, st.st_size);
135         close(fd);
136
137         return error;
138 }
139
140 int
141 iself(const char *p, size_t filesize)
142 {
143         Elf_Ehdr                *eh = (Elf_Ehdr *)p;
144
145         if (eh->e_ehsize < sizeof(Elf_Ehdr) || !IS_ELF(*eh)) {
146                 warnx("file is not ELF");
147                 return 0;
148         }
149         if (eh->e_ident[EI_CLASS] != ELFCLASS) {
150                 warnx("unexpected word size %u", eh->e_ident[EI_CLASS]);
151                 return 0;
152         }
153         if (eh->e_ident[EI_VERSION] != ELF_TARG_VER) {
154                 warnx("unexpected version %u", eh->e_ident[EI_VERSION]);
155                 return 0;
156         }
157         if (eh->e_ident[EI_DATA] >= ELFDATANUM) {
158                 warnx("unexpected data format %u", eh->e_ident[EI_DATA]);
159                 return 0;
160         }
161         if (eh->e_shoff > filesize) {
162                 warnx("bogus section table offset 0x%llx", eh->e_shoff);
163                 return 0;
164         }
165         if (eh->e_shentsize < sizeof(Elf_Shdr)) {
166                 warnx("bogus section header size %u", eh->e_shentsize);
167                 return 0;
168         }
169         if (eh->e_shnum > (filesize - eh->e_shoff) / eh->e_shentsize) {
170                 warnx("bogus section header count %u", eh->e_shnum);
171                 return 0;
172         }
173         if (eh->e_shstrndx >= eh->e_shnum) {
174                 warnx("bogus string table index %u", eh->e_shstrndx);
175                 return 0;
176         }
177
178         return 1;
179 }
180
181 int
182 elf_getshstrtab(const char *p, size_t filesize, const char **shstrtab,
183     size_t *shstrtabsize)
184 {
185         Elf_Ehdr                *eh = (Elf_Ehdr *)p;
186         Elf_Shdr                *sh;
187
188         sh = (Elf_Shdr *)(p + eh->e_shoff + eh->e_shstrndx * eh->e_shentsize);
189         if (sh->sh_type != SHT_STRTAB) {
190                 warnx("unexpected string table type");
191                 return 1;
192         }
193         if (sh->sh_offset > filesize) {
194                 warnx("bogus string table offset");
195                 return 1;
196         }
197         if (sh->sh_size > filesize - sh->sh_offset) {
198                 warnx("bogus string table size");
199                 return 1;
200         }
201         if (shstrtab != NULL)
202                 *shstrtab = p + sh->sh_offset;
203         if (shstrtabsize != NULL)
204                 *shstrtabsize = sh->sh_size;
205
206         return 0;
207 }
208
209 int
210 elf_getsymtab(const char *p, const char *shstrtab, size_t shstrtabsize,
211     const Elf_Sym **symtab, size_t *nsymb)
212 {
213         Elf_Ehdr        *eh = (Elf_Ehdr *)p;
214         Elf_Shdr        *sh;
215         size_t           i;
216
217         for (i = 0; i < eh->e_shnum; i++) {
218                 sh = (Elf_Shdr *)(p + eh->e_shoff + i * eh->e_shentsize);
219
220                 if (sh->sh_type != SHT_SYMTAB)
221                         continue;
222
223                 if ((sh->sh_link >= eh->e_shnum) ||
224                     (sh->sh_name >= shstrtabsize))
225                         continue;
226
227                 if (strncmp(shstrtab + sh->sh_name, ELF_SYMTAB,
228                     strlen(ELF_SYMTAB)) == 0) {
229                         if (symtab != NULL)
230                                 *symtab = (Elf_Sym *)(p + sh->sh_offset);
231                         if (nsymb != NULL)
232                                 *nsymb = (sh->sh_size / sh->sh_entsize);
233
234                         return 0;
235                 }
236         }
237
238         return 1;
239 }
240
241 int
242 elf_getstrtab(const char *p, const char *shstrtab, size_t shstrtabsize,
243     const char **strtab, size_t *strtabsize)
244 {
245         Elf_Ehdr        *eh = (Elf_Ehdr *)p;
246         Elf_Shdr        *sh;
247         size_t           i;
248
249         for (i = 0; i < eh->e_shnum; i++) {
250                 sh = (Elf_Shdr *)(p + eh->e_shoff + i * eh->e_shentsize);
251
252                 if (sh->sh_type != SHT_STRTAB)
253                         continue;
254
255                 if ((sh->sh_link >= eh->e_shnum) ||
256                     (sh->sh_name >= shstrtabsize))
257                         continue;
258
259                 if (strncmp(shstrtab + sh->sh_name, ELF_STRTAB,
260                     strlen(ELF_STRTAB)) == 0) {
261                         if (strtab != NULL)
262                                 *strtab = p + sh->sh_offset;
263                         if (strtabsize != NULL)
264                                 *strtabsize = sh->sh_size;
265
266                         return 0;
267                 }
268         }
269
270         return 1;
271 }
272
273 const char              *strtab;
274 const Elf_Sym           *symtab;
275 size_t                   strtabsize, nsymb;
276
277 const char *
278 elf_idx2sym(size_t *idx, unsigned char type)
279 {
280         const Elf_Sym   *st;
281         size_t           i;
282
283         for (i = *idx + 1; i < nsymb; i++) {
284                 st = &symtab[i];
285
286                 if (ELF_ST_TYPE(st->st_info) != type)
287                         continue;
288
289                 *idx = i;
290                 return strtab + st->st_name;
291         }
292
293         return NULL;
294 }
295
296 int
297 elf_dump(const char *p, size_t filesize, uint32_t flags)
298 {
299         Elf_Ehdr                *eh = (Elf_Ehdr *)p;
300         Elf_Shdr                *sh;
301         const char              *shstrtab;
302         size_t                   i, shstrtabsize;
303
304         /* Find section header string table location and size. */
305         if (elf_getshstrtab(p, filesize, &shstrtab, &shstrtabsize))
306                 return 1;
307
308         /* Find symbol table location and number of symbols. */
309         if (elf_getsymtab(p, shstrtab, shstrtabsize, &symtab, &nsymb))
310                 warnx("symbol table not found");
311
312         /* Find string table location and size. */
313         if (elf_getstrtab(p, shstrtab, shstrtabsize, &strtab, &strtabsize))
314                 warnx("string table not found");
315
316         /* Find CTF section and dump it. */
317         for (i = 0; i < eh->e_shnum; i++) {
318                 sh = (Elf_Shdr *)(p + eh->e_shoff + i * eh->e_shentsize);
319
320                 if ((sh->sh_link >= eh->e_shnum) ||
321                     (sh->sh_name >= shstrtabsize))
322                         continue;
323
324                 if (strncmp(shstrtab + sh->sh_name, SUNW_CTF, strlen(SUNW_CTF)))
325                         continue;
326
327                 if (!isctf(p + sh->sh_offset, sh->sh_size))
328                         break;
329
330                 return ctf_dump(p + sh->sh_offset, sh->sh_size, flags);
331         }
332
333         warnx("%s section not found", SUNW_CTF);
334         return 1;
335 }
336
337 int
338 isctf(const char *p, size_t filesize)
339 {
340         struct ctf_header       *cth = (struct ctf_header *)p;
341         off_t                    dlen = cth->cth_stroff + cth->cth_strlen;
342
343         if (cth->cth_magic != CTF_MAGIC || cth->cth_version != CTF_VERSION)
344                 return 0;
345
346         if (dlen > filesize && !(cth->cth_flags & CTF_F_COMPRESS)) {
347                 warnx("bogus file size");
348                 return 0;
349         }
350
351         if ((cth->cth_lbloff & 3) || (cth->cth_objtoff & 1) ||
352             (cth->cth_funcoff & 1) || (cth->cth_typeoff & 3)) {
353                 warnx("wrongly aligned offset");
354                 return 0;
355         }
356
357         if ((cth->cth_lbloff >= dlen) || (cth->cth_objtoff >= dlen) ||
358             (cth->cth_funcoff >= dlen) || (cth->cth_typeoff >= dlen)) {
359                 warnx("truncated file");
360                 return 0;
361         }
362
363         if ((cth->cth_lbloff > cth->cth_objtoff) ||
364             (cth->cth_objtoff > cth->cth_funcoff) ||
365             (cth->cth_funcoff > cth->cth_typeoff) ||
366             (cth->cth_typeoff > cth->cth_stroff)) {
367                 warnx("corrupted file");
368                 return 0;
369         }
370
371         return 1;
372 }
373
374 int
375 ctf_dump(const char *p, size_t size, uint32_t flags)
376 {
377         struct ctf_header       *cth = (struct ctf_header *)p;
378         char                    *data = (char *)p;
379         off_t                    dlen = cth->cth_stroff + cth->cth_strlen;
380
381         if (cth->cth_flags & CTF_F_COMPRESS) {
382                 data = decompress(p + sizeof(*cth), size - sizeof(*cth), dlen);
383                 if (data == NULL)
384                         return 1;
385         }
386
387         if (flags & DUMP_HEADER) {
388                 printf("cth_magic    = 0x%04x\n", cth->cth_magic);
389                 printf("cth_version  = %d\n", cth->cth_version);
390                 printf("cth_flags    = 0x%02x\n", cth->cth_flags);
391                 printf("cth_parlabel = %s\n",
392                     ctf_off2name(cth, data, dlen, cth->cth_parname));
393                 printf("cth_parname  = %s\n",
394                     ctf_off2name(cth, data, dlen, cth->cth_parname));
395                 printf("cth_lbloff   = %d\n", cth->cth_lbloff);
396                 printf("cth_objtoff  = %d\n", cth->cth_objtoff);
397                 printf("cth_funcoff  = %d\n", cth->cth_funcoff);
398                 printf("cth_typeoff  = %d\n", cth->cth_typeoff);
399                 printf("cth_stroff   = %d\n", cth->cth_stroff);
400                 printf("cth_strlen   = %d\n", cth->cth_strlen);
401         }
402
403         if (flags & DUMP_LABEL) {
404                 unsigned int             lbloff = cth->cth_lbloff;
405                 struct ctf_lblent       *ctl;
406
407                 while (lbloff < cth->cth_objtoff) {
408                         ctl = (struct ctf_lblent *)(data + lbloff);
409
410                         printf("%5u %s\n", ctl->ctl_typeidx,
411                             ctf_off2name(cth, data, dlen, ctl->ctl_label));
412
413                         lbloff += sizeof(*ctl);
414                 }
415         }
416
417         if (flags & DUMP_OBJECT) {
418                 unsigned int             objtoff = cth->cth_objtoff;
419                 size_t                   idx = 0, i = 0;
420                 unsigned short          *dsp;
421                 const char              *s;
422                 int                      l;
423
424                 while (objtoff < cth->cth_funcoff) {
425                         dsp = (unsigned short *)(data + objtoff);
426
427                         l = printf("[%zu] %u", i++, *dsp);
428                         if ((s = elf_idx2sym(&idx, STT_OBJECT)) != NULL)
429                                 printf("%*s %s (%zu)\n", (12 - l), "", s, idx);
430                         else
431                                 printf("\n");
432
433                         objtoff += sizeof(*dsp);
434                 }
435         }
436
437         if (cth->cth_flags & CTF_F_COMPRESS)
438                 free(data);
439
440         return 0;
441 }
442
443 const char *
444 ctf_off2name(struct ctf_header *cth, const char *data, off_t dlen,
445     unsigned int offset)
446 {
447         const char              *name;
448
449         if (CTF_NAME_STID(offset) != CTF_STRTAB_0)
450                 return "external";
451
452         if (CTF_NAME_OFFSET(offset) >= cth->cth_strlen)
453                 return "exceeds strlab";
454
455         if (cth->cth_stroff + CTF_NAME_OFFSET(offset) >= dlen)
456                 return "invalid";
457
458         name = data + cth->cth_stroff + CTF_NAME_OFFSET(offset);
459         if (*name == '\0')
460                 return "(anon)";
461
462         return name;
463 }
464
465 char *
466 decompress(const char *buf, size_t size, off_t len)
467 {
468 #ifdef ZLIB
469         z_stream                 stream;
470         char                    *data;
471         int                      error;
472
473         data = malloc(len);
474         if (data == NULL) {
475                 warn(NULL);
476                 return NULL;
477         }
478
479         memset(&stream, 0, sizeof(stream));
480         stream.next_in = (void *)buf;
481         stream.avail_in = size;
482         stream.next_out = data;
483         stream.avail_out = len;
484
485         if ((error = inflateInit(&stream)) != Z_OK) {
486                 warnx("zlib inflateInit failed: %s", zError(error));
487                 goto exit;
488         }
489
490         if ((error = inflate(&stream, Z_FINISH)) != Z_STREAM_END) {
491                 warnx("zlib inflate failed: %s", zError(error));
492                 goto exit;
493         }
494
495         if ((error = inflateEnd(&stream)) != Z_OK) {
496                 warnx("zlib inflateEnd failed: %s", zError(error));
497                 goto exit;
498         }
499
500         if (stream.total_out != len) {
501                 warnx("decompression failed: %llu != %llu",
502                     stream.total_out, len);
503                 goto exit;
504         }
505
506         return data;
507
508 exit:
509         free(data);
510 #endif /* ZLIB */
511         return NULL;
512 }
513
514 __dead void
515 usage(void)
516 {
517         extern char             *__progname;
518
519         fprintf(stderr, "usage: %s [-dfhlsSt] [file ...]\n",
520             __progname);
521         exit(1);
522 }
523