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