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