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