X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=dwarf_prototypes.c;h=9c369048ef5aa07ab34a73cbef764d520a1056ea;hb=refs%2Fheads%2Ftizen_5.5;hp=f76dbedbfb24d0d56769f27e46893cc680dee022;hpb=43420821a97063ec1617466654611d2d86631975;p=platform%2Fupstream%2Fltrace.git diff --git a/dwarf_prototypes.c b/dwarf_prototypes.c index f76dbed..9c36904 100644 --- a/dwarf_prototypes.c +++ b/dwarf_prototypes.c @@ -24,61 +24,30 @@ #include "library.h" #include "options.h" #include "filter.h" +#include "debug.h" +#define complain(die, format, ...) \ + debug(DEBUG_FUNCTION, "%s() die '%s' @ 0x%lx: " format, \ + __func__, dwarf_diename(die), dwarf_dieoffset(die), \ + ##__VA_ARGS__) -//#define DUMP_PROTOTYPES - -#if 1 -#define complain( die, format, ... ) \ - fprintf(stderr, "%s() die '%s' @ 0x%lx: " format "\n", \ - __func__, dwarf_diename(die), dwarf_dieoffset(die), \ - ##__VA_ARGS__ ) -#else -#define complain( die, format, ... ) -#endif - -#define NEXT_SIBLING(die) \ - int res = dwarf_siblingof(die, die); \ +#define NEXT_SIBLING(die) \ + int res = dwarf_siblingof(die, die); \ if (res == 0) continue; /* sibling exists */ \ if (res < 0) return false; /* error */ \ break /* no sibling exists */ -// A map from DIE addresses (Dwarf_Off) to type structures (struct -// arg_type_info*). This is created and filled in at the start of each import, -// and deleted when the import is complete -static struct dict type_hash; - - -static bool get_type(struct arg_type_info** info, Dwarf_Die* type_die); - - -#if 0 -static bool _dump_dwarf_tree(Dwarf_Die* die, int indent) -{ - while (1) { - fprintf(stderr, "%*sprocessing unit: 0x%02x/'%s'\n", indent*4, "", - dwarf_tag(die), dwarf_diename(die)); - - Dwarf_Die child; - if (dwarf_child(die, &child) == 0) { - if (!_dump_dwarf_tree(&child, indent+1)) - return false; - } +static struct arg_type_info *get_type(int *newly_allocated_info, + Dwarf_Die *type_die, + struct protolib *plib, + struct dict *type_dieoffset_hash); - SIBLING(die); - } - return true; -} -static bool dump_dwarf_tree(Dwarf_Die* die) -{ - return _dump_dwarf_tree( die, 0 ); -} -#endif +// debugging functions to dump types that I already parsed #ifdef DUMP_PROTOTYPES -static bool _dump_ltrace_tree(const struct arg_type_info* info, int indent) +static bool _dump_ltrace_tree(const struct arg_type_info *info, int indent) { if (indent > 7) { fprintf(stderr, "%*s%p ...\n", indent*4, "", (void*)info); @@ -108,38 +77,41 @@ static bool _dump_ltrace_tree(const struct arg_type_info* info, int indent) break; case ARGTYPE_ARRAY: - fprintf(stderr, "%*s%p array. elements not printed\n", indent*4, "", - (void*)info); + fprintf(stderr, "%*s%p array. elements not printed\n", indent*4, + "", (void*)info); break; case ARGTYPE_POINTER: - fprintf(stderr, "%*s%p pointer to...\n", indent*4, "", (void*)info); - _dump_ltrace_tree( info->u.ptr_info.info, indent+1 ); + fprintf(stderr, "%*s%p pointer to...\n", indent*4, + "", (void*)info); + _dump_ltrace_tree(info->u.ptr_info.info, indent+1); break; case ARGTYPE_STRUCT: - fprintf(stderr, "%*s%p struct...\n", indent*4, "", (void*)info); + fprintf(stderr, "%*s%p struct...\n", indent*4, + "", (void*)info); struct struct_field { struct arg_type_info *info; int own_info; - }* elements = (struct struct_field*)info->u.entries.data; + } *elements = (struct struct_field*)info->u.entries.data; unsigned int i; for(i=0; iu.entries.size; i++) - _dump_ltrace_tree( elements[i].info, indent+1 ); + _dump_ltrace_tree(elements[i].info, indent+1); break; default: - fprintf(stderr, "%*s%p unknown type\n", indent*4, "", (void*)info); + fprintf(stderr, "%*s%p unknown type\n", indent*4, + "", (void*)info); return false;; } return true; } -static bool dump_ltrace_tree(const struct arg_type_info* info) +static bool dump_ltrace_tree(const struct arg_type_info *info) { - return _dump_ltrace_tree( info, 0 ); + return _dump_ltrace_tree(info, 0); } #endif @@ -147,16 +119,16 @@ static bool dump_ltrace_tree(const struct arg_type_info* info) // pulls a numerical value out of a particular attribute in a die. Returns true // if successful. The result is returned in *result. Note that this is cast to // (uint64_t), regardless of the actual type of the input -static bool get_die_numeric(uint64_t* result, - Dwarf_Die *die, unsigned int attr_name) +static bool get_die_numeric(uint64_t *result, + Dwarf_Die *die, unsigned int attr_name) { Dwarf_Attribute attr ; union { - Dwarf_Word udata; - Dwarf_Sword sdata; - Dwarf_Addr addr; - bool flag; + Dwarf_Word udata; + Dwarf_Sword sdata; + Dwarf_Addr addr; + bool flag; } u; if (dwarf_attr(die, attr_name, &attr) == NULL) @@ -164,10 +136,10 @@ static bool get_die_numeric(uint64_t* result, unsigned int form = dwarf_whatform(&attr); -#define PROCESS_NUMERIC(type) \ +#define PROCESS_NUMERIC(type) \ if (dwarf_form ## type(&attr, &u.type) != 0) \ - return false; \ - *result = (uint64_t)u.type; \ + return false; \ + *result = (uint64_t)u.type; \ return true @@ -189,40 +161,47 @@ static bool get_die_numeric(uint64_t* result, PROCESS_NUMERIC(flag); default: - complain(die, "Unknown numeric form %d for attr_name: %d", form, attr_name); + complain(die, "Unknown numeric form %d for attr_name: %d", + form, attr_name); return false; } #undef PROCESS_NUMERIC } -static bool get_integer_base_type(enum arg_type* type, int byte_size, bool is_signed) +static bool get_integer_base_type(enum arg_type *type, int byte_size, + bool is_signed) { - switch (byte_size) { - case sizeof(char): + // not using a switch() here because sizeof(int) == sizeof(long) on some + // architectures, and removing that case for those arches is a pain + if (byte_size == sizeof(char)) { *type = ARGTYPE_CHAR; return true; + } - case sizeof(short): + if (byte_size == sizeof(short)) { *type = is_signed ? ARGTYPE_SHORT : ARGTYPE_USHORT; return true; + } - case sizeof(int): + if (byte_size == sizeof(int)) { *type = is_signed ? ARGTYPE_INT : ARGTYPE_UINT; return true; + } - case sizeof(long): + if (byte_size == sizeof(long)) { *type = is_signed ? ARGTYPE_LONG : ARGTYPE_ULONG; return true; - - default: - return false; } + + return false; } -static enum arg_type get_base_type(Dwarf_Die* die) +// returns an ltrace ARGTYPE_XXX base type from the given die. If we dont +// support a particular type (or an error occurred), I regturn ARGTYPE_VOID +static enum arg_type get_base_type(Dwarf_Die *die) { - int64_t encoding; - if( !get_die_numeric((uint64_t*)&encoding, die, DW_AT_encoding)) + uint64_t encoding; + if (!get_die_numeric(&encoding, die, DW_AT_encoding)) return ARGTYPE_VOID; if (encoding == DW_ATE_void) @@ -231,19 +210,20 @@ static enum arg_type get_base_type(Dwarf_Die* die) if (encoding == DW_ATE_signed_char || encoding == DW_ATE_unsigned_char) return ARGTYPE_CHAR; - uint64_t byte_size; - if (!get_die_numeric(&byte_size, die, DW_AT_byte_size)) - return ARGTYPE_VOID; + uint64_t byte_size; + if (!get_die_numeric(&byte_size, die, DW_AT_byte_size)) + return ARGTYPE_VOID; if (encoding == DW_ATE_signed || - encoding == DW_ATE_unsigned || - encoding == DW_ATE_boolean) { + encoding == DW_ATE_unsigned || + encoding == DW_ATE_boolean) { bool is_signed = (encoding == DW_ATE_signed); enum arg_type type; - if(!get_integer_base_type(&type, (int)byte_size, is_signed)) { - complain(die, "Unknown integer base type. Using 'void'"); + if (!get_integer_base_type(&type, (int)byte_size, is_signed)) { + complain(die, "Unknown integer base type. " + "Using 'void'"); return ARGTYPE_VOID; } return type; @@ -258,8 +238,8 @@ static enum arg_type get_base_type(Dwarf_Die* die) return ARGTYPE_DOUBLE; default: - // things like long doubles. ltrace has no support yet, so I just - // say "void" + // things like long doubles. ltrace has no support yet, + // so I just say "void" return ARGTYPE_VOID; } } @@ -274,8 +254,8 @@ static enum arg_type get_base_type(Dwarf_Die* die) return ARGTYPE_DOUBLE; default: - // things like long doubles. ltrace has no support yet, so I just - // say "void" + // things like long doubles. ltrace has no support yet, + // so I just say "void" return ARGTYPE_VOID; } } @@ -286,7 +266,7 @@ static enum arg_type get_base_type(Dwarf_Die* die) return ARGTYPE_VOID; } -static bool get_type_die(Dwarf_Die* type_die, Dwarf_Die* die) +static bool get_type_die(Dwarf_Die *type_die, Dwarf_Die *die) { Dwarf_Attribute attr; return @@ -294,349 +274,579 @@ static bool get_type_die(Dwarf_Die* type_die, Dwarf_Die* die) dwarf_formref_die(&attr, type_die) != NULL; } -static size_t dwarf_die_hash(const void* x) + + +// type_dieoffset_hash dictionary callbacks +static size_t dwarf_die_hash(const void *x) { return *(const Dwarf_Off*)x; } -static int dwarf_die_eq(const void* a, const void* b) +static int dwarf_die_eq(const void *a, const void *b) { return *(const Dwarf_Off*)a == *(const Dwarf_Off*)b; } -static bool get_enum(struct arg_type_info* enum_info, Dwarf_Die* parent) + +// returns a newly-allocated art_type_info*, or NULL on error +static struct arg_type_info *get_enum(Dwarf_Die *parent, + struct dict *type_dieoffset_hash) { + +#define CLEANUP_AND_RETURN_ERROR(ret) do { \ + if (dupkey != NULL) \ + free((void*)dupkey); \ + if (value != NULL) { \ + value_destroy(value); \ + free(value); \ + } \ + if (lens != NULL) { \ + lens_destroy(&lens->super); \ + free(lens); \ + } \ + if (result != NULL) { \ + type_destroy(result); \ + free(result); \ + } \ + dict_erase (type_dieoffset_hash, &die_offset, NULL, \ + NULL, NULL); \ + dict_insert(type_dieoffset_hash, &die_offset, \ + &(struct arg_type_info*){ \ + type_get_simple(ARGTYPE_VOID)}); \ + return ret; \ + } while (0) + + struct arg_type_info *result = NULL; + struct enum_lens *lens = NULL; + const char *dupkey = NULL; + struct value *value = NULL; + + Dwarf_Off die_offset = dwarf_dieoffset(parent); + + result = calloc(1, sizeof(struct arg_type_info)); + if (result == NULL) { + complain(parent, "alloc error"); + CLEANUP_AND_RETURN_ERROR(NULL); + } + + if (dict_insert(type_dieoffset_hash, &die_offset, &result) != 0) { + complain(parent, "Couldn't insert into cache dict"); + CLEANUP_AND_RETURN_ERROR(NULL); + } + uint64_t byte_size; if (!get_die_numeric(&byte_size, parent, DW_AT_byte_size)) { // No byte size given, assume 'int' - enum_info->type = ARGTYPE_INT; + result->type = ARGTYPE_INT; } else { - if(!get_integer_base_type(&enum_info->type, (int)byte_size, true)) { - complain(parent, "Unknown integer base type. Using 'int'"); - enum_info->type = ARGTYPE_INT; + if (!get_integer_base_type(&result->type, + (int)byte_size, true)) { + complain(parent, "Unknown integer base type. " + "Using 'int'"); + result->type = ARGTYPE_INT; } } - struct enum_lens *lens = calloc(1, sizeof(struct enum_lens)); + lens = calloc(1, sizeof(struct enum_lens)); if (lens == NULL) { complain(parent, "alloc error"); - return false; + CLEANUP_AND_RETURN_ERROR(NULL); } lens_init_enum(lens); - enum_info->lens = &lens->super; + result->lens = &lens->super; + result->own_lens = 1; Dwarf_Die die; if (dwarf_child(parent, &die) != 0) { // empty enum. we're done - return true; + CLEANUP_AND_RETURN_ERROR(NULL); } - while(1) { + + while (1) { complain(&die, "enum element: 0x%02x/'%s'", dwarf_tag(&die), - dwarf_diename(&die)); + dwarf_diename(&die)); + + dupkey = NULL; + value = NULL; if (dwarf_tag(&die) != DW_TAG_enumerator) { - complain(&die, "Enums can have ONLY DW_TAG_enumerator elements"); - return false; + complain(&die, "Enums can have ONLY DW_TAG_enumerator " + "elements"); + CLEANUP_AND_RETURN_ERROR(NULL); } if (!dwarf_hasattr(&die, DW_AT_const_value)) { - complain(&die, "Enums MUST have DW_AT_const_value values"); - return false; + complain(&die, "Enums MUST have DW_AT_const_value " + "values"); + CLEANUP_AND_RETURN_ERROR(NULL); } - const char* key = dwarf_diename(&die); + const char *key = dwarf_diename(&die); if (key == NULL) { complain(&die, "Enums must have a DW_AT_name key"); - return false; + CLEANUP_AND_RETURN_ERROR(NULL); } - const char* dupkey = strdup(key); + dupkey = strdup(key); if (dupkey == NULL) { complain(&die, "Couldn't duplicate enum key"); - return false; + CLEANUP_AND_RETURN_ERROR(NULL); } - struct value* value = calloc( 1, sizeof(struct value)); + value = calloc(1, sizeof(struct value)); if (value == NULL) { complain(&die, "Couldn't alloc enum value"); - return false; + CLEANUP_AND_RETURN_ERROR(NULL); } - value_init_detached(value, NULL, type_get_simple( enum_info->type ), 0); + value_init_detached(value, NULL, type_get_simple(result->type), + 0); uint64_t enum_value; if (!get_die_numeric(&enum_value, &die, DW_AT_const_value)) { complain(&die, "Couldn't get enum value"); - return false; + CLEANUP_AND_RETURN_ERROR(NULL); } value_set_word(value, (long)enum_value); - if (lens_enum_add( lens, dupkey, 0, value, 0 )) { + if (lens_enum_add(lens, dupkey, 1, value, 1)) { complain(&die, "Couldn't add enum element"); - return false; + CLEANUP_AND_RETURN_ERROR(NULL); } NEXT_SIBLING(&die); } - return true; + return result; + +#undef CLEANUP_AND_RETURN_ERROR } -static bool get_array(struct arg_type_info* array_info, Dwarf_Die* parent) +// returns a newly-allocated art_type_info*, or NULL on error +static struct arg_type_info *get_array(Dwarf_Die *parent, + struct protolib *plib, + struct dict *type_dieoffset_hash) { + +#define CLEANUP_AND_RETURN_ERROR(ret) do { \ + if (length != NULL) { \ + expr_destroy(length); \ + free(length); \ + } \ + if (array_type != NULL && newly_allocated_array_type) { \ + type_destroy(array_type); \ + free(array_type); \ + } \ + if (result != NULL) { \ + type_destroy(result); \ + free(result); \ + } \ + dict_erase (type_dieoffset_hash, &die_offset, \ + NULL, NULL, NULL); \ + dict_insert(type_dieoffset_hash, &die_offset, \ + &(struct arg_type_info*){ \ + type_get_simple(ARGTYPE_VOID)}); \ + return ret; \ + } while (0) + + + struct arg_type_info *result = NULL; + struct expr_node *length = NULL; + struct arg_type_info *array_type = NULL; + int newly_allocated_array_type = 0; + + Dwarf_Off die_offset = dwarf_dieoffset(parent); + + result = calloc(1, sizeof(struct arg_type_info)); + if (result == NULL) { + complain(parent, "alloc error"); + CLEANUP_AND_RETURN_ERROR(NULL); + } + Dwarf_Die type_die; - if (!get_type_die( &type_die, parent )) { - complain( parent, "Array has unknown type" ); - return false; + if (!get_type_die(&type_die, parent)) { + complain(parent, "Array has unknown type"); + CLEANUP_AND_RETURN_ERROR(NULL); } - struct arg_type_info* info; - if (!get_type( &info, &type_die )) { - complain( parent, "Couldn't figure out array's type" ); - return false; + if (dict_insert(type_dieoffset_hash, &die_offset, &result) != 0) { + complain(parent, "Couldn't insert into cache dict"); + CLEANUP_AND_RETURN_ERROR(NULL); + } + array_type = get_type(&newly_allocated_array_type, + &type_die, plib, type_dieoffset_hash); + if (array_type == NULL) { + complain(parent, "Couldn't figure out array's type"); + CLEANUP_AND_RETURN_ERROR(NULL); } Dwarf_Die subrange; if (dwarf_child(parent, &subrange) != 0) { complain(parent, - "Array must have a DW_TAG_subrange_type child, but has none"); - return false; + "Array must have a DW_TAG_subrange_type child, " + "but has none"); + CLEANUP_AND_RETURN_ERROR(NULL); } Dwarf_Die next_subrange; if (dwarf_siblingof(&subrange, &next_subrange) <= 0) { complain(parent, - "Array must have exactly one DW_TAG_subrange_type child"); - return false; + "Array must have exactly one DW_TAG_subrange_type " + "child"); + CLEANUP_AND_RETURN_ERROR(NULL); } if (dwarf_hasattr(&subrange, DW_AT_lower_bound)) { uint64_t lower_bound; - if (!get_die_numeric(&lower_bound, &subrange, DW_AT_lower_bound)) { - complain( parent, "Couldn't read lower bound"); - return false; + if (!get_die_numeric(&lower_bound, &subrange, + DW_AT_lower_bound)) { + complain(parent, "Couldn't read lower bound"); + CLEANUP_AND_RETURN_ERROR(NULL); } if (lower_bound != 0) { - complain( parent, - "Array subrange has a nonzero lower bound. Don't know what to do"); - return false; + complain(parent, + "Array subrange has a nonzero lower bound. " + "Don't know what to do"); + CLEANUP_AND_RETURN_ERROR(NULL); } } uint64_t N; if (!dwarf_hasattr(&subrange, DW_AT_upper_bound)) { - // no upper bound is defined. This is probably a variable-width array, - // and I don't know how long it is. Let's say 0 to be safe + // no upper bound is defined. This is probably a variable-width + // array, and I don't know how long it is. Let's say 0 to be + // safe N = 0; } else { if (!get_die_numeric(&N, &subrange, DW_AT_upper_bound)) { - complain( parent, "Couldn't read upper bound"); - return false; + complain(parent, "Couldn't read upper bound"); + CLEANUP_AND_RETURN_ERROR(NULL); } N++; } - // I'm not checking the subrange type. It should be some sort of integer, - // and I don't know what it would mean for it to be something else - - struct value* value = calloc( 1, sizeof(struct value)); - if (value == NULL) { - complain(&subrange, "Couldn't alloc length value"); - return false; - } - value_init_detached(value, NULL, type_get_simple( ARGTYPE_INT ), 0); - value_set_word(value, N ); - - struct expr_node* length = calloc( 1, sizeof(struct expr_node)); + // I'm not checking the subrange type. It should be some sort of + // integer, and I don't know what it would mean for it to be something + // else + length = calloc(1, sizeof(struct expr_node)); if (length == NULL) { complain(&subrange, "Couldn't alloc length expr"); - return false; + CLEANUP_AND_RETURN_ERROR(NULL); } - expr_init_const(length, value); + expr_init_const_word(length, N, type_get_simple(ARGTYPE_INT), 0); - type_init_array(array_info, info, 0, length, 0 ); + type_init_array(result, array_type, newly_allocated_array_type, + length, 1); + return result; - return true; +#undef CLEANUP_AND_RETURN_ERROR } -static bool get_structure(struct arg_type_info* struct_info, Dwarf_Die* parent) +// returns a newly-allocated art_type_info*, or NULL on error +static struct arg_type_info *get_structure(Dwarf_Die *parent, + struct protolib *plib, + struct dict *type_dieoffset_hash) { - type_init_struct(struct_info); + +#define CLEANUP_AND_RETURN_ERROR(ret) do { \ + if (member_type != NULL && newly_allocated_member_type) { \ + type_destroy(member_type); \ + free(member_type); \ + } \ + if (result != NULL) { \ + type_destroy(result); \ + free(result); \ + } \ + dict_erase (type_dieoffset_hash, &die_offset, \ + NULL, NULL, NULL); \ + dict_insert(type_dieoffset_hash, &die_offset, \ + &(struct arg_type_info*){ \ + type_get_simple(ARGTYPE_VOID)}); \ + return ret; \ + } while (0) + + + struct arg_type_info *result = NULL; + struct arg_type_info *member_type = NULL; + int newly_allocated_member_type = 0; + + Dwarf_Off die_offset = dwarf_dieoffset(parent); + + result = calloc(1, sizeof(struct arg_type_info)); + if (result == NULL) { + complain(parent, "alloc error"); + CLEANUP_AND_RETURN_ERROR(NULL); + } + type_init_struct(result); + if (dict_insert(type_dieoffset_hash, &die_offset, &result) != 0) { + complain(parent, "Couldn't insert into cache dict"); + CLEANUP_AND_RETURN_ERROR(NULL); + } Dwarf_Die die; if (dwarf_child(parent, &die) != 0) { // no elements; we're done - return true; + return result; } - while(1) { + while (1) { + member_type = NULL; + newly_allocated_member_type = 0; + complain(&die, "member: 0x%02x", dwarf_tag(&die)); if (dwarf_tag(&die) != DW_TAG_member) { complain(&die, "Structure can have ONLY DW_TAG_member"); - return false; + CLEANUP_AND_RETURN_ERROR(NULL); } Dwarf_Die type_die; - if (!get_type_die( &type_die, &die )) { - complain( &die, "Couldn't get type of element"); - return false; + if (!get_type_die(&type_die, &die)) { + complain(&die, "Couldn't get type of element"); + CLEANUP_AND_RETURN_ERROR(NULL); } - struct arg_type_info* member_info = NULL; - if (!get_type( &member_info, &type_die )) { + member_type = get_type(&newly_allocated_member_type, + &type_die, plib, type_dieoffset_hash); + if (member_type == NULL) { complain(&die, "Couldn't parse type from DWARF data"); - return false; + CLEANUP_AND_RETURN_ERROR(NULL); + } + if (type_struct_add(result, member_type, + newly_allocated_member_type) != 0) { + complain(&die, "Couldn't add type to struct"); + CLEANUP_AND_RETURN_ERROR(NULL); } - type_struct_add( struct_info, member_info, 0 ); NEXT_SIBLING(&die); } - return true; + return result; +#undef CLEANUP_AND_RETURN_ERROR } -// Reads the type in the die into the given structure -// Returns true on sucess -static bool get_type(struct arg_type_info** info, Dwarf_Die* type_die) +// Reads the type in the die and returns the corresponding arg_type_info*. If +// this was newly allocated on the heap, *newly_allocated_info = true. If an +// error occurred, returns NULL +static struct arg_type_info *get_type(int *newly_allocated_result, + Dwarf_Die *type_die, + struct protolib *plib, + struct dict *type_dieoffset_hash) { + +#define CLEANUP_AND_RETURN_ERROR(ret) do { \ + if (pointee != NULL && newly_allocated_pointee) { \ + type_destroy(pointee); \ + free(pointee); \ + } \ + if (result != NULL && *newly_allocated_result) { \ + type_destroy(result); \ + free(result); \ + } \ + dict_erase (type_dieoffset_hash, &die_offset, \ + NULL, NULL, NULL); \ + dict_insert(type_dieoffset_hash, &die_offset, \ + &(struct arg_type_info*){ \ + type_get_simple(ARGTYPE_VOID)}); \ + return ret; \ + } while (0) + +#define DICT_INSERT_AND_CHECK(type_dieoffset_hash, die_offset, result) \ + do { \ + if (dict_insert(type_dieoffset_hash, \ + die_offset, result) != 0) { \ + complain(type_die, \ + "Couldn't insert into cache dict"); \ + CLEANUP_AND_RETURN_ERROR(NULL); \ + } \ + } while(0) + + struct arg_type_info *result = NULL; + struct arg_type_info *pointee = NULL; + int newly_allocated_pointee = 0; + Dwarf_Off die_offset = dwarf_dieoffset(type_die); - struct arg_type_info** found_type = dict_find(&type_hash, &die_offset ); + + + // by default, we say we allocated nothing. I set this to true later, + // when I allocate memory + *newly_allocated_result = 0; + + struct arg_type_info **found_type = dict_find(type_dieoffset_hash, + &die_offset); if (found_type != NULL) { - *info = *found_type; - complain(type_die, "Read pre-computed type: %p", *info); - return true; + complain(type_die, "Read pre-computed type"); + return *found_type; + } + + const char *type_name = dwarf_diename(type_die); + if (type_name != NULL) { + + struct named_type *already_defined_type = + protolib_lookup_type(plib, type_name, true); + + if (already_defined_type != NULL) { + complain(type_die, + "Type '%s' defined in a .conf file. " + "Using that instead of DWARF", + type_name); + return already_defined_type->info; + } } Dwarf_Die next_die; switch (dwarf_tag(type_die)) { case DW_TAG_base_type: - *info = type_get_simple( get_base_type( type_die )); - complain(type_die, "Storing base type: %p", *info); - dict_insert( &type_hash, &die_offset, info ); - return true; + complain(type_die, "Storing base type"); + result = type_get_simple(get_base_type(type_die)); + DICT_INSERT_AND_CHECK(type_dieoffset_hash, &die_offset, &result); + return result; case DW_TAG_subroutine_type: case DW_TAG_inlined_subroutine: - // function pointers are stored as void*. If ltrace tries to dereference - // these, it'll get a segfault - *info = type_get_simple( ARGTYPE_VOID ); - complain(type_die, "Storing subroutine type: %p", *info); - dict_insert( &type_hash, &die_offset, info ); - return true; + // function pointers are stored as void*. If ltrace tries to + // dereference these, it'll get a segfault + complain(type_die, "Storing subroutine type"); + result = type_get_simple(ARGTYPE_VOID); + DICT_INSERT_AND_CHECK(type_dieoffset_hash, &die_offset, &result); + return result; case DW_TAG_pointer_type: - - if (!get_type_die(&next_die, type_die )) { - // the pointed-to type isn't defined, so I report a void* - *info = type_get_simple( ARGTYPE_VOID ); - complain(type_die, "Storing void-pointer type: %p", *info); - dict_insert( &type_hash, &die_offset, info ); - return true; + if (!get_type_die(&next_die, type_die)) { + // the pointed-to type isn't defined, so I report a + // void* + complain(type_die, "Storing void-pointer type"); + result = type_get_voidptr(); + DICT_INSERT_AND_CHECK(type_dieoffset_hash, &die_offset, &result); + return result; } - *info = calloc( 1, sizeof(struct arg_type_info)); - if (*info == NULL) { + complain(type_die, "Storing pointer type"); + + *newly_allocated_result = 1; + result = calloc(1, sizeof(struct arg_type_info)); + if (result == NULL) { complain(type_die, "alloc error"); - return false; + CLEANUP_AND_RETURN_ERROR(NULL); } - type_init_pointer(*info, NULL, 0); + DICT_INSERT_AND_CHECK(type_dieoffset_hash, &die_offset, &result); + pointee = get_type(&newly_allocated_pointee, + &next_die, plib, type_dieoffset_hash); + if (pointee == NULL) + CLEANUP_AND_RETURN_ERROR(NULL); - complain(type_die, "Storing pointer type: %p", *info); - dict_insert( &type_hash, &die_offset, info ); - return get_type( &(*info)->u.ptr_info.info, &next_die ); + type_init_pointer(result, pointee, newly_allocated_pointee); + return result; case DW_TAG_structure_type: - *info = calloc( 1, sizeof(struct arg_type_info)); - if (*info == NULL) { - complain(type_die, "alloc error"); - return false; - } + complain(type_die, "Storing struct type"); + *newly_allocated_result = 1; - complain(type_die, "Storing struct type: %p", *info); - dict_insert( &type_hash, &die_offset, info ); - return get_structure( *info, type_die ); + result = get_structure(type_die, plib, type_dieoffset_hash); + if (result == NULL) + CLEANUP_AND_RETURN_ERROR(NULL); + return result; case DW_TAG_typedef: case DW_TAG_const_type: - case DW_TAG_volatile_type: { + case DW_TAG_volatile_type: // Various tags are simply pass-through, so I just keep going - bool res = true; - if (get_type_die(&next_die, type_die )) { - complain(type_die, "Storing const/typedef type: %p", *info); - res = get_type( info, &next_die ); + if (get_type_die(&next_die, type_die)) { + complain(type_die, "Storing const/typedef type"); + + result = get_type(newly_allocated_result, &next_die, + plib, type_dieoffset_hash); + if (result == NULL) + CLEANUP_AND_RETURN_ERROR(NULL); } else { - // no type. Use 'void'. Normally I'd think this is bogus, but stdio - // typedefs something to void - *info = type_get_simple( ARGTYPE_VOID ); - complain(type_die, "Storing void type: %p", *info); + // no type. Use 'void'. Normally I'd think this is + // bogus, but stdio typedefs something to void + result = type_get_simple(ARGTYPE_VOID); + complain(type_die, "Storing void type"); } - if (res) - dict_insert( &type_hash, &die_offset, info ); - return res; - } + DICT_INSERT_AND_CHECK(type_dieoffset_hash, &die_offset, &result); + return result; case DW_TAG_enumeration_type: - // We have an enumeration. This has type "int", but has a particular - // lens to handle the enum - *info = calloc( 1, sizeof(struct arg_type_info)); - if (*info == NULL) { - complain(type_die, "alloc error"); - return false; - } + // We have an enumeration. This has a base type, but has a + // particular lens to handle the enum + *newly_allocated_result = 1; - complain(type_die, "Storing enum int: %p", *info); - dict_insert( &type_hash, &die_offset, info ); - return get_enum( *info, type_die ); + complain(type_die, "Storing enum int"); + result = get_enum(type_die, type_dieoffset_hash); + if (result == NULL) + CLEANUP_AND_RETURN_ERROR(NULL); + return result; case DW_TAG_array_type: - *info = calloc( 1, sizeof(struct arg_type_info)); - if (*info == NULL) { - complain(type_die, "alloc error"); - return false; - } + *newly_allocated_result = 1; - complain(type_die, "Storing array: %p", *info); - dict_insert( &type_hash, &die_offset, info ); - return get_array( *info, type_die ); + complain(type_die, "Storing array"); + result = get_array(type_die, plib, type_dieoffset_hash); + if (result == NULL) + CLEANUP_AND_RETURN_ERROR(NULL); + return result; case DW_TAG_union_type: - *info = type_get_simple( ARGTYPE_VOID ); - complain(type_die, "Storing union-as-void type: %p", *info); - return true; + result = type_get_simple(ARGTYPE_VOID); + complain(type_die, "Storing union-as-void type"); + DICT_INSERT_AND_CHECK(type_dieoffset_hash, &die_offset, &result); + return result; default: - complain(type_die, "Unknown type tag 0x%x", dwarf_tag(type_die)); - break; + complain(type_die, "Unknown type tag 0x%x. Returning void", + dwarf_tag(type_die)); + result = type_get_simple(ARGTYPE_VOID); + DICT_INSERT_AND_CHECK(type_dieoffset_hash, &die_offset, &result); + return result; } - return false; +#undef DICT_INSERT_AND_CHECK +#undef CLEANUP_AND_RETURN_ERROR } -static bool get_prototype(struct prototype* proto, Dwarf_Die* subroutine) +// fills in *proto with a prototype. Returns true on success +static bool get_prototype(struct prototype *result, + Dwarf_Die *subroutine, struct protolib *plib, + struct dict *type_dieoffset_hash) { - // First, look at the return type. This is stored in a DW_AT_type tag in the - // subroutine DIE. If there is no such tag, this function returns void + +#define CLEANUP_AND_RETURN_ERROR(ret) do { \ + if (argument_type != NULL && newly_allocated_argument_type) { \ + type_destroy(argument_type); \ + free(argument_type); \ + } \ + prototype_destroy(result); \ + return ret; \ + } while (0) + + + struct arg_type_info *argument_type = NULL; + int newly_allocated_argument_type = 0; + + prototype_init(result); + + // First, look at the return type. This is stored in a DW_AT_type tag in + // the subroutine DIE. If there is no such tag, this function returns + // void Dwarf_Die return_type_die; - if (!get_type_die(&return_type_die, subroutine )) { - proto->return_info = type_get_simple( ARGTYPE_VOID ); - proto->own_return_info = 0; + if (!get_type_die(&return_type_die, subroutine)) { + result->return_info = type_get_simple(ARGTYPE_VOID); + result->own_return_info = 0; } else { - proto->return_info = calloc( 1, sizeof( struct arg_type_info )); - if (proto->return_info == NULL) { - complain(subroutine, "Couldn't alloc return type"); - return false; - } - proto->own_return_info = 0; - - if (!get_type( &proto->return_info, &return_type_die )) { + int newly_allocated_return_type; + result->return_info = get_type(&newly_allocated_return_type, + &return_type_die, plib, + type_dieoffset_hash); + if (result->return_info == NULL) { complain(subroutine, "Couldn't get return type"); - return false; + CLEANUP_AND_RETURN_ERROR(false); } + result->own_return_info = newly_allocated_return_type; } @@ -647,33 +857,43 @@ static bool get_prototype(struct prototype* proto, Dwarf_Die* subroutine) return true; } - while(1) { + while (1) { if (dwarf_tag(&arg_die) == DW_TAG_formal_parameter) { complain(&arg_die, "arg: 0x%02x", dwarf_tag(&arg_die)); + argument_type = NULL; + newly_allocated_argument_type = false; + Dwarf_Die type_die; - if (!get_type_die(&type_die, &arg_die )) { - complain(&arg_die, "Couldn't get the argument type die"); - return false; + if (!get_type_die(&type_die, &arg_die)) { + complain(&arg_die, "Couldn't get the argument " + "type die"); + CLEANUP_AND_RETURN_ERROR(false); } - struct arg_type_info* arg_type_info = NULL; - if (!get_type( &arg_type_info, &type_die )) { - complain(&arg_die, "Couldn't parse arg type from DWARF data"); - return false; + + argument_type = get_type(&newly_allocated_argument_type, + &type_die, plib, + type_dieoffset_hash); + if (argument_type==NULL) { + complain(&arg_die, "Couldn't parse arg " + "type from DWARF data"); + CLEANUP_AND_RETURN_ERROR(false); } struct param param; - param_init_type(¶m, arg_type_info, 0); - if (prototype_push_param(proto, ¶m) <0) { - complain(&arg_die, "couldn't add argument to the prototype"); - return false; + param_init_type(¶m, argument_type, + newly_allocated_argument_type); + if (prototype_push_param(result, ¶m) <0) { + complain(&arg_die, "couldn't add argument to " + "the prototype"); + CLEANUP_AND_RETURN_ERROR(false); } #ifdef DUMP_PROTOTYPES fprintf(stderr, "Adding argument:\n"); - dump_ltrace_tree(arg_type_info); + dump_ltrace_tree(argument_type); #endif } @@ -681,62 +901,82 @@ static bool get_prototype(struct prototype* proto, Dwarf_Die* subroutine) } return true; +#undef CLEANUP_AND_RETURN_ERROR } -static bool import_subprogram(struct protolib* plib, struct library* lib, - Dwarf_Die* die) +static bool import_subprogram_name(struct protolib *plib, struct library *lib, + struct dict *type_dieoffset_hash, + Dwarf_Die *die, const char* function_name) { - // I use the linkage function name if there is one, otherwise the - // plain name - const char* function_name = NULL; - Dwarf_Attribute attr; - if (dwarf_attr(die, DW_AT_linkage_name, &attr) != NULL) - function_name = dwarf_formstring(&attr); - if (function_name == NULL) - function_name = dwarf_diename(die); - if (function_name == NULL) { - complain(die, "Function has no name. Not importing" ); + if (!filter_matches_symbol(options.plt_filter, function_name, lib) && + !filter_matches_symbol(options.static_filter, function_name, lib) && + !filter_matches_symbol(options.export_filter, function_name, lib)) { + complain(die, "Prototype not requested by any filter"); return true; } - complain(die, "subroutine_type: 0x%02x; function '%s'", - dwarf_tag(die), function_name); + dwarf_tag(die), function_name); - struct prototype* proto = - protolib_lookup_prototype(plib, function_name, false ); + struct prototype *proto_already_there = + protolib_lookup_prototype(plib, function_name, false); - if (proto != NULL) { + if (proto_already_there != NULL) { complain(die, "Prototype already exists. Skipping"); return true; } - if (!filter_matches_symbol(options.plt_filter, function_name, lib) && - !filter_matches_symbol(options.static_filter, function_name, lib) && - !filter_matches_symbol(options.export_filter, function_name, lib)) { - complain(die, "Prototype not requested by any filter"); - return true; + struct prototype proto; + if (!get_prototype(&proto, die, plib, type_dieoffset_hash)) { + complain(die, "couldn't get prototype"); + return false; } - proto = malloc(sizeof(struct prototype)); - if (proto == NULL) { - complain(die, "couldn't alloc prototype"); + const char *function_name_dup = strdup(function_name); + if (function_name_dup == NULL) { + complain(die, "couldn't strdup"); + prototype_destroy(&proto); return false; } - prototype_init( proto ); + protolib_add_prototype(plib, function_name_dup, 1, &proto); + return true; +} - if (!get_prototype(proto, die )) { - complain(die, "couldn't get prototype"); +static bool import_subprogram_die(struct protolib *plib, struct library *lib, + struct dict *type_dieoffset_hash, + Dwarf_Die *die) +{ + // If there is a linkage name, I use it (this is required for C++ code, + // in particular). + // + // I use the plain name regardless, since sometimes the exported symbol + // corresponds to the plain name, NOT the linkage name. For instance I + // see this on my Debian/sid amd64 box. In its libc, the linkage name of + // __nanosleep is __GI___nanosleep, but the export is __nanosleep + const char *function_name; + Dwarf_Attribute attr; + + if (dwarf_attr(die, DW_AT_linkage_name, &attr) != NULL && + (function_name = dwarf_formstring(&attr)) != NULL && + !import_subprogram_name(plib, lib, type_dieoffset_hash, die, + function_name)) { + return false; + } + + if ((function_name = dwarf_diename(die)) != NULL && + !import_subprogram_name(plib, lib, type_dieoffset_hash, die, + function_name)) { return false; } - protolib_add_prototype(plib, function_name, 0, proto); return true; } -static bool process_die_compileunit(struct protolib* plib, struct library* lib, - Dwarf_Die* parent) +static bool process_die_compileunit(struct protolib *plib, struct library *lib, + struct dict *type_dieoffset_hash, + Dwarf_Die *parent) { + complain(parent, "Processing compile unit"); Dwarf_Die die; if (dwarf_child(parent, &die) != 0) { // no child nodes, so nothing to do @@ -745,8 +985,10 @@ static bool process_die_compileunit(struct protolib* plib, struct library* lib, while (1) { if (dwarf_tag(&die) == DW_TAG_subprogram) - if(!import_subprogram(plib, lib, &die)) - return false; + if (!import_subprogram_die(plib, lib, type_dieoffset_hash, + &die)) + complain(&die, "Error importing subprogram. " + "Skipping"); NEXT_SIBLING(&die); } @@ -754,55 +996,55 @@ static bool process_die_compileunit(struct protolib* plib, struct library* lib, return true; } -static bool import(struct protolib* plib, struct library* lib, Dwfl* dwfl) +static void import(struct protolib *plib, struct library *lib, + Dwfl_Module *dwfl_module) { - dict_init(&type_hash, sizeof(Dwarf_Off), sizeof(struct arg_type_info*), - dwarf_die_hash, dwarf_die_eq, NULL ); + // A map from DIE addresses (Dwarf_Off) to type structures (struct + // arg_type_info*). This is created and filled in at the start of each + // import, and deleted when the import is complete + struct dict type_dieoffset_hash; + + dict_init(&type_dieoffset_hash, sizeof(Dwarf_Off), + sizeof(struct arg_type_info*), + dwarf_die_hash, dwarf_die_eq, NULL); Dwarf_Addr bias; - Dwarf_Die* die = NULL; - while ((die = dwfl_nextcu(dwfl, die, &bias)) != NULL) { - if (dwarf_tag(die) == DW_TAG_compile_unit) { - if (!process_die_compileunit(plib, lib, die)) { - complain(die, "Error reading compile unit"); - exit(1); - return false; - } - } else { - complain(die, "DW_TAG_compile_unit expected"); - exit(1); - return false; - } - } - - dict_destroy( &type_hash, NULL, NULL, NULL ); - return true; + Dwarf_Die *die = NULL; + while ((die = dwfl_module_nextcu(dwfl_module, die, &bias)) != NULL) { + if (dwarf_tag(die) == DW_TAG_compile_unit) + process_die_compileunit(plib, lib, + &type_dieoffset_hash, die); + else + complain(die, "A DW_TAG_compile_unit die expected. " + "Skipping this one"); + } + + dict_destroy(&type_dieoffset_hash, NULL, NULL, NULL); } -bool import_DWARF_prototypes(struct protolib* plib, struct library* lib, - Dwfl *dwfl) +bool import_DWARF_prototypes(struct library *lib) { + struct protolib *plib = lib->protolib; + + debug(DEBUG_FUNCTION, "Importing DWARF prototypes from '%s'", + lib->soname); if (plib == NULL) { - plib = protolib_cache_default(&g_protocache, lib->soname, 0); + + const char *soname_dup = strdup(lib->soname); + if (soname_dup == NULL) { + fprintf(stderr, "couldn't strdup"); + return false; + } + + plib = protolib_cache_default(&g_protocache, soname_dup, 1); if (plib == NULL) { fprintf(stderr, "Error loading protolib %s: %s.\n", - lib->soname, strerror(errno)); + lib->soname, strerror(errno)); } } - return import(plib, lib, dwfl); -} - -/* -- I handle static functions now. Should I? Those do not have DW_AT_external==1 - -- should process existing prototypes to make sure they match - -- what do function pointers look like? I'm doing void* - -- unions + import(plib, lib, lib->dwfl_module); + lib->protolib = plib; -- all my *allocs leak - - -*/ + return true; +}