2 * Copyright (c) 2016 Martin Pieuchot <mpi@openbsd.org>
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.
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.
17 #include <sys/param.h>
18 #include <sys/types.h>
20 #include <sys/exec_elf.h>
38 #define SUNW_CTF ".SUNW_ctf"
40 #define DUMP_OBJECT (1 << 0)
41 #define DUMP_FUNCTION (1 << 1)
42 #define DUMP_HEADER (1 << 2)
43 #define DUMP_LABEL (1 << 3)
45 int dump(const char *, uint32_t);
46 int iself(const char *, size_t);
47 int isctf(const char *, size_t);
48 __dead void usage(void);
50 int ctf_dump(const char *, size_t, uint32_t);
51 const char *ctf_off2name(struct ctf_header *, const char *, off_t,
54 int elf_dump(const char *, size_t, uint32_t);
55 int elf_getshstrtab(const char *, size_t, const char **, size_t *);
56 int elf_getsymtab(const char *, const char *, size_t,
57 const Elf_Sym **, size_t *);
58 int elf_getstrtab(const char *, const char *, size_t,
59 const char **, size_t *);
62 char *decompress(const char *, size_t, off_t);
66 main(int argc, char *argv[])
72 setlocale(LC_ALL, "");
74 while ((ch = getopt(argc, argv, "dfhlsSt")) != -1) {
80 flags |= DUMP_FUNCTION;
96 while ((filename = *argv++) != NULL)
97 error |= dump(filename, flags);
103 dump(const char *path, uint32_t flags)
109 fd = open(path, O_RDONLY);
114 if (fstat(fd, &st) == -1) {
118 if (st.st_size < (off_t)sizeof(struct ctf_header)) {
119 warnx("file too small to be CTF");
122 if ((uintmax_t)st.st_size > SIZE_MAX) {
123 warnx("file too big to fit memory");
127 p = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
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);
137 munmap(p, st.st_size);
144 iself(const char *p, size_t filesize)
146 Elf_Ehdr *eh = (Elf_Ehdr *)p;
148 if (eh->e_ehsize < sizeof(Elf_Ehdr) || !IS_ELF(*eh)) {
149 warnx("file is not ELF");
152 if (eh->e_ident[EI_CLASS] != ELFCLASS) {
153 warnx("unexpected word size %u", eh->e_ident[EI_CLASS]);
156 if (eh->e_ident[EI_VERSION] != ELF_TARG_VER) {
157 warnx("unexpected version %u", eh->e_ident[EI_VERSION]);
160 if (eh->e_ident[EI_DATA] >= ELFDATANUM) {
161 warnx("unexpected data format %u", eh->e_ident[EI_DATA]);
164 if (eh->e_shoff > filesize) {
165 warnx("bogus section table offset 0x%llx", eh->e_shoff);
168 if (eh->e_shentsize < sizeof(Elf_Shdr)) {
169 warnx("bogus section header size %u", eh->e_shentsize);
172 if (eh->e_shnum > (filesize - eh->e_shoff) / eh->e_shentsize) {
173 warnx("bogus section header count %u", eh->e_shnum);
176 if (eh->e_shstrndx >= eh->e_shnum) {
177 warnx("bogus string table index %u", eh->e_shstrndx);
185 elf_getshstrtab(const char *p, size_t filesize, const char **shstrtab,
186 size_t *shstrtabsize)
188 Elf_Ehdr *eh = (Elf_Ehdr *)p;
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");
196 if (sh->sh_offset > filesize) {
197 warnx("bogus string table offset");
200 if (sh->sh_size > filesize - sh->sh_offset) {
201 warnx("bogus string table size");
204 if (shstrtab != NULL)
205 *shstrtab = p + sh->sh_offset;
206 if (shstrtabsize != NULL)
207 *shstrtabsize = sh->sh_size;
213 elf_getsymtab(const char *p, const char *shstrtab, size_t shstrtabsize,
214 const Elf_Sym **symtab, size_t *nsymb)
216 Elf_Ehdr *eh = (Elf_Ehdr *)p;
220 for (i = 0; i < eh->e_shnum; i++) {
221 sh = (Elf_Shdr *)(p + eh->e_shoff + i * eh->e_shentsize);
223 if (sh->sh_type != SHT_SYMTAB)
226 if ((sh->sh_link >= eh->e_shnum) ||
227 (sh->sh_name >= shstrtabsize))
230 if (strncmp(shstrtab + sh->sh_name, ELF_SYMTAB,
231 strlen(ELF_SYMTAB)) == 0) {
233 *symtab = (Elf_Sym *)(p + sh->sh_offset);
235 *nsymb = (sh->sh_size / sh->sh_entsize);
245 elf_getstrtab(const char *p, const char *shstrtab, size_t shstrtabsize,
246 const char **strtab, size_t *strtabsize)
248 Elf_Ehdr *eh = (Elf_Ehdr *)p;
252 for (i = 0; i < eh->e_shnum; i++) {
253 sh = (Elf_Shdr *)(p + eh->e_shoff + i * eh->e_shentsize);
255 if (sh->sh_type != SHT_STRTAB)
258 if ((sh->sh_link >= eh->e_shnum) ||
259 (sh->sh_name >= shstrtabsize))
262 if (strncmp(shstrtab + sh->sh_name, ELF_STRTAB,
263 strlen(ELF_STRTAB)) == 0) {
265 *strtab = p + sh->sh_offset;
266 if (strtabsize != NULL)
267 *strtabsize = sh->sh_size;
277 const Elf_Sym *symtab;
278 size_t strtabsize, nsymb;
281 elf_idx2sym(size_t *idx, unsigned char type)
286 for (i = *idx + 1; i < nsymb; i++) {
289 if (ELF_ST_TYPE(st->st_info) != type)
293 return strtab + st->st_name;
300 elf_dump(const char *p, size_t filesize, uint32_t flags)
302 Elf_Ehdr *eh = (Elf_Ehdr *)p;
304 const char *shstrtab;
305 size_t i, shstrtabsize;
307 /* Find section header string table location and size. */
308 if (elf_getshstrtab(p, filesize, &shstrtab, &shstrtabsize))
311 /* Find symbol table location and number of symbols. */
312 if (elf_getsymtab(p, shstrtab, shstrtabsize, &symtab, &nsymb))
313 warnx("symbol table not found");
315 /* Find string table location and size. */
316 if (elf_getstrtab(p, shstrtab, shstrtabsize, &strtab, &strtabsize))
317 warnx("string table not found");
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);
323 if ((sh->sh_link >= eh->e_shnum) ||
324 (sh->sh_name >= shstrtabsize))
327 if (strncmp(shstrtab + sh->sh_name, SUNW_CTF, strlen(SUNW_CTF)))
330 if (!isctf(p + sh->sh_offset, sh->sh_size))
333 return ctf_dump(p + sh->sh_offset, sh->sh_size, flags);
336 warnx("%s section not found", SUNW_CTF);
341 isctf(const char *p, size_t filesize)
343 struct ctf_header *cth = (struct ctf_header *)p;
344 off_t dlen = cth->cth_stroff + cth->cth_strlen;
346 if (cth->cth_magic != CTF_MAGIC || cth->cth_version != CTF_VERSION)
349 if (dlen > filesize && !(cth->cth_flags & CTF_F_COMPRESS)) {
350 warnx("bogus file size");
354 if ((cth->cth_lbloff & 3) || (cth->cth_objtoff & 1) ||
355 (cth->cth_funcoff & 1) || (cth->cth_typeoff & 3)) {
356 warnx("wrongly aligned offset");
360 if ((cth->cth_lbloff >= dlen) || (cth->cth_objtoff >= dlen) ||
361 (cth->cth_funcoff >= dlen) || (cth->cth_typeoff >= dlen)) {
362 warnx("truncated file");
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");
378 ctf_dump(const char *p, size_t size, uint32_t flags)
380 struct ctf_header *cth = (struct ctf_header *)p;
381 char *data = (char *)p;
382 off_t dlen = cth->cth_stroff + cth->cth_strlen;
384 if (cth->cth_flags & CTF_F_COMPRESS) {
385 data = decompress(p + sizeof(*cth), size - sizeof(*cth), dlen);
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);
406 if (flags & DUMP_LABEL) {
407 unsigned int lbloff = cth->cth_lbloff;
408 struct ctf_lblent *ctl;
410 while (lbloff < cth->cth_objtoff) {
411 ctl = (struct ctf_lblent *)(data + lbloff);
413 printf("%5u %s\n", ctl->ctl_typeidx,
414 ctf_off2name(cth, data, dlen, ctl->ctl_label));
416 lbloff += sizeof(*ctl);
420 if (flags & DUMP_OBJECT) {
421 unsigned int objtoff = cth->cth_objtoff;
422 size_t idx = 0, i = 0;
427 while (objtoff < cth->cth_funcoff) {
428 dsp = (unsigned short *)(data + objtoff);
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);
436 objtoff += sizeof(*dsp);
440 if (flags & DUMP_FUNCTION) {
441 unsigned short *fsp, kind, vlen;
442 size_t idx = 0, i = 0;
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);
452 if (kind == CTF_K_UNKNOWN && vlen == 0)
455 l = printf("%u [%zu] FUNC", vlen, i++);
456 if ((s = elf_idx2sym(&idx, STT_FUNC)) != NULL)
458 printf(" returns: %u args: (", *fsp++);
460 printf("%u%s", *fsp++, (vlen > 0) ? ", " : "");
465 if (cth->cth_flags & CTF_F_COMPRESS)
472 ctf_off2name(struct ctf_header *cth, const char *data, off_t dlen,
477 if (CTF_NAME_STID(offset) != CTF_STRTAB_0)
480 if (CTF_NAME_OFFSET(offset) >= cth->cth_strlen)
481 return "exceeds strlab";
483 if (cth->cth_stroff + CTF_NAME_OFFSET(offset) >= dlen)
486 name = data + cth->cth_stroff + CTF_NAME_OFFSET(offset);
494 decompress(const char *buf, size_t size, off_t len)
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;
513 if ((error = inflateInit(&stream)) != Z_OK) {
514 warnx("zlib inflateInit failed: %s", zError(error));
518 if ((error = inflate(&stream, Z_FINISH)) != Z_STREAM_END) {
519 warnx("zlib inflate failed: %s", zError(error));
523 if ((error = inflateEnd(&stream)) != Z_OK) {
524 warnx("zlib inflateEnd failed: %s", zError(error));
528 if (stream.total_out != len) {
529 warnx("decompression failed: %llu != %llu",
530 stream.total_out, len);
545 extern char *__progname;
547 fprintf(stderr, "usage: %s [-dfhlsSt] [file ...]\n",