From 23f8800a2f78fc13c41a1387c1c351c5743a4197 Mon Sep 17 00:00:00 2001 From: Sofian Brabez Date: Thu, 10 Nov 2016 20:57:16 +0100 Subject: [PATCH] FreeBSD support, reduce output difference, start adding statistics (-S) and tests Lazy for splitting all the commits -- sbz --- compat.h | 21 ++++++ ctfdump.1 | 11 ++- ctfdump.c | 223 +++++++++++++++++++++++++++++++++++++++++++++++-------- elf.c | 6 +- tests/Makefile | 28 +++++++ tests/header.in | 14 ++++ tests/header.out | 14 ++++ tests/label.in | 4 + tests/label.out | 4 + tests/stats.in | 48 ++++++++++++ tests/stats.out | 23 ++++++ tests/usage.in | 1 + tests/usage.out | 1 + 13 files changed, 359 insertions(+), 39 deletions(-) create mode 100644 compat.h create mode 100644 tests/Makefile create mode 100644 tests/header.in create mode 100644 tests/header.out create mode 100644 tests/label.in create mode 100644 tests/label.out create mode 100644 tests/stats.in create mode 100644 tests/stats.out create mode 100644 tests/usage.in create mode 100644 tests/usage.out diff --git a/compat.h b/compat.h new file mode 100644 index 0000000..64f4f7c --- /dev/null +++ b/compat.h @@ -0,0 +1,21 @@ +#ifndef COMPAT_H +#define COMPAT_H + +#if defined(__FreeBSD__) +#include + +#define __dead __dead2 + +#define ELF_STRTAB ".strtab" +#define ELF_SYMTAB ".symtab" +#define ELF_CTF ".SUNW_ctf" +#define ELFCLASS ELF_CLASS +#define ELFDATANUM 3 + +#else +#include +#endif + +#include + +#endif diff --git a/ctfdump.1 b/ctfdump.1 index ec15a08..f4759ab 100644 --- a/ctfdump.1 +++ b/ctfdump.1 @@ -21,15 +21,15 @@ .Nd display CTF information .Sh SYNOPSIS .Nm ctfdump -.Op Fl dfhlst +.Op Fl dfhlSst +file... .Sh DESCRIPTION The .Nm utility display CTF information from the .Dv \.SUNW_ctf -section of an -.Xr elf 5 -file or from a raw CTF file. +section of an ELF binary +or from a raw CTF binary file. .Pp The options are as follows: .Bl -tag -width Ds @@ -43,10 +43,13 @@ Dump the CTF header. Display the label section. .It Fl s Display the string table. +.It Fl S +Display statistics. .It Fl t Display the type section. .El .Sh EXIT STATUS .Ex -std ctfdump .Sh SEE ALSO +.Xr ctf 5 .Xr elf 5 diff --git a/ctfdump.c b/ctfdump.c index b661d02..1b90928 100644 --- a/ctfdump.c +++ b/ctfdump.c @@ -14,17 +14,17 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include +#include #include -#include #include -#include -#include +#include #include #include #include -#include #include +#include #include #include #include @@ -34,10 +34,7 @@ #endif /* ZLIB */ #include "ctf.h" - -#ifndef nitems -#define nitems(_a) (sizeof((_a)) / sizeof((_a)[0])) -#endif +#include "compat.h" #define DUMP_OBJECT (1 << 0) #define DUMP_FUNCTION (1 << 1) @@ -47,6 +44,20 @@ #define DUMP_STATISTIC (1 << 5) #define DUMP_TYPE (1 << 6) +static struct { + unsigned long total_objs; + unsigned long total_funcs; + unsigned long total_func_args; + unsigned long max_args; + unsigned long total_args; + unsigned long total_types; + unsigned long types[16]; + unsigned int total_members; + unsigned int total_all_struct; + size_t total_strings; + size_t max_string; +} stats; + int dump(const char *, uint8_t); int isctf(const char *, size_t); __dead void usage(void); @@ -54,6 +65,7 @@ __dead void usage(void); int ctf_dump(const char *, size_t, uint8_t); unsigned int ctf_dump_type(struct ctf_header *, const char *, off_t, unsigned int, unsigned int); +void ctf_dump_stats(void); const char *ctf_kind2name(unsigned short); const char *ctf_off2name(struct ctf_header *, const char *, off_t, unsigned int); @@ -73,6 +85,8 @@ int elf_getsection(const char *, const char *, const char *, char *decompress(const char *, size_t, off_t); #endif /* ZLIB */ +void print_line(const char *); + int main(int argc, char *argv[]) { @@ -82,7 +96,11 @@ main(int argc, char *argv[]) setlocale(LC_ALL, ""); - while ((ch = getopt(argc, argv, "dfhlst")) != -1) { + if (argc == 1) { + usage(); + } + + while ((ch = getopt(argc, argv, "dfhlsSt")) != -1) { switch (ch) { case 'd': flags |= DUMP_OBJECT; @@ -99,6 +117,9 @@ main(int argc, char *argv[]) case 's': flags |= DUMP_STRTAB; break; + case 'S': + flags |= DUMP_STATISTIC; + break; case 't': flags |= DUMP_TYPE; break; @@ -278,11 +299,13 @@ ctf_dump(const char *p, size_t size, uint8_t flags) } if (flags & DUMP_HEADER) { + print_line("- CTF Header "); + printf(" cth_magic = 0x%04x\n", cth->cth_magic); printf(" cth_version = %d\n", cth->cth_version); printf(" cth_flags = 0x%02x\n", cth->cth_flags); printf(" cth_parlabel = %s\n", - ctf_off2name(cth, data, dlen, cth->cth_parname)); + ctf_off2name(cth, data, dlen, cth->cth_parlabel)); printf(" cth_parname = %s\n", ctf_off2name(cth, data, dlen, cth->cth_parname)); printf(" cth_lbloff = %d\n", cth->cth_lbloff); @@ -291,13 +314,14 @@ ctf_dump(const char *p, size_t size, uint8_t flags) printf(" cth_typeoff = %d\n", cth->cth_typeoff); printf(" cth_stroff = %d\n", cth->cth_stroff); printf(" cth_strlen = %d\n", cth->cth_strlen); - printf("\n"); } if (flags & DUMP_LABEL) { unsigned int lbloff = cth->cth_lbloff; struct ctf_lblent *ctl; + print_line("- Label Table "); + while (lbloff < cth->cth_objtoff) { ctl = (struct ctf_lblent *)(data + lbloff); @@ -306,16 +330,17 @@ ctf_dump(const char *p, size_t size, uint8_t flags) lbloff += sizeof(*ctl); } - printf("\n"); } - if (flags & DUMP_OBJECT) { + if (flags & (DUMP_OBJECT | DUMP_STATISTIC)) { unsigned int objtoff = cth->cth_objtoff; size_t idx = 0, i = 0; unsigned short *dsp; const char *s; int l; + print_line("- Data Objects "); + while (objtoff < cth->cth_funcoff) { dsp = (unsigned short *)(data + objtoff); @@ -326,15 +351,19 @@ ctf_dump(const char *p, size_t size, uint8_t flags) printf("\n"); objtoff += sizeof(*dsp); + + stats.total_objs++; } } - if (flags & DUMP_FUNCTION) { + if (flags & (DUMP_FUNCTION | DUMP_STATISTIC)) { unsigned short *fsp, kind, vlen; size_t idx = 0, i = -1; const char *s; int l; + print_line("- Functions "); + fsp = (unsigned short *)(data + cth->cth_funcoff); while (fsp < (unsigned short *)(data + cth->cth_typeoff)) { kind = CTF_INFO_KIND(*fsp); @@ -350,17 +379,33 @@ ctf_dump(const char *p, size_t size, uint8_t flags) if (s != NULL) printf("(%s)", s); printf(" returns: %u args: (", *fsp++); - while (vlen-- > 0) + while (vlen-- > 0) { printf("%u%s", *fsp++, (vlen > 0) ? ", " : ""); + stats.total_func_args++; + } printf(")\n"); + + stats.total_funcs++; } - printf("\n"); } - if (flags & DUMP_STRTAB) { + if (flags & (DUMP_TYPE | DUMP_STATISTIC)) { + unsigned int idx = 1, offset = 0; + + print_line("- Types "); + + while (offset < cth->cth_stroff) { + offset += ctf_dump_type(cth, data, dlen, offset, idx++); + } + + } + + if (flags & (DUMP_STRTAB | DUMP_STATISTIC)) { unsigned int offset = 0; const char *str; + print_line("- String Table "); + while (offset < cth->cth_strlen) { str = ctf_off2name(cth, data, dlen, offset); @@ -372,16 +417,13 @@ ctf_dump(const char *p, size_t size, uint8_t flags) offset++; } } - printf("\n"); } - if (flags & DUMP_TYPE) { - unsigned int idx = 1, offset = 0; + if (flags & DUMP_STATISTIC) { - while (offset < cth->cth_stroff) { - offset += ctf_dump_type(cth, data, dlen, offset, idx++); - } - printf("\n"); + print_line(" - CTF Statistics "); + + ctf_dump_stats(); } if (cth->cth_flags & CTF_F_COMPRESS) @@ -447,7 +489,7 @@ ctf_dump_type(struct ctf_header *cth, const char *data, off_t dlen, break; case CTF_K_STRUCT: case CTF_K_UNION: - printf(" (%llu bytes)\n", size); + printf(" (%"PRIu64" bytes)\n", size); if (size < CTF_LSTRUCT_THRESH) { for (i = 0; i < vlen; i++) { @@ -468,7 +510,7 @@ ctf_dump_type(struct ctf_header *cth, const char *data, off_t dlen, ctlm = (struct ctf_lmember *)(p + toff); toff += sizeof(struct ctf_lmember); - printf("\t%s type=%u off=%llu\n", + printf("\t%s type=%u off=%"PRIu64"\n", ctf_off2name(cth, data, dlen, ctlm->ctlm_name), ctlm->ctlm_type, CTF_LMEM_OFFSET(ctlm)); @@ -501,9 +543,121 @@ ctf_dump_type(struct ctf_header *cth, const char *data, off_t dlen, printf("\n"); + stats.total_types++; + stats.types[kind]++; + return toff; } +void +ctf_dump_stats(void) { + + int i; + const char *fmt = " %-36s= %"PRId64"\n"; + struct { + const char *format; + const char *desc; + unsigned long count; + } dump_types[18] = { + { + .format = fmt, + .desc = "total number of functions", + .count = stats.total_funcs + }, + { + .format = fmt, + .desc = "total number of function arguments", + .count = stats.total_func_args + }, + { + .format = fmt, + .desc = "maximum argument list length", + .count = stats.max_args + }, + { + .format = fmt, + .desc = "total number of types", + .count = stats.total_types + }, + { + .format = fmt, + .desc = "total number of integers", + .count = stats.types[CTF_K_INTEGER] + }, + { + .format = fmt, + .desc = "total number of floats", + .count = stats.types[CTF_K_FLOAT] + }, + { + .format = fmt, + .desc = "total number of pointers", + .count = stats.types[CTF_K_POINTER] + }, + { + .format = fmt, + .desc = "total number of arrays", + .count = stats.types[CTF_K_ARRAY] + }, + { + .format = fmt, + .desc = "total number of func types", + .count = stats.types[CTF_K_FUNCTION] + }, + { + .format = fmt, + .desc = "total number of structs", + .count = stats.types[CTF_K_STRUCT] + }, + { + .format = fmt, + .desc = "total number of unions", + .count = stats.types[CTF_K_UNION] + }, + { + .format = fmt, + .desc = "total number of enums", + .count = stats.types[CTF_K_ENUM] + }, + { + .format = fmt, + .desc = "total number of forward tags", + .count = stats.types[CTF_K_FORWARD] + }, + { + .format = fmt, + .desc = "total number of typedefs", + .count = stats.types[CTF_K_TYPEDEF] + }, + { + .format = fmt, + .desc = "total number of volatile types", + .count = stats.types[CTF_K_VOLATILE] + }, + { + .format = fmt, + .desc = "total number of const types", + .count = stats.types[CTF_K_CONST] + }, + { + .format = fmt, + .desc = "total number of restrict types", + .count = stats.types[CTF_K_RESTRICT] + }, + { + .format = fmt, + .desc = "total number of unknowns (holes)", + .count = stats.types[CTF_K_UNKNOWN] + } + }; + + printf(fmt, "total number of data objects", stats.total_objs); + printf("\n"); + + for(i=0; i < nitems(dump_types); i++) + printf(dump_types[i].format, dump_types[i].desc, dump_types[i].count); +} + const char * ctf_kind2name(unsigned short kind) { @@ -556,7 +710,7 @@ decompress(const char *buf, size_t size, off_t len) memset(&stream, 0, sizeof(stream)); stream.next_in = (void *)buf; stream.avail_in = size; - stream.next_out = data; + stream.next_out = (unsigned char *)data; stream.avail_out = len; if ((error = inflateInit(&stream)) != Z_OK) { @@ -576,7 +730,7 @@ decompress(const char *buf, size_t size, off_t len) } if (stream.total_out != len) { - warnx("decompression failed: %llu != %llu", + warnx("decompression failed: %"PRIu64" != %"PRIu64" ", stream.total_out, len); goto exit; } @@ -592,10 +746,15 @@ exit: __dead void usage(void) { - extern char *__progname; - - fprintf(stderr, "usage: %s [-dfhlst] [file ...]\n", - __progname); + fprintf(stderr, "usage: %s [-dfhlsSt] file...\n", getprogname()); exit(1); } +void +print_line(const char *str) +{ + static int nchar = 78; + static const char line[] = "----------------------------------------" + "----------------------------------------"; + printf("\n%s%.*s\n\n", str, (int)(nchar - strlen(str)), line); +} diff --git a/elf.c b/elf.c index 7f2da18..b4ca9f9 100644 --- a/elf.c +++ b/elf.c @@ -15,11 +15,11 @@ */ #include -#include - #include #include +#include "compat.h" + int iself(const char *p, size_t filesize) { @@ -41,7 +41,7 @@ iself(const char *p, size_t filesize) return 0; } if (eh->e_shoff > filesize) { - warnx("bogus section table offset 0x%llx", (off_t)eh->e_shoff); + warnx("bogus section table offset 0x%"PRIx64"", (off_t)eh->e_shoff); return 0; } if (eh->e_shentsize < sizeof(Elf_Shdr)) { diff --git a/tests/Makefile b/tests/Makefile new file mode 100644 index 0000000..8a0d6fa --- /dev/null +++ b/tests/Makefile @@ -0,0 +1,28 @@ +TEST_TARGETS=test-header test-label test-stats test-usage +MAKEOBJDIR= # hack for warning message, see /usr/src/share/mk/bsd.obj.mk + +all: test-summary + +test-header: + # Generated .out using ctfdump -h > header.out. Later can be piped directly + @cat header.in | cmp -s header.out /dev/stdin || echo "[-] Test header failed" + +test-label: + # Generated .out using ctfdump -l > label.out + @cat label.in | cmp -s label.out /dev/stdin || echo "[-] Test label failed" \ + && diff -urN label.in label.out + +test-stats: + # Generated .out using ctfdump -S > stats.out + @cat stats.in | cmp -s stats.out /dev/stdin || echo "[-] Test stats failed" \ + && diff -urN stats.in stats.out + +test-usage: + # Generated .out using ctfdump -h > usage.out + @cat usage.in | cmp -s usage.out /dev/stdin || echo "[-] Test usage failed" \ + && diff -urN usage.in usage.out + +test-summary: ${TEST_TARGETS} + @echo "[+] All tests succeeded" + +.include diff --git a/tests/header.in b/tests/header.in new file mode 100644 index 0000000..8d03ba9 --- /dev/null +++ b/tests/header.in @@ -0,0 +1,14 @@ + +- CTF Header ----------------------------------------------------------------- + + cth_magic = 0xcff1 + cth_version = 2 + cth_flags = 0x00 + cth_parlabel = (anon) + cth_parname = (anon) + cth_lbloff = 0 + cth_objtoff = 8 + cth_funcoff = 24 + cth_typeoff = 128 + cth_stroff = 1780 + cth_strlen = 1753 diff --git a/tests/header.out b/tests/header.out new file mode 100644 index 0000000..8d03ba9 --- /dev/null +++ b/tests/header.out @@ -0,0 +1,14 @@ + +- CTF Header ----------------------------------------------------------------- + + cth_magic = 0xcff1 + cth_version = 2 + cth_flags = 0x00 + cth_parlabel = (anon) + cth_parname = (anon) + cth_lbloff = 0 + cth_objtoff = 8 + cth_funcoff = 24 + cth_typeoff = 128 + cth_stroff = 1780 + cth_strlen = 1753 diff --git a/tests/label.in b/tests/label.in new file mode 100644 index 0000000..088ee04 --- /dev/null +++ b/tests/label.in @@ -0,0 +1,4 @@ + +- Label Table ---------------------------------------------------------------- + + 100 sbz diff --git a/tests/label.out b/tests/label.out new file mode 100644 index 0000000..248684a --- /dev/null +++ b/tests/label.out @@ -0,0 +1,4 @@ + +- Label Table ---------------------------------------------------------------- + + 100 sbz FreeBSD kakato 12.0-CURRENT FreeBSD 12.0-CURRENT #0 r308317M: Mon Nov 7 21:10:39 CET 2016 sbz@kakato:/usr/obj/usr/src/sys/GENERIC-DEBUG amd64 with CTF diff --git a/tests/stats.in b/tests/stats.in new file mode 100644 index 0000000..521508f --- /dev/null +++ b/tests/stats.in @@ -0,0 +1,48 @@ + +- CTF Statistics ------------------------------------------------------------- + + total number of data objects = 8 + + total number of functions = 12 + total number of function arguments = 28 + maximum argument list length = 5 + average argument list length = 2.33 + + total number of types = 100 + total number of integers = 8 + total number of floats = 0 + total number of pointers = 11 + total number of arrays = 2 + total number of func types = 2 + total number of structs = 15 + total number of unions = 1 + total number of enums = 0 + total number of forward tags = 0 + total number of typedefs = 54 + total number of volatile types = 0 + total number of const types = 2 + total number of restrict types = 0 + total number of unknowns (holes) = 5 + + total number of struct members = 97 + maximum number of struct members = 17 + total size of all structs = 672 + maximum size of a struct = 168 + average number of struct members = 6.47 + average size of a struct = 44.80 + + total number of union members = 2 + maximum number of union members = 2 + total size of all unions = 2 + maximum size of a union = 2 + average number of union members = 2.00 + average size of a union = 2.00 + + total number of enum members = 0 + maximum number of enum members = 0 + + total number of unique strings = 173 + bytes of string data = 1753 + maximum string length = 170 + average string length = 10.13 + diff --git a/tests/stats.out b/tests/stats.out new file mode 100644 index 0000000..84f4d6f --- /dev/null +++ b/tests/stats.out @@ -0,0 +1,23 @@ + + - CTF Statistics ------------------------------------------------------------ + + total number of data objects = 8 + + total number of functions = 12 + total number of function arguments = 28 + maximum argument list length = 0 + total number of types = 105 + total number of integers = 8 + total number of floats = 0 + total number of pointers = 11 + total number of arrays = 2 + total number of func types = 2 + total number of structs = 16 + total number of unions = 1 + total number of enums = 0 + total number of forward tags = 0 + total number of typedefs = 54 + total number of volatile types = 0 + total number of const types = 3 + total number of restrict types = 3 + total number of unknowns (holes) = 5 diff --git a/tests/usage.in b/tests/usage.in new file mode 100644 index 0000000..6591ee3 --- /dev/null +++ b/tests/usage.in @@ -0,0 +1 @@ +Usage: ctfdump [-dfhlsSt] [-u file] file diff --git a/tests/usage.out b/tests/usage.out new file mode 100644 index 0000000..dc3b55f --- /dev/null +++ b/tests/usage.out @@ -0,0 +1 @@ +usage: ctfdump [-dfhlsSt] file... -- 2.10.1