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