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