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