]> sbz's 6dev Repos - ctfdump/.git/blame - ctfdump.c
Merge pull request #2 from jasperla/inflail
[ctfdump/.git] / ctfdump.c
CommitLineData
f8621c6a
MP
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
74b86e22
MP
38#ifndef nitems
39#define nitems(_a) (sizeof((_a)) / sizeof((_a)[0]))
40#endif
41
f8621c6a
MP
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)
751fc09a 47#define DUMP_LABEL (1 << 3)
80c28199 48#define DUMP_STRTAB (1 << 4)
74b86e22
MP
49#define DUMP_STATISTIC (1 << 5)
50#define DUMP_TYPE (1 << 6)
f8621c6a 51
c1bd258a 52int dump(const char *, uint8_t);
f8621c6a
MP
53int isctf(const char *, size_t);
54__dead void usage(void);
55
c1bd258a 56int ctf_dump(const char *, size_t, uint8_t);
74b86e22
MP
57unsigned int ctf_dump_type(struct ctf_header *, const char *, off_t,
58 unsigned int, unsigned int);
59const char *ctf_kind2name(unsigned short);
f8621c6a
MP
60const char *ctf_off2name(struct ctf_header *, const char *, off_t,
61 unsigned int);
62
c1bd258a 63int elf_dump(const char *, size_t, uint8_t);
c36bfc0c
MP
64const char *elf_idx2sym(size_t *, unsigned char);
65
66/* elf.c */
67int iself(const char *, size_t);
f8621c6a
MP
68int elf_getshstrtab(const char *, size_t, const char **, size_t *);
69int elf_getsymtab(const char *, const char *, size_t,
70 const Elf_Sym **, size_t *);
2ef38d60
MP
71int elf_getsection(const char *, const char *, const char *,
72 size_t, const char **, size_t *);
f8621c6a
MP
73
74#ifdef ZLIB
75char *decompress(const char *, size_t, off_t);
76#endif /* ZLIB */
77
78int
79main(int argc, char *argv[])
80{
81 const char *filename;
c1bd258a 82 uint8_t flags = 0;
f8621c6a
MP
83 int ch, error = 0;
84
85 setlocale(LC_ALL, "");
86
53477564 87 while ((ch = getopt(argc, argv, "dfhlst")) != -1) {
f8621c6a 88 switch (ch) {
9fe3b2c8
MP
89 case 'd':
90 flags |= DUMP_OBJECT;
91 break;
acada86d
MP
92 case 'f':
93 flags |= DUMP_FUNCTION;
94 break;
f8621c6a
MP
95 case 'h':
96 flags |= DUMP_HEADER;
97 break;
751fc09a
MP
98 case 'l':
99 flags |= DUMP_LABEL;
100 break;
80c28199
MP
101 case 's':
102 flags |= DUMP_STRTAB;
103 break;
74b86e22
MP
104 case 't':
105 flags |= DUMP_TYPE;
106 break;
f8621c6a
MP
107 default:
108 usage();
109 }
110 }
111
112 argc -= optind;
113 argv += optind;
114
c1bd258a
MP
115 /* Dump everything by default */
116 if (flags == 0)
117 flags = 0xff;
118
f8621c6a
MP
119 while ((filename = *argv++) != NULL)
120 error |= dump(filename, flags);
121
122 return error;
123}
124
125int
c1bd258a 126dump(const char *path, uint8_t flags)
f8621c6a
MP
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
f8621c6a
MP
166const char *strtab;
167const Elf_Sym *symtab;
168size_t strtabsize, nsymb;
169
9fe3b2c8
MP
170const char *
171elf_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
f8621c6a 189int
c1bd258a 190elf_dump(const char *p, size_t filesize, uint8_t flags)
f8621c6a
MP
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. */
2ef38d60 206 if (elf_getsection(p, ELF_STRTAB, shstrtab, shstrtabsize, &strtab, &strtabsize))
f8621c6a
MP
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
230int
231isctf(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
267int
c1bd258a 268ctf_dump(const char *p, size_t size, uint8_t flags)
f8621c6a
MP
269{
270 struct ctf_header *cth = (struct ctf_header *)p;
f8621c6a 271 off_t dlen = cth->cth_stroff + cth->cth_strlen;
b51bd0d8 272 char *data;
f8621c6a
MP
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;
b51bd0d8
MP
278 } else {
279 data = (char *)p + sizeof(*cth);
f8621c6a
MP
280 }
281
282 if (flags & DUMP_HEADER) {
6befd992
MP
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",
f8621c6a 287 ctf_off2name(cth, data, dlen, cth->cth_parname));
6befd992 288 printf(" cth_parname = %s\n",
f8621c6a 289 ctf_off2name(cth, data, dlen, cth->cth_parname));
6befd992
MP
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");
f8621c6a
MP
297 }
298
751fc09a
MP
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
6befd992 306 printf(" %5u %s\n", ctl->ctl_typeidx,
751fc09a
MP
307 ctf_off2name(cth, data, dlen, ctl->ctl_label));
308
309 lbloff += sizeof(*ctl);
310 }
6befd992 311 printf("\n");
751fc09a
MP
312 }
313
9fe3b2c8
MP
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
6befd992 324 l = printf(" [%zu] %u", i++, *dsp);
9fe3b2c8 325 if ((s = elf_idx2sym(&idx, STT_OBJECT)) != NULL)
86e1759d 326 printf("%*s %s (%zu)\n", (14 - l), "", s, idx);
9fe3b2c8
MP
327 else
328 printf("\n");
329
330 objtoff += sizeof(*dsp);
331 }
332 }
333
acada86d
MP
334 if (flags & DUMP_FUNCTION) {
335 unsigned short *fsp, kind, vlen;
ae3b8a21 336 size_t idx = 0, i = -1;
acada86d
MP
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);
ae3b8a21 344 s = elf_idx2sym(&idx, STT_FUNC);
acada86d 345 fsp++;
ae3b8a21 346 i++;
acada86d
MP
347
348 if (kind == CTF_K_UNKNOWN && vlen == 0)
349 continue;
350
ae3b8a21
MP
351 l = printf(" [%zu] FUNC ", i);
352 if (s != NULL)
86e1759d 353 printf("(%s)", s);
acada86d
MP
354 printf(" returns: %u args: (", *fsp++);
355 while (vlen-- > 0)
356 printf("%u%s", *fsp++, (vlen > 0) ? ", " : "");
357 printf(")\n");
358 }
6befd992 359 printf("\n");
acada86d
MP
360 }
361
80c28199
MP
362 if (flags & DUMP_STRTAB) {
363 unsigned int offset = 0;
364 const char *str;
365
366 while (offset < cth->cth_strlen) {
3093320c 367 str = ctf_off2name(cth, data, dlen, offset);
80c28199 368
6befd992 369 printf(" [%u] ", offset);
3093320c 370 if (strcmp(str, "(anon)"))
80c28199
MP
371 offset += printf("%s\n", str);
372 else {
373 printf("\\0\n");
374 offset++;
375 }
376 }
6befd992 377 printf("\n");
80c28199
MP
378 }
379
74b86e22
MP
380 if (flags & DUMP_TYPE) {
381 unsigned int idx = 1, offset = 0;
382
6befd992 383 while (offset < cth->cth_stroff) {
74b86e22 384 offset += ctf_dump_type(cth, data, dlen, offset, idx++);
6befd992
MP
385 }
386 printf("\n");
74b86e22
MP
387 }
388
f8621c6a
MP
389 if (cth->cth_flags & CTF_F_COMPRESS)
390 free(data);
391
392 return 0;
393}
394
74b86e22
MP
395unsigned int
396ctf_dump_type(struct ctf_header *cth, const char *data, off_t dlen,
397 unsigned int offset, unsigned int idx)
398{
23543ec6
MP
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;
74b86e22
MP
403 uint64_t size;
404 const char *name, *kname;
405
74b86e22
MP
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)
6befd992 412 printf(" <%u> ", idx);
74b86e22 413 else
6befd992 414 printf(" [%u] ", idx);
74b86e22
MP
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:
23543ec6
MP
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));
74b86e22
MP
437 break;
438 case CTF_K_FLOAT:
23543ec6
MP
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));
74b86e22
MP
443 break;
444 case CTF_K_ARRAY:
23543ec6 445 toff += sizeof(struct ctf_array);
74b86e22
MP
446 break;
447 case CTF_K_FUNCTION:
23543ec6 448 toff += (vlen + (vlen & 1)) * sizeof(unsigned short);
74b86e22
MP
449 break;
450 case CTF_K_STRUCT:
451 case CTF_K_UNION:
23543ec6
MP
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 }
74b86e22
MP
479 break;
480 case CTF_K_ENUM:
23543ec6
MP
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 }
74b86e22
MP
492 break;
493 case CTF_K_POINTER:
74b86e22
MP
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
23543ec6 506 return toff;
74b86e22
MP
507}
508
509const char *
510ctf_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
f8621c6a
MP
522const char *
523ctf_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
544char *
545decompress(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));
8aa77ab4 571 inflateEnd(&stream);
f8621c6a
MP
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
588exit:
589 free(data);
590#endif /* ZLIB */
591 return NULL;
592}
593
594__dead void
595usage(void)
596{
597 extern char *__progname;
598
53477564 599 fprintf(stderr, "usage: %s [-dfhlst] [file ...]\n",
f8621c6a
MP
600 __progname);
601 exit(1);
602}
603