diff --git a/CMakeLists.txt b/CMakeLists.txt index 3bf396c..969d831 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -27,13 +27,14 @@ endif (NOT CMAKE_BUILD_TYPE) add_definitions(-D_GNU_SOURCE) find_package(DWARF REQUIRED) +find_package(ZLIB REQUIRED) _set_fancy(LIB_INSTALL_DIR "${EXEC_INSTALL_PREFIX}${CMAKE_INSTALL_PREFIX}/${__LIB}" "libdir") -set(dwarves_LIB_SRCS dwarves.c dwarf_loader.c) +set(dwarves_LIB_SRCS dwarves.c ctf_loader.c libctf.c dwarf_loader.c) add_library(dwarves SHARED ${dwarves_LIB_SRCS}) set_target_properties(dwarves PROPERTIES VERSION 1.0.0 SOVERSION 1) -target_link_libraries(dwarves ${DWARF_LIBRARIES}) +target_link_libraries(dwarves ${DWARF_LIBRARIES} ${ZLIB_LIBRARIES}) set(dwarves_emit_LIB_SRCS dwarves_emit.c) add_library(dwarves_emit SHARED ${dwarves_emit_LIB_SRCS}) diff --git a/MANIFEST b/MANIFEST index 455d24d..a2f1b71 100644 --- a/MANIFEST +++ b/MANIFEST @@ -1,3 +1,5 @@ +ctf_loader.c +ctf_loader.h dwarf_loader.c dwarf_loader.h dwarves.c @@ -32,3 +34,6 @@ lib/ctracer_relay.h lib/linux.blacklist.cu ostra/ostra-cg ostra/python/ostra.py +ctf.h +libctf.c +libctf.h diff --git a/ctf.h b/ctf.h new file mode 100644 index 0000000..710c0ae --- /dev/null +++ b/ctf.h @@ -0,0 +1,154 @@ +#ifndef _CTF_H +#define _CTF_H + +#include + +struct ctf_header { + u_int16_t ctf_magic; /* Header magic value */ +#define CTF_MAGIC 0xcff1 +#define CTF_MAGIC_SWAP 0xf1cf + + u_int8_t ctf_version; /* Header version */ +#define CTF_VERSION 2 + + u_int8_t ctf_flags; /* Header flags */ +#define CTF_FLAGS_COMPR 0x01 + + u_int32_t ctf_parent_label; /* Label of parent CTF object */ + u_int32_t ctf_parent_name; /* Name of parent CTF object */ + + /* All offsets are in bytes are relative to the end of + * this header. + */ + u_int32_t ctf_label_off; /* Offset of label section */ + u_int32_t ctf_object_off; /* Offset of data object section */ + u_int32_t ctf_func_off; /* Offset of function section */ + u_int32_t ctf_type_off; /* Offset of type section */ + u_int32_t ctf_str_off; /* Offset of string section */ + u_int32_t ctf_str_len; /* Length of string section */ +}; + +#define CTF_REF_OFFSET(REF) ((REF) & 0x7fffffff) +#define CTF_REF_TBL_ID(REF) (((REF) >> 31) & 0x1) +#define CTF_STR_TBL_ID_0 0 +#define CTF_STR_TBL_ID_1 1 + +#define CTF_REF_ENCODE(TBL, OFF) (((TBL) << 31) | (OFF)) + +struct ctf_label_ent { + u_int32_t ctf_label_ref; + u_int32_t ctf_type_index; +}; + +/* Types are encoded with ctf_short_type so long as the ctf_size + * field can be fully represented in a u_int16_t. If not, then + * the ctf_size is given the value 0xffff and ctf_full_type is + * used. + */ +struct ctf_short_type { + u_int32_t ctf_name; + u_int16_t ctf_info; + union { + u_int16_t ctf_size; + u_int16_t ctf_type; + } u; +}; + +struct ctf_full_type { + struct ctf_short_type base; + u_int32_t ctf_size_high; + u_int32_t ctf_size_low; +}; + +#define CTF_GET_KIND(VAL) (((VAL) >> 11) & 0x1f) +#define CTF_GET_VLEN(VAL) ((VAL) & 0x3ff) +#define CTF_ISROOT(VAL) (((VAL) & 0x400) != 0) + +#define CTF_INFO_ENCODE(KIND, VLEN, ISROOT) \ + (((ISROOT) ? 0x400 : 0) | ((KIND) << 11) | (VLEN)) + +#define CTF_TYPE_KIND_UNKN 0 /* Unknown */ +#define CTF_TYPE_KIND_INT 1 /* Integer */ +#define CTF_TYPE_KIND_FLT 2 /* Float */ +#define CTF_TYPE_KIND_PTR 3 /* Pointer */ +#define CTF_TYPE_KIND_ARR 4 /* Array */ +#define CTF_TYPE_KIND_FUNC 5 /* Function */ +#define CTF_TYPE_KIND_STR 6 /* Struct */ +#define CTF_TYPE_KIND_UNION 7 /* Union */ +#define CTF_TYPE_KIND_ENUM 8 /* Enumeration */ +#define CTF_TYPE_KIND_FWD 9 /* Forward */ +#define CTF_TYPE_KIND_TYPDEF 10 /* Typedef */ +#define CTF_TYPE_KIND_VOLATILE 11 /* Volatile */ +#define CTF_TYPE_KIND_CONST 12 /* Const */ +#define CTF_TYPE_KIND_RESTRICT 13 /* Restrict */ +#define CTF_TYPE_KIND_MAX 31 + +#define CTF_TYPE_INT_ATTRS(VAL) ((VAL) >> 24) +#define CTF_TYPE_INT_OFFSET(VAL) (((VAL) >> 16) & 0xff) +#define CTF_TYPE_INT_BITS(VAL) ((VAL) & 0xffff) + +#define CTF_TYPE_INT_ENCODE(ATTRS, OFF, BITS) \ + (((ATTRS) << 24) | ((OFF) << 16) | (BITS)) + +/* Integer type attributes */ +#define CTF_TYPE_INT_SIGNED 0x1 +#define CTF_TYPE_INT_CHAR 0x2 +#define CTF_TYPE_INT_BOOL 0x4 +#define CTF_TYPE_INT_VARARGS 0x8 + +#define CTF_TYPE_FP_ATTRS(VAL) ((VAL) >> 24) +#define CTF_TYPE_FP_OFFSET(VAL) (((VAL) >> 16) & 0xff) +#define CTF_TYPE_FP_BITS(VAL) ((VAL) & 0xffff) + +#define CTF_TYPE_FP_ENCODE(ATTRS, OFF, BITS) \ + (((ATTRS) << 24) | ((OFF) << 16) | (BITS)) + +/* Possible values for the float type attribute field */ +#define CTF_TYPE_FP_SINGLE 1 +#define CTF_TYPE_FP_DOUBLE 2 +#define CTF_TYPE_FP_CMPLX 3 +#define CTF_TYPE_FP_CMPLX_DBL 4 +#define CTF_TYPE_FP_CMPLX_LDBL 5 +#define CTF_TYPE_FP_LDBL 6 +#define CTF_TYPE_FP_INTVL 7 +#define CTF_TYPE_FP_INTVL_DBL 8 +#define CTF_TYPE_FP_INTVL_LDBL 9 +#define CTF_TYPE_FP_IMGRY 10 +#define CTF_TYPE_FP_IMGRY_DBL 11 +#define CTF_TYPE_FP_IMGRY_LDBL 12 +#define CTF_TYPE_FP_MAX 12 + +struct ctf_enum { + u_int32_t ctf_enum_name; + u_int32_t ctf_enum_val; +}; + +struct ctf_array { + u_int16_t ctf_array_type; + u_int16_t ctf_array_index_type; + u_int32_t ctf_array_nelems; +}; + +/* Struct members are encoded with either ctf_short_member or + * ctf_full_member, depending upon the 'size' of the struct or + * union being defined. If it is less than CTF_SHORT_MEMBER_LIMIT + * then ctf_short_member objects are used to encode, else + * ctf_full_member is used. + */ +#define CTF_SHORT_MEMBER_LIMIT 8192 + +struct ctf_short_member { + u_int32_t ctf_member_name; + u_int16_t ctf_member_type; + u_int16_t ctf_member_offset; +}; + +struct ctf_full_member { + u_int32_t ctf_member_name; + u_int16_t ctf_member_type; + u_int16_t ctf_member_unused; + u_int32_t ctf_member_offset_high; + u_int32_t ctf_member_offset_low; +}; + +#endif /* _CTF_H */ diff --git a/ctf_loader.c b/ctf_loader.c new file mode 100644 index 0000000..bde72c2 --- /dev/null +++ b/ctf_loader.c @@ -0,0 +1,815 @@ +/* ctfdump.c: CTF dumper. + * + * Copyright (C) 2008 David S. Miller + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "libctf.h" +#include "ctf.h" +#include "dutil.h" +#include "dwarves.h" + +static void *zalloc(const size_t size) +{ + void *s = malloc(size); + if (s != NULL) + memset(s, 0, size); + return s; +} + +static void oom(const char *msg) +{ + fprintf(stderr, "libclasses: out of memory(%s)\n", msg); + exit(EXIT_FAILURE); +} + +struct ctf_state { + Ctf *ctf; + Elf *elf; + Elf_Data *elf_syms; + Elf_Data *elf_symstrs; + struct cu *cu; + int elf_num_syms; + int in_fd; +}; + +static Elf_Scn *elf_section_by_name(Elf *elf, GElf_Ehdr *ep, + GElf_Shdr *shp, const char *name) +{ + Elf_Scn *sec = NULL; + + while ((sec = elf_nextscn(elf, sec)) != NULL) { + char *str; + + gelf_getshdr(sec, shp); + str = elf_strptr(elf, ep->e_shstrndx, shp->sh_name); + if (!strcmp(name, str)) + break; + } + + return sec; +} + +struct elf_sym_iter_state { + int (*func)(struct ctf_state *sp, const char *sym_name, + int sym_index, int call_index, void *data); + void *data; + + int st_type; + int limit; +}; + +#if 0 +static int ctf_ignores_elf_symbol(GElf_Sym *sym, char *name, int type) +{ + if (type == STT_OBJECT && + sym->st_shndx == SHN_ABS && + sym->st_value == 0) + return 1; + if (sym->st_name == 0) + return 1; + if (sym->st_shndx == SHN_UNDEF) + return 1; + if (!strcmp(name, "_START_") || !strcmp(name, "_END_")) + return 1; + return 0; +} + +static void elf_symbol_iterate(struct ctf_state *sp, + struct elf_sym_iter_state *ep) +{ + int i, index; + + index = 0; + for (i = 0; i < sp->elf_num_syms; i++) { + GElf_Sym sym; + char *name; + int type; + + if (gelf_getsym(sp->elf_syms, i, &sym) == NULL) { + fprintf(stderr, "Could not get ELF symbol %d.\n", i); + exit(2); + } + type = GELF_ST_TYPE(sym.st_info); + name = (char *)sp->elf_symstrs->d_buf + sym.st_name; + + if ((ep->st_type == -1 || ep->st_type == type) && + !ctf_ignores_elf_symbol(&sym, name, type)) { + if (index >= ep->limit) { + fprintf(stderr, "Symbol limit reached " + "([%u], %d vs %d).\n", + ep->limit, i, sp->elf_num_syms); + exit(2); + } + + if (ep->func(sp, name, i, index++, ep->data) < 0) + return; + } + } +} +#endif + +static void parse_elf(struct ctf_state *sp) +{ + GElf_Ehdr ehdr; + GElf_Shdr shdr; + Elf_Data *data; + Elf_Scn *sec; + + if (gelf_getehdr(sp->elf, &ehdr) == NULL) { + fprintf(stderr, "Cannot get elf header.\n"); + exit(2); + } + + sec = elf_section_by_name(sp->elf, &ehdr, &shdr, ".SUNW_ctf"); + if (!sec) { + fprintf(stderr, "File has no CTF section.\n"); + exit(2); + } + + data = elf_getdata(sec, NULL); + if (!data) { + fprintf(stderr, "Cannot get data of CTF section.\n"); + exit(2); + } + + sp->ctf = ctf_begin(data->d_buf, data->d_size); + if (!sp->ctf) { + fprintf(stderr, "Cannot initialize CTF state.\n"); + exit(2); + } + + if (shdr.sh_link != 0) + sec = elf_getscn(sp->elf, shdr.sh_link); + else + sec = elf_section_by_name(sp->elf, &ehdr, &shdr, ".symtab"); + + if (!sec) + return; + + if (gelf_getshdr(sec, &shdr) != NULL) { + sp->elf_syms = elf_getdata(sec, NULL); + sp->elf_num_syms = shdr.sh_size / shdr.sh_entsize; + + sec = elf_getscn(sp->elf, shdr.sh_link); + sp->elf_symstrs = elf_getdata(sec, NULL); + } +} + +static char *ctf_string(u_int32_t ref, struct ctf_state *sp) +{ + struct ctf_header *hp = ctf_get_buffer(sp->ctf); + u_int32_t off = CTF_REF_OFFSET(ref); + char *name; + + if (CTF_REF_TBL_ID(ref) != CTF_STR_TBL_ID_0) + return "(external ref)"; + + if (off >= ctf_get32(sp->ctf, &hp->ctf_str_len)) + return "(ref out-of-bounds)"; + + if ((off + ctf_get32(sp->ctf, &hp->ctf_str_off)) >= + ctf_get_size(sp->ctf)) + return "(string table truncated)"; + + name = ((char *)(hp + 1) + ctf_get32(sp->ctf, &hp->ctf_str_off) + off); + if (name[0] == '\0') + return "(anonymous)"; + + return name; +} + +static char *ctf_format_flt_attrs(u_int32_t eval, char *buf) +{ + u_int32_t attrs = CTF_TYPE_FP_ATTRS(eval); + + buf[0] = '\0'; + + if (attrs < CTF_TYPE_FP_SINGLE || + attrs > CTF_TYPE_FP_MAX) + buf += sprintf(buf, "0x%02x ", attrs); + else { + switch (attrs) { + case CTF_TYPE_FP_SINGLE: + buf += sprintf(buf, "single "); + break; + case CTF_TYPE_FP_DOUBLE: + buf += sprintf(buf, "double "); + break; + case CTF_TYPE_FP_CMPLX: + buf += sprintf(buf, "complex "); + break; + case CTF_TYPE_FP_CMPLX_DBL: + buf += sprintf(buf, "complex double "); + break; + case CTF_TYPE_FP_CMPLX_LDBL: + buf += sprintf(buf, "complex long double "); + break; + case CTF_TYPE_FP_LDBL: + buf += sprintf(buf, "long double "); + break; + case CTF_TYPE_FP_INTVL: + buf += sprintf(buf, "interval "); + break; + case CTF_TYPE_FP_INTVL_DBL: + buf += sprintf(buf, "interval double "); + break; + case CTF_TYPE_FP_INTVL_LDBL: + buf += sprintf(buf, "interval long double "); + break; + case CTF_TYPE_FP_IMGRY: + buf += sprintf(buf, "imaginary "); + break; + case CTF_TYPE_FP_IMGRY_DBL: + buf += sprintf(buf, "imaginary double "); + break; + case CTF_TYPE_FP_IMGRY_LDBL: + buf += sprintf(buf, "imaginary long double "); + break; + } + } + + return buf; +} + +#if 0 +static int dump_one_func(struct ctf_state *sp, const char *sym_name, + int sym_index, int call_index, void *data) +{ + u_int16_t **func_pp = data; + u_int16_t val = ctf_get16(sp->ctf, *func_pp); + u_int16_t type = CTF_GET_KIND(val); + u_int16_t vlen = CTF_GET_VLEN(val); + u_int16_t i; + + (*func_pp)++; + + if (type == CTF_TYPE_KIND_UNKN && vlen == 0) + return 0; + + if (type != CTF_TYPE_KIND_FUNC) { + fprintf(stderr, "Expected function type, got %u\n", type); + exit(2); + } + + fprintf(stdout, " [%6d] %-36s %8d\n", + call_index, sym_name, sym_index); + fprintf(stdout, " 0x%04x (", + ctf_get16(sp->ctf, *func_pp)); + + (*func_pp)++; + for (i = 0; i < vlen; i++) { + if (i >= 1) + fprintf(stdout, ", "); + + fprintf(stdout, "0x%04x", ctf_get16(sp->ctf, *func_pp)); + (*func_pp)++; + } + fprintf(stdout, ")\n"); + + return 0; +} + +static void dump_funcs(struct ctf_state *sp) +{ + struct ctf_header *hp = ctf_get_buffer(sp->ctf); + struct elf_sym_iter_state estate; + u_int16_t *func_ptr; + + fprintf(stdout, "CTF Functions:\n"); + fprintf(stdout, + " [ Nr ] " + "SymName " + "SymIndex\n" + " Returns " + "Args\n"); + + memset(&estate, 0, sizeof(estate)); + func_ptr = ctf_get_buffer(sp->ctf) + sizeof(*hp) + + ctf_get32(sp->ctf, &hp->ctf_func_off); + estate.data = &func_ptr; + estate.func = dump_one_func; + estate.st_type = STT_FUNC; + estate.limit = INT_MAX; + + elf_symbol_iterate(sp, &estate); + + fprintf(stdout, "\n"); +} +#endif + +static struct base_type *base_type__new(const char *name, size_t size) +{ + struct base_type *self = zalloc(sizeof(*self)); + + if (self != NULL) { + self->name = strings__add(name); + self->size = size / 8; + } + return self; +} + +static void type__init(struct type *self, uint16_t tag, unsigned int id, + const char *name, size_t size) +{ + INIT_LIST_HEAD(&self->node); + INIT_LIST_HEAD(&self->namespace.tags); + self->size = size; + self->namespace.tag.id = id; + self->namespace.tag.tag = tag; + self->namespace.name = strings__add(name[0] == '(' ? NULL : name); +} + +static struct type *type__new(uint16_t tag, unsigned int id, + const char *name, size_t size) +{ + struct type *self = zalloc(sizeof(*self)); + + if (self != NULL) + type__init(self, tag, id, name, size); + + return self; +} + +static struct class *class__new(const char *name, unsigned int id, size_t size) +{ + struct class *self = zalloc(sizeof(*self)); + + if (self != NULL) { + type__init(&self->type, DW_TAG_structure_type, id, name, size); + INIT_LIST_HEAD(&self->vtable); + } + + return self; +} + +static int create_new_base_type(struct ctf_state *sp, void *ptr, + int vlen __unused, struct ctf_full_type *tp, + unsigned int id) +{ + u_int32_t *enc = ptr, name_idx; + char name[64], *buf = name; + u_int32_t eval = ctf_get32(sp->ctf, enc); + u_int32_t attrs = CTF_TYPE_INT_ATTRS(eval); + struct base_type *base; + + if (attrs & CTF_TYPE_INT_SIGNED) + buf += sprintf(buf, "signed "); + if (attrs & CTF_TYPE_INT_CHAR) + buf += sprintf(buf, "char "); + if (attrs & CTF_TYPE_INT_BOOL) + buf += sprintf(buf, "bool "); + if (attrs & CTF_TYPE_INT_VARARGS) + buf += sprintf(buf, "varargs "); + + name_idx = ctf_get32(sp->ctf, &tp->base.ctf_name); + buf += sprintf(buf, "%s", ctf_string(name_idx, sp)); + base = base_type__new(name, CTF_TYPE_INT_BITS(eval)); + if (base == NULL) + oom("base_type__new"); + + base->tag.tag = DW_TAG_base_type; + base->tag.id = id; + cu__add_tag(sp->cu, &base->tag); + + return sizeof(*enc); +} + +static int create_new_base_type_float(struct ctf_state *sp, void *ptr, + int vlen __unused, + struct ctf_full_type *tp, + unsigned int id) +{ + u_int32_t *enc = ptr, eval; + char name[64]; + struct base_type *base; + + eval = ctf_get32(sp->ctf, enc); + sprintf(ctf_format_flt_attrs(eval, name), "%s", + ctf_string(ctf_get32(sp->ctf, &tp->base.ctf_name), sp)); + + base = base_type__new(name, CTF_TYPE_FP_BITS(eval)); + if (base == NULL) + oom("base_type__new"); + + base->tag.tag = DW_TAG_base_type; + base->tag.id = id; + cu__add_tag(sp->cu, &base->tag); + + return sizeof(*enc); +} + +static int create_new_array(struct ctf_state *sp, void *ptr, + int vlen __unused, + struct ctf_full_type *tp __unused, + unsigned int id) +{ + struct ctf_array *ap = ptr; + struct array_type *self = zalloc(sizeof(*self)); + + if (self == NULL) + oom("array_type"); + + /* FIXME: where to get the number of dimensions? + * it it flattened? */ + self->dimensions = 1; + self->nr_entries = malloc(sizeof(uint32_t)); + + if (self->nr_entries == NULL) + oom("array_type->nr_entries"); + + self->nr_entries[0] = ctf_get32(sp->ctf, &ap->ctf_array_nelems); + self->tag.tag = DW_TAG_array_type; + self->tag.id = id; + self->tag.type = ctf_get16(sp->ctf, &ap->ctf_array_type); + + cu__add_tag(sp->cu, &self->tag); + + return sizeof(*ap); +} + +static int create_new_subroutine_type(struct ctf_state *sp, void *ptr, + int vlen, struct ctf_full_type *tp, + unsigned int id) +{ + u_int16_t *args = ptr; + u_int16_t i; + const char *name = ctf_string(ctf_get32(sp->ctf, &tp->base.ctf_name), sp); + unsigned int type = ctf_get16(sp->ctf, &tp->base.u.ctf_type); + struct function *self = zalloc(sizeof(*self)); + + if (self == NULL) + oom("function__new"); + + self->name = strings__add(name); + INIT_LIST_HEAD(&self->vtable_node); + INIT_LIST_HEAD(&self->tool_node); + INIT_LIST_HEAD(&self->proto.parms); + self->proto.tag.tag = DW_TAG_subroutine_type; + self->proto.tag.id = id; + self->proto.tag.type = type; + INIT_LIST_HEAD(&self->lexblock.tags); + + for (i = 0; i < vlen; i++) { + //fprintf(stdout, "0x%04x", ctf_get16(sp->ctf, &args[i])); + } + + vlen *= sizeof(*args); + + /* Round up to next multiple of 4 to maintain + * 32-bit alignment. + */ + if (vlen & 0x2) + vlen += 0x2; + + cu__add_tag(sp->cu, &self->proto.tag); + + return vlen; +} + +static unsigned long create_full_members(struct ctf_state *sp, void *ptr, + int vlen, struct type *class) +{ + struct ctf_full_member *mp = ptr; + int i; + + for (i = 0; i < vlen; i++) { + struct class_member *member = zalloc(sizeof(*member)); + uint32_t bit_offset; + + if (member == NULL) + oom("class_member"); + + member->tag.tag = DW_TAG_member; + member->tag.type = ctf_get16(sp->ctf, &mp[i].ctf_member_type); + member->name = strings__add(ctf_string(ctf_get32(sp->ctf, &mp[i].ctf_member_name), sp)); + bit_offset = (ctf_get32(sp->ctf, &mp[i].ctf_member_offset_high) << 16) | + ctf_get32(sp->ctf, &mp[i].ctf_member_offset_low); + member->offset = bit_offset / 8; + member->bit_offset = bit_offset % 8; + type__add_member(class, member); + hashtags__hash(sp->cu->hash_tags, &member->tag); + } + + return sizeof(*mp); +} + +static unsigned long create_short_members(struct ctf_state *sp, void *ptr, + int vlen, struct type *class) +{ + struct ctf_short_member *mp = ptr; + int i; + + for (i = 0; i < vlen; i++) { + struct class_member *member = zalloc(sizeof(*member)); + uint32_t bit_offset; + + if (member == NULL) + oom("class_member"); + + member->tag.tag = DW_TAG_member; + member->tag.type = ctf_get16(sp->ctf, &mp[i].ctf_member_type); + member->name = strings__add(ctf_string(ctf_get32(sp->ctf, &mp[i].ctf_member_name), sp)); + bit_offset = ctf_get16(sp->ctf, &mp[i].ctf_member_offset); + member->offset = bit_offset / 8; + member->bit_offset = bit_offset % 8; + + type__add_member(class, member); + hashtags__hash(sp->cu->hash_tags, &member->tag); + } + + return sizeof(*mp); +} + +static int create_new_class(struct ctf_state *sp, void *ptr, + int vlen, struct ctf_full_type *tp, + u_int64_t size, unsigned int id) +{ + unsigned long member_size; + const char *name = ctf_string(ctf_get32(sp->ctf, &tp->base.ctf_name), sp); + struct class *self = class__new(name, id, size); + + if (size >= CTF_SHORT_MEMBER_LIMIT) { + member_size = create_full_members(sp, ptr, vlen, &self->type); + } else { + member_size = create_short_members(sp, ptr, vlen, &self->type); + } + + cu__add_tag(sp->cu, &self->type.namespace.tag); + + return (vlen * member_size); +} + +static int create_new_union(struct ctf_state *sp, void *ptr, + int vlen, struct ctf_full_type *tp, + u_int64_t size, unsigned int id) +{ + unsigned long member_size; + const char *name = ctf_string(ctf_get32(sp->ctf, &tp->base.ctf_name), sp); + struct type *self = type__new(DW_TAG_union_type, id, name, size); + + if (size >= CTF_SHORT_MEMBER_LIMIT) { + member_size = create_full_members(sp, ptr, vlen, self); + } else { + member_size = create_short_members(sp, ptr, vlen, self); + } + + cu__add_tag(sp->cu, &self->namespace.tag); + + return (vlen * member_size); +} + +static struct enumerator *enumerator__new(const char *name, + uint32_t value) +{ + struct enumerator *self = zalloc(sizeof(*self)); + + if (self != NULL) { + self->name = strings__add(name); + self->value = value; + self->tag.tag = DW_TAG_enumerator; + } + + return self; +} + +static int create_new_enumeration(struct ctf_state *sp, void *ptr, + int vlen, struct ctf_full_type *tp, + unsigned int id) +{ + struct ctf_enum *ep = ptr; + u_int16_t i; + struct type *enumeration = type__new(DW_TAG_enumeration_type, id, + ctf_string(ctf_get32(sp->ctf, + &tp->base.ctf_name), sp), 0); + + if (enumeration == NULL) + oom("enumeration"); + + for (i = 0; i < vlen; i++) { + char *name = ctf_string(ctf_get32(sp->ctf, &ep[i].ctf_enum_name), sp); + uint32_t value = ctf_get32(sp->ctf, &ep[i].ctf_enum_val); + struct enumerator *enumerator = enumerator__new(name, value); + + if (enumerator == NULL) + oom("enumerator__new"); + + enumeration__add(enumeration, enumerator); + hashtags__hash(sp->cu->hash_tags, &enumerator->tag); + } + + cu__add_tag(sp->cu, &enumeration->namespace.tag); + + return (vlen * sizeof(*ep)); +} + +static int create_new_forward_decl(struct ctf_state *sp, void *ptr __unused, + int vlen __unused, struct ctf_full_type *tp, + u_int64_t size, unsigned int id) +{ + char *name = ctf_string(ctf_get32(sp->ctf, &tp->base.ctf_name), sp); + struct class *self = class__new(name, id, size); + + if (self == NULL) + oom("class foward decl"); + self->type.declaration = 1; + cu__add_tag(sp->cu, &self->type.namespace.tag); + return 0; +} + +static int create_new_typedef(struct ctf_state *sp, int type, + void *ptr __unused, int vlen __unused, + struct ctf_full_type *tp, + u_int64_t size, unsigned int id) +{ + const char *name = ctf_string(ctf_get32(sp->ctf, &tp->base.ctf_name), sp); + unsigned int type_id = ctf_get16(sp->ctf, &tp->base.u.ctf_type); + unsigned int tag; + struct type *self; + + switch (type) { + case CTF_TYPE_KIND_TYPDEF: tag = DW_TAG_typedef; break; + default: + printf("%s: FOO %d\n\n", __func__, type); + return 0; + } + + self = type__new(tag, id, name, size); + if (self == NULL) + oom("type__new"); + self->namespace.tag.type = type_id; + cu__add_tag(sp->cu, &self->namespace.tag); + + return 0; +} + +static int create_new_tag(struct ctf_state *sp, int type, + void *ptr __unused, int vlen __unused, + struct ctf_full_type *tp, unsigned int id) +{ + unsigned int type_id = ctf_get16(sp->ctf, &tp->base.u.ctf_type); + struct tag *self = zalloc(sizeof(*self)); + + if (self == NULL) + oom("tag__new"); + + switch (type) { + case CTF_TYPE_KIND_CONST: self->tag = DW_TAG_const_type; break; + case CTF_TYPE_KIND_PTR: self->tag = DW_TAG_pointer_type; break; + case CTF_TYPE_KIND_RESTRICT: self->tag = DW_TAG_restrict_type; break; + case CTF_TYPE_KIND_VOLATILE: self->tag = DW_TAG_volatile_type; break; + default: + printf("%s: FOO %d\n\n", __func__, type); + return 0; + } + + self->id = id; + self->type = type_id; + cu__add_tag(sp->cu, self); + + return 0; +} + +static void load_types(struct ctf_state *sp) +{ + struct ctf_header *hp = ctf_get_buffer(sp->ctf); + struct ctf_full_type *type_ptr, *end; + unsigned int type_index; + + type_ptr = ctf_get_buffer(sp->ctf) + sizeof(*hp) + + ctf_get32(sp->ctf, &hp->ctf_type_off); + end = ctf_get_buffer(sp->ctf) + sizeof(*hp) + + ctf_get32(sp->ctf, &hp->ctf_str_off); + + type_index = 0x0001; + if (hp->ctf_parent_name || + hp->ctf_parent_label) + type_index += 0x8000; + + while (type_ptr < end) { + u_int16_t val, type, vlen, base_size; + u_int64_t size; + void *ptr; + + val = ctf_get16(sp->ctf, &type_ptr->base.ctf_info); + type = CTF_GET_KIND(val); + vlen = CTF_GET_VLEN(val); + + base_size = ctf_get16(sp->ctf, &type_ptr->base.u.ctf_size); + ptr = type_ptr; + if (base_size == 0xffff) { + size = ctf_get32(sp->ctf, &type_ptr->ctf_size_high); + size <<= 32; + size |= ctf_get32(sp->ctf, &type_ptr->ctf_size_low); + ptr += sizeof(struct ctf_full_type); + } else { + size = base_size; + ptr += sizeof(struct ctf_short_type); + } + + if (type == CTF_TYPE_KIND_INT) { + vlen = create_new_base_type(sp, ptr, vlen, type_ptr, type_index); + } else if (type == CTF_TYPE_KIND_FLT) { + vlen = create_new_base_type_float(sp, ptr, vlen, type_ptr, type_index); + } else if (type == CTF_TYPE_KIND_ARR) { + vlen = create_new_array(sp, ptr, vlen, type_ptr, type_index); + } else if (type == CTF_TYPE_KIND_FUNC) { + vlen = create_new_subroutine_type(sp, ptr, vlen, type_ptr, type_index); + } else if (type == CTF_TYPE_KIND_STR) { + vlen = create_new_class(sp, ptr, + vlen, type_ptr, size, type_index); + } else if (type == CTF_TYPE_KIND_UNION) { + vlen = create_new_union(sp, ptr, + vlen, type_ptr, size, type_index); + } else if (type == CTF_TYPE_KIND_ENUM) { + vlen = create_new_enumeration(sp, ptr, vlen, type_ptr, type_index); + } else if (type == CTF_TYPE_KIND_FWD) { + vlen = create_new_forward_decl(sp, ptr, vlen, type_ptr, size, type_index); + } else if (type == CTF_TYPE_KIND_TYPDEF) { + vlen = create_new_typedef(sp, type, ptr, vlen, type_ptr, size, type_index); + } else if (type == CTF_TYPE_KIND_VOLATILE || + type == CTF_TYPE_KIND_PTR || + type == CTF_TYPE_KIND_CONST || + type == CTF_TYPE_KIND_RESTRICT) { + vlen = create_new_tag(sp, type, ptr, vlen, type_ptr, type_index); + } else if (type == CTF_TYPE_KIND_UNKN) { + printf("CTF: [%#6x] %1d Unknown\n", type_index, CTF_ISROOT(val)); + vlen = 0; + } else { + abort(); + } + + type_ptr = ptr + vlen; + type_index++; + } +} + +static void dump_ctf(struct ctf_state *sp) +{ + //dump_funcs(sp); + load_types(sp); +} + +static void open_files(struct ctf_state *sp, const char *in_filename) +{ + sp->in_fd = -1; + if (in_filename) { + sp->in_fd = open(in_filename, O_RDONLY); + if (sp->in_fd < 0) { + perror("open"); + exit(2); + } + } +} + +int ctf__load(struct cus *self, struct argp *argp __unused, + int argc, char *argv[]) +{ + struct ctf_state state; + + memset(&state, 0, sizeof(state)); + + open_files(&state, argv[argc - 1]); + + if (elf_version(EV_CURRENT) == EV_NONE) { + fprintf(stderr, "Cannot set libelf version.\n"); + exit(2); + } + + state.elf = elf_begin(state.in_fd, ELF_C_READ_MMAP, NULL); + if (!state.elf) { + fprintf(stderr, "Cannot read ELF file.\n"); + exit(2); + } + + parse_elf(&state); + + state.cu = cu__new("FIXME.c", 8, NULL, 0); + if (state.cu == NULL) + oom("cu__new"); + + cus__add(self, state.cu); + + dump_ctf(&state); + + elf_end(state.elf); + + close(state.in_fd); + + return 0; +} diff --git a/ctf_loader.h b/ctf_loader.h new file mode 100644 index 0000000..cb76253 --- /dev/null +++ b/ctf_loader.h @@ -0,0 +1,16 @@ +#ifndef _CTF_LOADER_H_ +#define _CTF_LOADER_H_ 1 +/* + Copyright (C) 2008 Arnaldo Carvalho de Melo + + This program is free software; you can redistribute it and/or modify it + under the terms of version 2 of the GNU General Public License as + published by the Free Software Foundation. +*/ + +struct cus; +struct argp; + +int ctf__load(struct cus *self, struct argp *argp, int argc, char *argv[]); + +#endif /* _CTF_LOADER_H_ */ diff --git a/dwarves.c b/dwarves.c index f40288d..0ff016d 100644 --- a/dwarves.c +++ b/dwarves.c @@ -24,6 +24,7 @@ #include #include "config.h" +#include "ctf_loader.h" #include "dwarf_loader.h" #include "list.h" #include "dwarves.h" @@ -1292,8 +1293,10 @@ static size_t union__fprintf(struct type *self, const struct cu *cu, if (type == NULL) { tag__type_not_found(&pos->tag); - printed += fprintf(fp, "%.*s>>>ERROR: type for %s not " - "found!\n", uconf.indent, tabs, pos->name); + printed += fprintf(fp, "%.*s>>>ERROR: type(%#llx) for %s not " + "found!\n", uconf.indent, tabs, + (unsigned long long)pos->tag.type, + pos->name); continue; } @@ -2096,8 +2099,10 @@ size_t class__fprintf(struct class *self, const struct cu *cu, type = cu__find_tag_by_id(cu, pos->tag.type); if (type == NULL) { tag__type_not_found(&pos->tag); - printed += fprintf(fp, "%.*s>>>ERROR: type for %s not " - "found!\n", cconf.indent, tabs, pos->name); + printed += fprintf(fp, "%.*s>>>ERROR: type(%#llx) for %s not " + "found!\n", cconf.indent, tabs, + (unsigned long long)pos->tag.type, + pos->name); continue; } @@ -2513,7 +2518,12 @@ int cus__loadfl(struct cus *self, struct argp *argp, int argc, char *argv[]) * for the magic types they support or just pass them the file, * unloading the shared object if it says its not the type they * support. + * FIXME: for now we just check if no DWARF info was found + * by looking at the list of CUs found: */ + if (list_empty(&self->cus)) + err = ctf__load(self, argp, argc, argv); + return err; } diff --git a/libctf.c b/libctf.c new file mode 100644 index 0000000..ed278eb --- /dev/null +++ b/libctf.c @@ -0,0 +1,176 @@ +#include +#include +#include +#include +#include + +#include "libctf.h" +#include "ctf.h" + +struct Ctf { + void *buf; + size_t size; + int swapped; +}; + +u_int16_t ctf_get16(Ctf *cp, u_int16_t *p) +{ + u_int16_t val = *p; + + if (cp->swapped) + val = ((val >> 8) | (val << 8)); + return val; +} + +u_int32_t ctf_get32(Ctf *cp, u_int32_t *p) +{ + u_int32_t val = *p; + + if (cp->swapped) + val = ((val >> 24) | + ((val >> 8) & 0x0000ff00) | + ((val << 8) & 0x00ff0000) | + (val << 24)); + return val; +} + +void ctf_put16(Ctf *cp, u_int16_t *p, u_int16_t val) +{ + if (cp->swapped) + val = ((val >> 8) | (val << 8)); + *p = val; +} + +void ctf_put32(Ctf *cp, u_int32_t *p, u_int32_t val) +{ + if (cp->swapped) + val = ((val >> 24) | + ((val >> 8) & 0x0000ff00) | + ((val << 8) & 0x00ff0000) | + (val << 24)); + *p = val; +} + +static int decompress_ctf(struct Ctf *cp, void *orig_buf, size_t orig_size) +{ + struct ctf_header *hp = orig_buf; + const char *err_str; + z_stream state; + size_t len; + void *new; + + len = (ctf_get32(cp, &hp->ctf_str_off) + + ctf_get32(cp, &hp->ctf_str_len)); + new = malloc(len + sizeof(*hp)); + if (!new) { + fprintf(stderr, "CTF decompression allocation failure.\n"); + return -ENOMEM; + } + memcpy(new, hp, sizeof(*hp)); + + memset(&state, 0, sizeof(state)); + state.next_in = (Bytef *) (hp + 1); + state.avail_in = orig_size - sizeof(*hp); + state.next_out = new + sizeof(*hp); + state.avail_out = len; + + if (inflateInit(&state) != Z_OK) { + err_str = "Ctf decompression inflateInit failure."; + goto err; + } + + if (inflate(&state, Z_FINISH) != Z_STREAM_END) { + err_str = "Ctf decompression inflate failure."; + goto err; + } + + if (inflateEnd(&state) != Z_OK) { + err_str = "Ctf decompression inflateEnd failure."; + goto err; + } + + if (state.total_out != len) { + err_str = "Ctf decompression truncation error."; + goto err; + } + + cp->buf = new; + cp->size = len + sizeof(*hp); + + return 0; + +err: + fputs(err_str, stderr); + free(new); + return -EINVAL; +} + +Ctf *ctf_begin(void *orig_buf, size_t orig_size) +{ + struct ctf_header *hp = orig_buf; + struct Ctf *cp; + int swapped; + + if (hp->ctf_magic == CTF_MAGIC) + swapped = 0; + else if (hp->ctf_magic == CTF_MAGIC_SWAP) + swapped = 1; + else { + fprintf(stderr, "Bad CTF magic %04x.\n", hp->ctf_magic); + return NULL; + } + + if (hp->ctf_version != CTF_VERSION) { + fprintf(stderr, "Bad CTF version %u, expected %u.\n", + hp->ctf_version, CTF_VERSION); + return NULL; + } + + cp = malloc(sizeof(*cp)); + if (!cp) { + fprintf(stderr, "Ctf allocation failure.\n"); + return NULL; + } + + memset(cp, 0, sizeof(*cp)); + cp->swapped = swapped; + + if (!(hp->ctf_flags & CTF_FLAGS_COMPR)) { + cp->buf = malloc(orig_size); + if (!cp->buf) { + fprintf(stderr, "Ctf buffer allocation failure.\n"); + free(cp); + return NULL; + } + memcpy(cp->buf, orig_buf, orig_size); + cp->size = orig_size; + + return cp; + } else { + int err = decompress_ctf(cp, orig_buf, orig_size); + + if (err) { + fprintf(stderr, "Ctf decompression failure.\n"); + free(cp); + return NULL; + } + } + + return cp; +} + +void ctf_end(Ctf *cp) +{ + free(cp->buf); + free(cp); +} + +void *ctf_get_buffer(Ctf *cp) +{ + return cp->buf; +} + +size_t ctf_get_size(Ctf *cp) +{ + return cp->size; +} diff --git a/libctf.h b/libctf.h new file mode 100644 index 0000000..8c2bd23 --- /dev/null +++ b/libctf.h @@ -0,0 +1,19 @@ +#ifndef _LIBCTF_H +#define _LIBCTF_H + +#include + +typedef struct Ctf Ctf; + +extern Ctf *ctf_begin(void *buf, size_t size); +extern void ctf_end(Ctf *ctf); + +extern u_int16_t ctf_get16(Ctf *cp, u_int16_t *p); +extern u_int32_t ctf_get32(Ctf *cp, u_int32_t *p); +extern void ctf_put16(Ctf *cp, u_int16_t *p, u_int16_t val); +extern void ctf_put32(Ctf *cp, u_int32_t *p, u_int32_t val); + +extern void *ctf_get_buffer(Ctf *cp); +extern size_t ctf_get_size(Ctf *cp); + +#endif /* _LIBCTF_H */