Replace readdir_r with readdir
[platform/upstream/ltrace.git] / dwarf_prototypes.c
index f76dbed..9c36904 100644 (file)
 #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_infoinfo, 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; i<info->u.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_infoinfo)
+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_tresult,
-                                                       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 charkey = 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(&param, arg_type_info, 0);
-                       if (prototype_push_param(proto, &param) <0) {
-                               complain(&arg_die, "couldn't add argument to the prototype");
-                               return false;
+                       param_init_type(&param, argument_type,
+                                       newly_allocated_argument_type);
+                       if (prototype_push_param(result, &param) <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;
+}