275928fc14
OpenSolaris. This commit resets files to match the versions in the OpenSolaris tree as of 2008/04/10. The changes in this import from the previous import are the ones that will subsequently re-applied to take files off the vendor branch. This is unfortunately necessary because the Solaris developers won't allow FreeBSD support #ifdefs in their source code because that creates 'dead code' (stuff that they never compile).
1029 lines
27 KiB
C
1029 lines
27 KiB
C
/*
|
|
* CDDL HEADER START
|
|
*
|
|
* The contents of this file are subject to the terms of the
|
|
* Common Development and Distribution License, Version 1.0 only
|
|
* (the "License"). You may not use this file except in compliance
|
|
* with the License.
|
|
*
|
|
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
|
* or http://www.opensolaris.org/os/licensing.
|
|
* See the License for the specific language governing permissions
|
|
* and limitations under the License.
|
|
*
|
|
* When distributing Covered Code, include this CDDL HEADER in each
|
|
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
|
|
* If applicable, add the following below this CDDL HEADER, with the
|
|
* fields enclosed by brackets "[]" replaced with your own identifying
|
|
* information: Portions Copyright [yyyy] [name of copyright owner]
|
|
*
|
|
* CDDL HEADER END
|
|
*/
|
|
/*
|
|
* Copyright 2004 Sun Microsystems, Inc. All rights reserved.
|
|
* Use is subject to license terms.
|
|
*/
|
|
|
|
#pragma ident "%Z%%M% %I% %E% SMI"
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/sysmacros.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/mman.h>
|
|
|
|
#include <strings.h>
|
|
#include <unistd.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <fcntl.h>
|
|
#include <gelf.h>
|
|
#include <zlib.h>
|
|
|
|
#include "ctf_headers.h"
|
|
#include "utils.h"
|
|
#include "symbol.h"
|
|
|
|
#define WARN(x) { warn(x); return (E_ERROR); }
|
|
|
|
/*
|
|
* Flags that indicate what data is to be displayed. An explicit `all' value is
|
|
* provided to allow the code to distinguish between a request for everything
|
|
* (currently requested by invoking ctfdump without flags) and individual
|
|
* requests for all of the types of data (an invocation with all flags). In the
|
|
* former case, we want to be able to implicitly adjust the definition of `all'
|
|
* based on the CTF version of the file being dumped. For example, if a v2 file
|
|
* is being dumped, `all' includes F_LABEL - a request to dump the label
|
|
* section. If a v1 file is being dumped, `all' does not include F_LABEL,
|
|
* because v1 CTF doesn't support labels. We need to be able to distinguish
|
|
* between `ctfdump foo', which has an implicit request for labels if `foo'
|
|
* supports them, and `ctfdump -l foo', which has an explicity request. In the
|
|
* latter case, we exit with an error if `foo' is a v1 CTF file.
|
|
*/
|
|
static enum {
|
|
F_DATA = 0x01, /* show data object section */
|
|
F_FUNC = 0x02, /* show function section */
|
|
F_HDR = 0x04, /* show header */
|
|
F_STR = 0x08, /* show string table */
|
|
F_TYPES = 0x10, /* show type section */
|
|
F_STATS = 0x20, /* show statistics */
|
|
F_LABEL = 0x40, /* show label section */
|
|
F_ALL = 0x80, /* explicit request for `all' */
|
|
F_ALLMSK = 0xff /* show all sections and statistics */
|
|
} flags = 0;
|
|
|
|
static struct {
|
|
ulong_t s_ndata; /* total number of data objects */
|
|
ulong_t s_nfunc; /* total number of functions */
|
|
ulong_t s_nargs; /* total number of function arguments */
|
|
ulong_t s_argmax; /* longest argument list */
|
|
ulong_t s_ntypes; /* total number of types */
|
|
ulong_t s_types[16]; /* number of types by kind */
|
|
ulong_t s_nsmem; /* total number of struct members */
|
|
ulong_t s_nsbytes; /* total size of all structs */
|
|
ulong_t s_smmax; /* largest struct in terms of members */
|
|
ulong_t s_sbmax; /* largest struct in terms of bytes */
|
|
ulong_t s_numem; /* total number of union members */
|
|
ulong_t s_nubytes; /* total size of all unions */
|
|
ulong_t s_ummax; /* largest union in terms of members */
|
|
ulong_t s_ubmax; /* largest union in terms of bytes */
|
|
ulong_t s_nemem; /* total number of enum members */
|
|
ulong_t s_emmax; /* largest enum in terms of members */
|
|
ulong_t s_nstr; /* total number of strings */
|
|
size_t s_strlen; /* total length of all strings */
|
|
size_t s_strmax; /* longest string length */
|
|
} stats;
|
|
|
|
typedef struct ctf_data {
|
|
caddr_t cd_ctfdata; /* Pointer to the CTF data */
|
|
size_t cd_ctflen; /* Length of CTF data */
|
|
|
|
/*
|
|
* cd_symdata will be non-NULL if the CTF data is being retrieved from
|
|
* an ELF file with a symbol table. cd_strdata and cd_nsyms should be
|
|
* used only if cd_symdata is non-NULL.
|
|
*/
|
|
Elf_Data *cd_symdata; /* Symbol table */
|
|
Elf_Data *cd_strdata; /* Symbol table strings */
|
|
int cd_nsyms; /* Number of symbol table entries */
|
|
} ctf_data_t;
|
|
|
|
static const char *
|
|
ref_to_str(uint_t name, const ctf_header_t *hp, const ctf_data_t *cd)
|
|
{
|
|
size_t offset = CTF_NAME_OFFSET(name);
|
|
const char *s = cd->cd_ctfdata + hp->cth_stroff + offset;
|
|
|
|
if (CTF_NAME_STID(name) != CTF_STRTAB_0)
|
|
return ("<< ??? - name in external strtab >>");
|
|
|
|
if (offset >= hp->cth_strlen)
|
|
return ("<< ??? - name exceeds strlab len >>");
|
|
|
|
if (hp->cth_stroff + offset >= cd->cd_ctflen)
|
|
return ("<< ??? - file truncated >>");
|
|
|
|
if (s[0] == '\0')
|
|
return ("(anon)");
|
|
|
|
return (s);
|
|
}
|
|
|
|
static const char *
|
|
int_encoding_to_str(uint_t encoding)
|
|
{
|
|
static char buf[32];
|
|
|
|
if (encoding == 0 || (encoding & ~(CTF_INT_SIGNED | CTF_INT_CHAR |
|
|
CTF_INT_BOOL | CTF_INT_VARARGS)) != 0)
|
|
(void) snprintf(buf, sizeof (buf), " 0x%x", encoding);
|
|
else {
|
|
buf[0] = '\0';
|
|
if (encoding & CTF_INT_SIGNED)
|
|
(void) strcat(buf, " SIGNED");
|
|
if (encoding & CTF_INT_CHAR)
|
|
(void) strcat(buf, " CHAR");
|
|
if (encoding & CTF_INT_BOOL)
|
|
(void) strcat(buf, " BOOL");
|
|
if (encoding & CTF_INT_VARARGS)
|
|
(void) strcat(buf, " VARARGS");
|
|
}
|
|
|
|
return (buf + 1);
|
|
}
|
|
|
|
static const char *
|
|
fp_encoding_to_str(uint_t encoding)
|
|
{
|
|
static const char *const encs[] = {
|
|
NULL, "SINGLE", "DOUBLE", "COMPLEX", "DCOMPLEX", "LDCOMPLEX",
|
|
"LDOUBLE", "INTERVAL", "DINTERVAL", "LDINTERVAL", "IMAGINARY",
|
|
"DIMAGINARY", "LDIMAGINARY"
|
|
};
|
|
|
|
static char buf[16];
|
|
|
|
if (encoding < 1 || encoding >= (sizeof (encs) / sizeof (char *))) {
|
|
(void) snprintf(buf, sizeof (buf), "%u", encoding);
|
|
return (buf);
|
|
}
|
|
|
|
return (encs[encoding]);
|
|
}
|
|
|
|
static void
|
|
print_line(const char *s)
|
|
{
|
|
static const char line[] = "----------------------------------------"
|
|
"----------------------------------------";
|
|
(void) printf("\n%s%.*s\n\n", s, (int)(78 - strlen(s)), line);
|
|
}
|
|
|
|
static int
|
|
print_header(const ctf_header_t *hp, const ctf_data_t *cd)
|
|
{
|
|
print_line("- CTF Header ");
|
|
|
|
(void) printf(" cth_magic = 0x%04x\n", hp->cth_magic);
|
|
(void) printf(" cth_version = %u\n", hp->cth_version);
|
|
(void) printf(" cth_flags = 0x%02x\n", hp->cth_flags);
|
|
(void) printf(" cth_parlabel = %s\n",
|
|
ref_to_str(hp->cth_parlabel, hp, cd));
|
|
(void) printf(" cth_parname = %s\n",
|
|
ref_to_str(hp->cth_parname, hp, cd));
|
|
(void) printf(" cth_lbloff = %u\n", hp->cth_lbloff);
|
|
(void) printf(" cth_objtoff = %u\n", hp->cth_objtoff);
|
|
(void) printf(" cth_funcoff = %u\n", hp->cth_funcoff);
|
|
(void) printf(" cth_typeoff = %u\n", hp->cth_typeoff);
|
|
(void) printf(" cth_stroff = %u\n", hp->cth_stroff);
|
|
(void) printf(" cth_strlen = %u\n", hp->cth_strlen);
|
|
|
|
return (E_SUCCESS);
|
|
}
|
|
|
|
static int
|
|
print_labeltable(const ctf_header_t *hp, const ctf_data_t *cd)
|
|
{
|
|
/* LINTED - pointer alignment */
|
|
const ctf_lblent_t *ctl = (ctf_lblent_t *)(cd->cd_ctfdata +
|
|
hp->cth_lbloff);
|
|
ulong_t i, n = (hp->cth_objtoff - hp->cth_lbloff) / sizeof (*ctl);
|
|
|
|
print_line("- Label Table ");
|
|
|
|
if (hp->cth_lbloff & 3)
|
|
WARN("cth_lbloff is not aligned properly\n");
|
|
if (hp->cth_lbloff >= cd->cd_ctflen)
|
|
WARN("file is truncated or cth_lbloff is corrupt\n");
|
|
if (hp->cth_objtoff >= cd->cd_ctflen)
|
|
WARN("file is truncated or cth_objtoff is corrupt\n");
|
|
if (hp->cth_lbloff > hp->cth_objtoff)
|
|
WARN("file is corrupt -- cth_lbloff > cth_objtoff\n");
|
|
|
|
for (i = 0; i < n; i++, ctl++) {
|
|
(void) printf(" %5u %s\n", ctl->ctl_typeidx,
|
|
ref_to_str(ctl->ctl_label, hp, cd));
|
|
}
|
|
|
|
return (E_SUCCESS);
|
|
}
|
|
|
|
/*
|
|
* Given the current symbol index (-1 to start at the beginning of the symbol
|
|
* table) and the type of symbol to match, this function returns the index of
|
|
* the next matching symbol (if any), and places the name of that symbol in
|
|
* *namep. If no symbol is found, -1 is returned.
|
|
*/
|
|
static int
|
|
next_sym(const ctf_data_t *cd, const int symidx, const uchar_t matchtype,
|
|
char **namep)
|
|
{
|
|
int i;
|
|
|
|
for (i = symidx + 1; i < cd->cd_nsyms; i++) {
|
|
GElf_Sym sym;
|
|
char *name;
|
|
int type;
|
|
|
|
if (gelf_getsym(cd->cd_symdata, i, &sym) == 0)
|
|
return (-1);
|
|
|
|
name = (char *)cd->cd_strdata->d_buf + sym.st_name;
|
|
type = GELF_ST_TYPE(sym.st_info);
|
|
|
|
/*
|
|
* Skip various types of symbol table entries.
|
|
*/
|
|
if (type != matchtype || ignore_symbol(&sym, name))
|
|
continue;
|
|
|
|
/* Found one */
|
|
*namep = name;
|
|
return (i);
|
|
}
|
|
|
|
return (-1);
|
|
}
|
|
|
|
static int
|
|
read_data(const ctf_header_t *hp, const ctf_data_t *cd)
|
|
{
|
|
/* LINTED - pointer alignment */
|
|
const ushort_t *idp = (ushort_t *)(cd->cd_ctfdata + hp->cth_objtoff);
|
|
ulong_t n = (hp->cth_funcoff - hp->cth_objtoff) / sizeof (ushort_t);
|
|
|
|
if (flags != F_STATS)
|
|
print_line("- Data Objects ");
|
|
|
|
if (hp->cth_objtoff & 1)
|
|
WARN("cth_objtoff is not aligned properly\n");
|
|
if (hp->cth_objtoff >= cd->cd_ctflen)
|
|
WARN("file is truncated or cth_objtoff is corrupt\n");
|
|
if (hp->cth_funcoff >= cd->cd_ctflen)
|
|
WARN("file is truncated or cth_funcoff is corrupt\n");
|
|
if (hp->cth_objtoff > hp->cth_funcoff)
|
|
WARN("file is corrupt -- cth_objtoff > cth_funcoff\n");
|
|
|
|
if (flags != F_STATS) {
|
|
int symidx, len, i;
|
|
char *name = NULL;
|
|
|
|
for (symidx = -1, i = 0; i < n; i++) {
|
|
int nextsym;
|
|
|
|
if (cd->cd_symdata == NULL || (nextsym = next_sym(cd,
|
|
symidx, STT_OBJECT, &name)) < 0)
|
|
name = NULL;
|
|
else
|
|
symidx = nextsym;
|
|
|
|
len = printf(" [%u] %u", i, *idp++);
|
|
if (name != NULL)
|
|
(void) printf("%*s%s (%u)", (15 - len), "",
|
|
name, symidx);
|
|
(void) putchar('\n');
|
|
}
|
|
}
|
|
|
|
stats.s_ndata = n;
|
|
return (E_SUCCESS);
|
|
}
|
|
|
|
static int
|
|
read_funcs(const ctf_header_t *hp, const ctf_data_t *cd)
|
|
{
|
|
/* LINTED - pointer alignment */
|
|
const ushort_t *fp = (ushort_t *)(cd->cd_ctfdata + hp->cth_funcoff);
|
|
|
|
/* LINTED - pointer alignment */
|
|
const ushort_t *end = (ushort_t *)(cd->cd_ctfdata + hp->cth_typeoff);
|
|
|
|
ulong_t id;
|
|
int symidx;
|
|
|
|
if (flags != F_STATS)
|
|
print_line("- Functions ");
|
|
|
|
if (hp->cth_funcoff & 1)
|
|
WARN("cth_funcoff is not aligned properly\n");
|
|
if (hp->cth_funcoff >= cd->cd_ctflen)
|
|
WARN("file is truncated or cth_funcoff is corrupt\n");
|
|
if (hp->cth_typeoff >= cd->cd_ctflen)
|
|
WARN("file is truncated or cth_typeoff is corrupt\n");
|
|
if (hp->cth_funcoff > hp->cth_typeoff)
|
|
WARN("file is corrupt -- cth_funcoff > cth_typeoff\n");
|
|
|
|
for (symidx = -1, id = 0; fp < end; id++) {
|
|
ushort_t info = *fp++;
|
|
ushort_t kind = CTF_INFO_KIND(info);
|
|
ushort_t n = CTF_INFO_VLEN(info);
|
|
ushort_t i;
|
|
int nextsym;
|
|
char *name;
|
|
|
|
if (cd->cd_symdata == NULL || (nextsym = next_sym(cd, symidx,
|
|
STT_FUNC, &name)) < 0)
|
|
name = NULL;
|
|
else
|
|
symidx = nextsym;
|
|
|
|
if (kind == CTF_K_UNKNOWN && n == 0)
|
|
continue; /* skip padding */
|
|
|
|
if (kind != CTF_K_FUNCTION) {
|
|
(void) printf(" [%lu] unexpected kind -- %u\n",
|
|
id, kind);
|
|
return (E_ERROR);
|
|
}
|
|
|
|
if (fp + n > end) {
|
|
(void) printf(" [%lu] vlen %u extends past section "
|
|
"boundary\n", id, n);
|
|
return (E_ERROR);
|
|
}
|
|
|
|
if (flags != F_STATS) {
|
|
(void) printf(" [%lu] FUNC ", id);
|
|
if (name != NULL)
|
|
(void) printf("(%s) ", name);
|
|
(void) printf("returns: %u args: (", *fp++);
|
|
|
|
if (n != 0) {
|
|
(void) printf("%u", *fp++);
|
|
for (i = 1; i < n; i++)
|
|
(void) printf(", %u", *fp++);
|
|
}
|
|
|
|
(void) printf(")\n");
|
|
} else
|
|
fp += n + 1; /* skip to next function definition */
|
|
|
|
stats.s_nfunc++;
|
|
stats.s_nargs += n;
|
|
stats.s_argmax = MAX(stats.s_argmax, n);
|
|
}
|
|
|
|
return (E_SUCCESS);
|
|
}
|
|
|
|
static int
|
|
read_types(const ctf_header_t *hp, const ctf_data_t *cd)
|
|
{
|
|
/* LINTED - pointer alignment */
|
|
const ctf_type_t *tp = (ctf_type_t *)(cd->cd_ctfdata + hp->cth_typeoff);
|
|
|
|
/* LINTED - pointer alignment */
|
|
const ctf_type_t *end = (ctf_type_t *)(cd->cd_ctfdata + hp->cth_stroff);
|
|
|
|
ulong_t id;
|
|
|
|
if (flags != F_STATS)
|
|
print_line("- Types ");
|
|
|
|
if (hp->cth_typeoff & 3)
|
|
WARN("cth_typeoff is not aligned properly\n");
|
|
if (hp->cth_typeoff >= cd->cd_ctflen)
|
|
WARN("file is truncated or cth_typeoff is corrupt\n");
|
|
if (hp->cth_stroff >= cd->cd_ctflen)
|
|
WARN("file is truncated or cth_stroff is corrupt\n");
|
|
if (hp->cth_typeoff > hp->cth_stroff)
|
|
WARN("file is corrupt -- cth_typeoff > cth_stroff\n");
|
|
|
|
id = 1;
|
|
if (hp->cth_parlabel || hp->cth_parname)
|
|
id += 1 << CTF_PARENT_SHIFT;
|
|
|
|
for (/* */; tp < end; id++) {
|
|
ulong_t i, n = CTF_INFO_VLEN(tp->ctt_info);
|
|
size_t size, increment, vlen = 0;
|
|
int kind = CTF_INFO_KIND(tp->ctt_info);
|
|
|
|
union {
|
|
const void *ptr;
|
|
const ctf_array_t *ap;
|
|
const ctf_member_t *mp;
|
|
const ctf_lmember_t *lmp;
|
|
const ctf_enum_t *ep;
|
|
const ushort_t *argp;
|
|
} u;
|
|
|
|
if (flags != F_STATS) {
|
|
(void) printf(" %c%lu%c ",
|
|
"[<"[CTF_INFO_ISROOT(tp->ctt_info)], id,
|
|
"]>"[CTF_INFO_ISROOT(tp->ctt_info)]);
|
|
}
|
|
|
|
if (tp->ctt_size == CTF_LSIZE_SENT) {
|
|
increment = sizeof (ctf_type_t);
|
|
size = (size_t)CTF_TYPE_LSIZE(tp);
|
|
} else {
|
|
increment = sizeof (ctf_stype_t);
|
|
size = tp->ctt_size;
|
|
}
|
|
u.ptr = (caddr_t)tp + increment;
|
|
|
|
switch (kind) {
|
|
case CTF_K_INTEGER:
|
|
if (flags != F_STATS) {
|
|
uint_t encoding = *((const uint_t *)u.ptr);
|
|
|
|
(void) printf("INTEGER %s encoding=%s offset=%u"
|
|
" bits=%u", ref_to_str(tp->ctt_name, hp,
|
|
cd), int_encoding_to_str(
|
|
CTF_INT_ENCODING(encoding)),
|
|
CTF_INT_OFFSET(encoding),
|
|
CTF_INT_BITS(encoding));
|
|
}
|
|
vlen = sizeof (uint_t);
|
|
break;
|
|
|
|
case CTF_K_FLOAT:
|
|
if (flags != F_STATS) {
|
|
uint_t encoding = *((const uint_t *)u.ptr);
|
|
|
|
(void) printf("FLOAT %s encoding=%s offset=%u "
|
|
"bits=%u", ref_to_str(tp->ctt_name, hp,
|
|
cd), fp_encoding_to_str(
|
|
CTF_FP_ENCODING(encoding)),
|
|
CTF_FP_OFFSET(encoding),
|
|
CTF_FP_BITS(encoding));
|
|
}
|
|
vlen = sizeof (uint_t);
|
|
break;
|
|
|
|
case CTF_K_POINTER:
|
|
if (flags != F_STATS) {
|
|
(void) printf("POINTER %s refers to %u",
|
|
ref_to_str(tp->ctt_name, hp, cd),
|
|
tp->ctt_type);
|
|
}
|
|
break;
|
|
|
|
case CTF_K_ARRAY:
|
|
if (flags != F_STATS) {
|
|
(void) printf("ARRAY %s content: %u index: %u "
|
|
"nelems: %u\n", ref_to_str(tp->ctt_name,
|
|
hp, cd), u.ap->cta_contents,
|
|
u.ap->cta_index, u.ap->cta_nelems);
|
|
}
|
|
vlen = sizeof (ctf_array_t);
|
|
break;
|
|
|
|
case CTF_K_FUNCTION:
|
|
if (flags != F_STATS) {
|
|
(void) printf("FUNCTION %s returns: %u args: (",
|
|
ref_to_str(tp->ctt_name, hp, cd),
|
|
tp->ctt_type);
|
|
|
|
if (n != 0) {
|
|
(void) printf("%u", *u.argp++);
|
|
for (i = 1; i < n; i++, u.argp++)
|
|
(void) printf(", %u", *u.argp);
|
|
}
|
|
|
|
(void) printf(")");
|
|
}
|
|
|
|
vlen = sizeof (ushort_t) * (n + (n & 1));
|
|
break;
|
|
|
|
case CTF_K_STRUCT:
|
|
case CTF_K_UNION:
|
|
if (kind == CTF_K_STRUCT) {
|
|
stats.s_nsmem += n;
|
|
stats.s_smmax = MAX(stats.s_smmax, n);
|
|
stats.s_nsbytes += size;
|
|
stats.s_sbmax = MAX(stats.s_sbmax, size);
|
|
|
|
if (flags != F_STATS)
|
|
(void) printf("STRUCT");
|
|
} else {
|
|
stats.s_numem += n;
|
|
stats.s_ummax = MAX(stats.s_ummax, n);
|
|
stats.s_nubytes += size;
|
|
stats.s_ubmax = MAX(stats.s_ubmax, size);
|
|
|
|
if (flags != F_STATS)
|
|
(void) printf("UNION");
|
|
}
|
|
|
|
if (flags != F_STATS) {
|
|
(void) printf(" %s (%d bytes)\n",
|
|
ref_to_str(tp->ctt_name, hp, cd), size);
|
|
|
|
if (size >= CTF_LSTRUCT_THRESH) {
|
|
for (i = 0; i < n; i++, u.lmp++) {
|
|
(void) printf(
|
|
"\t%s type=%u off=%llu\n",
|
|
ref_to_str(u.lmp->ctlm_name,
|
|
hp, cd), u.lmp->ctlm_type,
|
|
CTF_LMEM_OFFSET(u.lmp));
|
|
}
|
|
} else {
|
|
for (i = 0; i < n; i++, u.mp++) {
|
|
(void) printf(
|
|
"\t%s type=%u off=%u\n",
|
|
ref_to_str(u.mp->ctm_name,
|
|
hp, cd), u.mp->ctm_type,
|
|
u.mp->ctm_offset);
|
|
}
|
|
}
|
|
}
|
|
|
|
vlen = n * (size >= CTF_LSTRUCT_THRESH ?
|
|
sizeof (ctf_lmember_t) : sizeof (ctf_member_t));
|
|
break;
|
|
|
|
case CTF_K_ENUM:
|
|
if (flags != F_STATS) {
|
|
(void) printf("ENUM %s\n",
|
|
ref_to_str(tp->ctt_name, hp, cd));
|
|
|
|
for (i = 0; i < n; i++, u.ep++) {
|
|
(void) printf("\t%s = %d\n",
|
|
ref_to_str(u.ep->cte_name, hp, cd),
|
|
u.ep->cte_value);
|
|
}
|
|
}
|
|
|
|
stats.s_nemem += n;
|
|
stats.s_emmax = MAX(stats.s_emmax, n);
|
|
|
|
vlen = sizeof (ctf_enum_t) * n;
|
|
break;
|
|
|
|
case CTF_K_FORWARD:
|
|
if (flags != F_STATS) {
|
|
(void) printf("FORWARD %s",
|
|
ref_to_str(tp->ctt_name, hp, cd));
|
|
}
|
|
break;
|
|
|
|
case CTF_K_TYPEDEF:
|
|
if (flags != F_STATS) {
|
|
(void) printf("TYPEDEF %s refers to %u",
|
|
ref_to_str(tp->ctt_name, hp, cd),
|
|
tp->ctt_type);
|
|
}
|
|
break;
|
|
|
|
case CTF_K_VOLATILE:
|
|
if (flags != F_STATS) {
|
|
(void) printf("VOLATILE %s refers to %u",
|
|
ref_to_str(tp->ctt_name, hp, cd),
|
|
tp->ctt_type);
|
|
}
|
|
break;
|
|
|
|
case CTF_K_CONST:
|
|
if (flags != F_STATS) {
|
|
(void) printf("CONST %s refers to %u",
|
|
ref_to_str(tp->ctt_name, hp, cd),
|
|
tp->ctt_type);
|
|
}
|
|
break;
|
|
|
|
case CTF_K_RESTRICT:
|
|
if (flags != F_STATS) {
|
|
(void) printf("RESTRICT %s refers to %u",
|
|
ref_to_str(tp->ctt_name, hp, cd),
|
|
tp->ctt_type);
|
|
}
|
|
break;
|
|
|
|
case CTF_K_UNKNOWN:
|
|
break; /* hole in type id space */
|
|
|
|
default:
|
|
(void) printf("unexpected kind %u\n", kind);
|
|
return (E_ERROR);
|
|
}
|
|
|
|
if (flags != F_STATS)
|
|
(void) printf("\n");
|
|
|
|
stats.s_ntypes++;
|
|
stats.s_types[kind]++;
|
|
|
|
tp = (ctf_type_t *)((uintptr_t)tp + increment + vlen);
|
|
}
|
|
|
|
return (E_SUCCESS);
|
|
}
|
|
|
|
static int
|
|
read_strtab(const ctf_header_t *hp, const ctf_data_t *cd)
|
|
{
|
|
size_t n, off, len = hp->cth_strlen;
|
|
const char *s = cd->cd_ctfdata + hp->cth_stroff;
|
|
|
|
if (flags != F_STATS)
|
|
print_line("- String Table ");
|
|
|
|
if (hp->cth_stroff >= cd->cd_ctflen)
|
|
WARN("file is truncated or cth_stroff is corrupt\n");
|
|
if (hp->cth_stroff + hp->cth_strlen > cd->cd_ctflen)
|
|
WARN("file is truncated or cth_strlen is corrupt\n");
|
|
|
|
for (off = 0; len != 0; off += n) {
|
|
if (flags != F_STATS) {
|
|
(void) printf(" [%lu] %s\n", (ulong_t)off,
|
|
s[0] == '\0' ? "\\0" : s);
|
|
}
|
|
n = strlen(s) + 1;
|
|
len -= n;
|
|
s += n;
|
|
|
|
stats.s_nstr++;
|
|
stats.s_strlen += n;
|
|
stats.s_strmax = MAX(stats.s_strmax, n);
|
|
}
|
|
|
|
return (E_SUCCESS);
|
|
}
|
|
|
|
static void
|
|
long_stat(const char *name, ulong_t value)
|
|
{
|
|
(void) printf(" %-36s= %lu\n", name, value);
|
|
}
|
|
|
|
static void
|
|
fp_stat(const char *name, float value)
|
|
{
|
|
(void) printf(" %-36s= %.2f\n", name, value);
|
|
}
|
|
|
|
static int
|
|
print_stats(void)
|
|
{
|
|
print_line("- CTF Statistics ");
|
|
|
|
long_stat("total number of data objects", stats.s_ndata);
|
|
(void) printf("\n");
|
|
|
|
long_stat("total number of functions", stats.s_nfunc);
|
|
long_stat("total number of function arguments", stats.s_nargs);
|
|
long_stat("maximum argument list length", stats.s_argmax);
|
|
|
|
if (stats.s_nfunc != 0) {
|
|
fp_stat("average argument list length",
|
|
(float)stats.s_nargs / (float)stats.s_nfunc);
|
|
}
|
|
|
|
(void) printf("\n");
|
|
|
|
long_stat("total number of types", stats.s_ntypes);
|
|
long_stat("total number of integers", stats.s_types[CTF_K_INTEGER]);
|
|
long_stat("total number of floats", stats.s_types[CTF_K_FLOAT]);
|
|
long_stat("total number of pointers", stats.s_types[CTF_K_POINTER]);
|
|
long_stat("total number of arrays", stats.s_types[CTF_K_ARRAY]);
|
|
long_stat("total number of func types", stats.s_types[CTF_K_FUNCTION]);
|
|
long_stat("total number of structs", stats.s_types[CTF_K_STRUCT]);
|
|
long_stat("total number of unions", stats.s_types[CTF_K_UNION]);
|
|
long_stat("total number of enums", stats.s_types[CTF_K_ENUM]);
|
|
long_stat("total number of forward tags", stats.s_types[CTF_K_FORWARD]);
|
|
long_stat("total number of typedefs", stats.s_types[CTF_K_TYPEDEF]);
|
|
long_stat("total number of volatile types",
|
|
stats.s_types[CTF_K_VOLATILE]);
|
|
long_stat("total number of const types", stats.s_types[CTF_K_CONST]);
|
|
long_stat("total number of restrict types",
|
|
stats.s_types[CTF_K_RESTRICT]);
|
|
long_stat("total number of unknowns (holes)",
|
|
stats.s_types[CTF_K_UNKNOWN]);
|
|
|
|
(void) printf("\n");
|
|
|
|
long_stat("total number of struct members", stats.s_nsmem);
|
|
long_stat("maximum number of struct members", stats.s_smmax);
|
|
long_stat("total size of all structs", stats.s_nsbytes);
|
|
long_stat("maximum size of a struct", stats.s_sbmax);
|
|
|
|
if (stats.s_types[CTF_K_STRUCT] != 0) {
|
|
fp_stat("average number of struct members",
|
|
(float)stats.s_nsmem / (float)stats.s_types[CTF_K_STRUCT]);
|
|
fp_stat("average size of a struct", (float)stats.s_nsbytes /
|
|
(float)stats.s_types[CTF_K_STRUCT]);
|
|
}
|
|
|
|
(void) printf("\n");
|
|
|
|
long_stat("total number of union members", stats.s_numem);
|
|
long_stat("maximum number of union members", stats.s_ummax);
|
|
long_stat("total size of all unions", stats.s_nubytes);
|
|
long_stat("maximum size of a union", stats.s_ubmax);
|
|
|
|
if (stats.s_types[CTF_K_UNION] != 0) {
|
|
fp_stat("average number of union members",
|
|
(float)stats.s_numem / (float)stats.s_types[CTF_K_UNION]);
|
|
fp_stat("average size of a union", (float)stats.s_nubytes /
|
|
(float)stats.s_types[CTF_K_UNION]);
|
|
}
|
|
|
|
(void) printf("\n");
|
|
|
|
long_stat("total number of enum members", stats.s_nemem);
|
|
long_stat("maximum number of enum members", stats.s_emmax);
|
|
|
|
if (stats.s_types[CTF_K_ENUM] != 0) {
|
|
fp_stat("average number of enum members",
|
|
(float)stats.s_nemem / (float)stats.s_types[CTF_K_ENUM]);
|
|
}
|
|
|
|
(void) printf("\n");
|
|
|
|
long_stat("total number of unique strings", stats.s_nstr);
|
|
long_stat("bytes of string data", stats.s_strlen);
|
|
long_stat("maximum string length", stats.s_strmax);
|
|
|
|
if (stats.s_nstr != 0) {
|
|
fp_stat("average string length",
|
|
(float)stats.s_strlen / (float)stats.s_nstr);
|
|
}
|
|
|
|
(void) printf("\n");
|
|
return (E_SUCCESS);
|
|
}
|
|
|
|
static int
|
|
print_usage(FILE *fp, int verbose)
|
|
{
|
|
(void) fprintf(fp, "Usage: %s [-dfhlsSt] [-u file] file\n", getpname());
|
|
|
|
if (verbose) {
|
|
(void) fprintf(fp,
|
|
"\t-d dump data object section\n"
|
|
"\t-f dump function section\n"
|
|
"\t-h dump file header\n"
|
|
"\t-l dump label table\n"
|
|
"\t-s dump string table\n"
|
|
"\t-S dump statistics\n"
|
|
"\t-t dump type section\n"
|
|
"\t-u save uncompressed CTF to a file\n");
|
|
}
|
|
|
|
return (E_USAGE);
|
|
}
|
|
|
|
static Elf_Scn *
|
|
findelfscn(Elf *elf, GElf_Ehdr *ehdr, char *secname)
|
|
{
|
|
GElf_Shdr shdr;
|
|
Elf_Scn *scn;
|
|
char *name;
|
|
|
|
for (scn = NULL; (scn = elf_nextscn(elf, scn)) != NULL; ) {
|
|
if (gelf_getshdr(scn, &shdr) != NULL && (name =
|
|
elf_strptr(elf, ehdr->e_shstrndx, shdr.sh_name)) != NULL &&
|
|
strcmp(name, secname) == 0)
|
|
return (scn);
|
|
}
|
|
|
|
return (NULL);
|
|
}
|
|
|
|
int
|
|
main(int argc, char *argv[])
|
|
{
|
|
const char *filename = NULL;
|
|
const char *ufile = NULL;
|
|
int error = 0;
|
|
int c, fd, ufd;
|
|
|
|
ctf_data_t cd;
|
|
const ctf_preamble_t *pp;
|
|
ctf_header_t *hp;
|
|
Elf *elf;
|
|
GElf_Ehdr ehdr;
|
|
|
|
(void) elf_version(EV_CURRENT);
|
|
|
|
for (opterr = 0; optind < argc; optind++) {
|
|
while ((c = getopt(argc, argv, "dfhlsStu:")) != (int)EOF) {
|
|
switch (c) {
|
|
case 'd':
|
|
flags |= F_DATA;
|
|
break;
|
|
case 'f':
|
|
flags |= F_FUNC;
|
|
break;
|
|
case 'h':
|
|
flags |= F_HDR;
|
|
break;
|
|
case 'l':
|
|
flags |= F_LABEL;
|
|
break;
|
|
case 's':
|
|
flags |= F_STR;
|
|
break;
|
|
case 'S':
|
|
flags |= F_STATS;
|
|
break;
|
|
case 't':
|
|
flags |= F_TYPES;
|
|
break;
|
|
case 'u':
|
|
ufile = optarg;
|
|
break;
|
|
default:
|
|
if (optopt == '?')
|
|
return (print_usage(stdout, 1));
|
|
warn("illegal option -- %c\n", optopt);
|
|
return (print_usage(stderr, 0));
|
|
}
|
|
}
|
|
|
|
if (optind < argc) {
|
|
if (filename != NULL)
|
|
return (print_usage(stderr, 0));
|
|
filename = argv[optind];
|
|
}
|
|
}
|
|
|
|
if (filename == NULL)
|
|
return (print_usage(stderr, 0));
|
|
|
|
if (flags == 0 && ufile == NULL)
|
|
flags = F_ALLMSK;
|
|
|
|
if ((fd = open(filename, O_RDONLY)) == -1)
|
|
die("failed to open %s", filename);
|
|
|
|
if ((elf = elf_begin(fd, ELF_C_READ, NULL)) != NULL &&
|
|
gelf_getehdr(elf, &ehdr) != NULL) {
|
|
|
|
Elf_Data *dp;
|
|
Elf_Scn *ctfscn = findelfscn(elf, &ehdr, ".SUNW_ctf");
|
|
Elf_Scn *symscn;
|
|
GElf_Shdr ctfshdr;
|
|
|
|
if (ctfscn == NULL || (dp = elf_getdata(ctfscn, NULL)) == NULL)
|
|
die("%s does not contain .SUNW_ctf data\n", filename);
|
|
|
|
cd.cd_ctfdata = dp->d_buf;
|
|
cd.cd_ctflen = dp->d_size;
|
|
|
|
/*
|
|
* If the sh_link field of the CTF section header is non-zero
|
|
* it indicates which section contains the symbol table that
|
|
* should be used. We default to the .symtab section if sh_link
|
|
* is zero or if there's an error reading the section header.
|
|
*/
|
|
if (gelf_getshdr(ctfscn, &ctfshdr) != NULL &&
|
|
ctfshdr.sh_link != 0) {
|
|
symscn = elf_getscn(elf, ctfshdr.sh_link);
|
|
} else {
|
|
symscn = findelfscn(elf, &ehdr, ".symtab");
|
|
}
|
|
|
|
/* If we found a symbol table, find the corresponding strings */
|
|
if (symscn != NULL) {
|
|
GElf_Shdr shdr;
|
|
Elf_Scn *symstrscn;
|
|
|
|
if (gelf_getshdr(symscn, &shdr) != NULL) {
|
|
symstrscn = elf_getscn(elf, shdr.sh_link);
|
|
|
|
cd.cd_nsyms = shdr.sh_size / shdr.sh_entsize;
|
|
cd.cd_symdata = elf_getdata(symscn, NULL);
|
|
cd.cd_strdata = elf_getdata(symstrscn, NULL);
|
|
}
|
|
}
|
|
} else {
|
|
struct stat st;
|
|
|
|
if (fstat(fd, &st) == -1)
|
|
die("failed to fstat %s", filename);
|
|
|
|
cd.cd_ctflen = st.st_size;
|
|
cd.cd_ctfdata = mmap(NULL, cd.cd_ctflen, PROT_READ,
|
|
MAP_PRIVATE, fd, 0);
|
|
if (cd.cd_ctfdata == MAP_FAILED)
|
|
die("failed to mmap %s", filename);
|
|
}
|
|
|
|
/*
|
|
* Get a pointer to the CTF data buffer and interpret the first portion
|
|
* as a ctf_header_t. Validate the magic number and size.
|
|
*/
|
|
|
|
if (cd.cd_ctflen < sizeof (ctf_preamble_t))
|
|
die("%s does not contain a CTF preamble\n", filename);
|
|
|
|
/* LINTED - pointer alignment */
|
|
pp = (const ctf_preamble_t *)cd.cd_ctfdata;
|
|
|
|
if (pp->ctp_magic != CTF_MAGIC)
|
|
die("%s does not appear to contain CTF data\n", filename);
|
|
|
|
if (pp->ctp_version == CTF_VERSION) {
|
|
/* LINTED - pointer alignment */
|
|
hp = (ctf_header_t *)cd.cd_ctfdata;
|
|
cd.cd_ctfdata = (caddr_t)cd.cd_ctfdata + sizeof (ctf_header_t);
|
|
|
|
if (cd.cd_ctflen < sizeof (ctf_header_t)) {
|
|
die("%s does not contain a v%d CTF header\n", filename,
|
|
CTF_VERSION);
|
|
}
|
|
|
|
} else {
|
|
die("%s contains unsupported CTF version %d\n", filename,
|
|
pp->ctp_version);
|
|
}
|
|
|
|
/*
|
|
* If the data buffer is compressed, then malloc a buffer large enough
|
|
* to hold the decompressed data, and use zlib to decompress it.
|
|
*/
|
|
if (hp->cth_flags & CTF_F_COMPRESS) {
|
|
z_stream zstr;
|
|
void *buf;
|
|
int rc;
|
|
|
|
if ((buf = malloc(hp->cth_stroff + hp->cth_strlen)) == NULL)
|
|
die("failed to allocate decompression buffer");
|
|
|
|
bzero(&zstr, sizeof (z_stream));
|
|
zstr.next_in = (void *)cd.cd_ctfdata;
|
|
zstr.avail_in = cd.cd_ctflen;
|
|
zstr.next_out = buf;
|
|
zstr.avail_out = hp->cth_stroff + hp->cth_strlen;
|
|
|
|
if ((rc = inflateInit(&zstr)) != Z_OK)
|
|
die("failed to initialize zlib: %s\n", zError(rc));
|
|
|
|
if ((rc = inflate(&zstr, Z_FINISH)) != Z_STREAM_END)
|
|
die("failed to decompress CTF data: %s\n", zError(rc));
|
|
|
|
if ((rc = inflateEnd(&zstr)) != Z_OK)
|
|
die("failed to finish decompression: %s\n", zError(rc));
|
|
|
|
if (zstr.total_out != hp->cth_stroff + hp->cth_strlen)
|
|
die("CTF data is corrupt -- short decompression\n");
|
|
|
|
cd.cd_ctfdata = buf;
|
|
cd.cd_ctflen = hp->cth_stroff + hp->cth_strlen;
|
|
}
|
|
|
|
if (flags & F_HDR)
|
|
error |= print_header(hp, &cd);
|
|
if (flags & (F_LABEL))
|
|
error |= print_labeltable(hp, &cd);
|
|
if (flags & (F_DATA | F_STATS))
|
|
error |= read_data(hp, &cd);
|
|
if (flags & (F_FUNC | F_STATS))
|
|
error |= read_funcs(hp, &cd);
|
|
if (flags & (F_TYPES | F_STATS))
|
|
error |= read_types(hp, &cd);
|
|
if (flags & (F_STR | F_STATS))
|
|
error |= read_strtab(hp, &cd);
|
|
if (flags & F_STATS)
|
|
error |= print_stats();
|
|
|
|
/*
|
|
* If the -u option is specified, write the uncompressed CTF data to a
|
|
* raw CTF file. CTF data can already be extracted compressed by
|
|
* applying elfdump -w -N .SUNW_ctf to an ELF file, so we don't bother.
|
|
*/
|
|
if (ufile != NULL) {
|
|
ctf_header_t h;
|
|
|
|
bcopy(hp, &h, sizeof (h));
|
|
h.cth_flags &= ~CTF_F_COMPRESS;
|
|
|
|
if ((ufd = open(ufile, O_WRONLY|O_CREAT|O_TRUNC, 0666)) < 0 ||
|
|
write(ufd, &h, sizeof (h)) != sizeof (h) ||
|
|
write(ufd, cd.cd_ctfdata, cd.cd_ctflen) != cd.cd_ctflen) {
|
|
warn("failed to write CTF data to '%s'", ufile);
|
|
error |= E_ERROR;
|
|
}
|
|
|
|
(void) close(ufd);
|
|
}
|
|
|
|
if (elf != NULL)
|
|
(void) elf_end(elf);
|
|
|
|
(void) close(fd);
|
|
return (error);
|
|
}
|