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