reworked error and memory handling
authorDima Kogan <dima@secretsauce.net>
Wed, 30 Apr 2014 10:35:13 +0000 (03:35 -0700)
committerChanho Park <chanho61.park@samsung.com>
Fri, 22 Aug 2014 11:38:25 +0000 (20:38 +0900)
I now longer exit() on the slightest sign of trouble, nor do I leak all my heap
memory allocations

dwarf_prototypes.c

index ec39758..48d2673 100644 (file)
@@ -43,8 +43,9 @@
        if (res < 0)  return false; /* error             */     \
        break                       /* no sibling exists */
 
-static bool get_type(struct arg_type_info** info, Dwarf_Die* type_die, struct protolib* plib,
-                                        struct dict* type_dieoffset_hash);
+static struct arg_type_info* get_type(int* newly_allocated_info,
+                                                                         Dwarf_Die* type_die, struct protolib* plib,
+                                                                         struct dict* type_dieoffset_hash);
 
 
 #if 0
@@ -214,6 +215,8 @@ static bool get_integer_base_type(enum arg_type* type, int byte_size, bool is_si
        }
 }
 
+// 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;
@@ -289,6 +292,9 @@ static bool get_type_die(Dwarf_Die* type_die, Dwarf_Die* die)
                dwarf_formref_die(&attr, type_die) != NULL;
 }
 
+
+
+// type_dieoffset_hash dictionary callbacks
 static size_t dwarf_die_hash(const void* x)
 {
        return *(const Dwarf_Off*)x;
@@ -298,124 +304,212 @@ 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(type_die, "alloc error");
+               CLEANUP_AND_RETURN_ERROR(NULL);
+       }
+
+       dict_insert(type_dieoffset_hash, &die_offset, &result);
+
        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)) {
+               if(!get_integer_base_type(&result->type, (int)byte_size, true)) {
                        complain(parent, "Unknown integer base type. Using 'int'");
-                       enum_info->type = ARGTYPE_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) {
                complain(&die, "enum element: 0x%02x/'%s'", dwarf_tag(&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;
+                       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;
+                       CLEANUP_AND_RETURN_ERROR(NULL);
                }
 
                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, struct protolib* plib,
-                                         struct dict* type_dieoffset_hash)
+// 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(value != NULL) {                                                                                             \
+                       value_destroy(value);                                                                           \
+                       free(value);                                                                                            \
+               }                                                                                                                               \
+               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 value*                           value                                      = 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(type_die, "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;
+               CLEANUP_AND_RETURN_ERROR(NULL);
        }
 
-       struct arg_type_info* info;
-       if (!get_type(&info, &type_die, plib, type_dieoffset_hash)) {
+       dict_insert(type_dieoffset_hash, &die_offset, &result);
+       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");
-               return false;
+               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;
+               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;
+               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;
+                       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;
+                       CLEANUP_AND_RETURN_ERROR(NULL);
                }
        }
 
@@ -429,7 +523,7 @@ static bool get_array(struct arg_type_info* array_info, Dwarf_Die* parent, struc
        {
                if (!get_die_numeric(&N, &subrange, DW_AT_upper_bound)) {
                        complain(parent, "Couldn't read upper bound");
-                       return false;
+                       CLEANUP_AND_RETURN_ERROR(NULL);
                }
                N++;
        }
@@ -437,75 +531,139 @@ static bool get_array(struct arg_type_info* array_info, Dwarf_Die* parent, struc
        // 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));
+       value = calloc(1, sizeof(struct value));
        if (value == NULL) {
                complain(&subrange, "Couldn't alloc length value");
-               return false;
+               CLEANUP_AND_RETURN_ERROR(NULL);
        }
        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));
+       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);
 
-       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, struct protolib* plib,
-                                                 struct dict* type_dieoffset_hash)
+// 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(type_die, "alloc error");
+               CLEANUP_AND_RETURN_ERROR(NULL);
+       }
+       type_init_struct(result);
+       dict_insert(type_dieoffset_hash, &die_offset, &result);
 
        Dwarf_Die die;
        if (dwarf_child(parent, &die) != 0) {
                // no elements; we're done
-               return true;
+               return result;
        }
 
        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;
+                       CLEANUP_AND_RETURN_ERROR(NULL);
                }
 
-               struct arg_type_info* member_info = NULL;
-               if (!get_type(&member_info, &type_die, plib, type_dieoffset_hash)) {
+               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);
                }
-               type_struct_add(struct_info, member_info, 0);
+               type_struct_add(result, member_type, newly_allocated_member_type);
 
                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, struct protolib* plib,
-                                        struct dict* type_dieoffset_hash)
+// 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)
+
+       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);
+
+
+    // 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);
@@ -518,8 +676,7 @@ static bool get_type(struct arg_type_info** info, Dwarf_Die* type_die, struct pr
                        complain(type_die,
                                         "Type '%s' defined in a .conf file. Using that instead of DWARF",
                                         type_name);
-                       *info = already_defined_type->info;
-                       return true;
+                       return already_defined_type->info;
                }
        }
 
@@ -527,130 +684,163 @@ static bool get_type(struct arg_type_info** info, Dwarf_Die* type_die, struct pr
 
        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_dieoffset_hash, &die_offset, info);
-               return true;
+               complain(type_die, "Storing base type");
+               result = type_get_simple(get_base_type(type_die));
+               dict_insert(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_dieoffset_hash, &die_offset, info);
-               return true;
+               complain(type_die, "Storing subroutine type");
+               result = type_get_simple(ARGTYPE_VOID);
+               dict_insert(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_voidptr();
-                       complain(type_die, "Storing void-pointer type: %p", *info);
-                       dict_insert(type_dieoffset_hash, &die_offset, info);
-                       return true;
+                       complain(type_die, "Storing void-pointer type");
+                       result = type_get_voidptr();
+                       dict_insert(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(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_dieoffset_hash, &die_offset, info);
-               return get_type(&(*info)->u.ptr_info.info, &next_die, plib, type_dieoffset_hash);
+               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_dieoffset_hash, &die_offset, info);
-               return get_structure(*info, type_die, plib, type_dieoffset_hash);
+               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, plib, type_dieoffset_hash);
+                       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);
+                       result = type_get_simple(ARGTYPE_VOID);
+                       complain(type_die, "Storing void type");
                }
-               if (res)
-                       dict_insert(type_dieoffset_hash, &die_offset, info);
-               return res;
-       }
+               dict_insert(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
+               // We have an enumeration. This has a base type, 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;
-               }
+               *newly_allocated_result = 1;
 
-               complain(type_die, "Storing enum int: %p", *info);
-               dict_insert(type_dieoffset_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_dieoffset_hash, &die_offset, info);
-               return get_array(*info, type_die, plib, type_dieoffset_hash);
+               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(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(type_dieoffset_hash, &die_offset, &result);
+               return result;
        }
 
-       return false;
+#undef CLEANUP_AND_RETURN_ERROR
 }
 
-static bool get_prototype(struct prototype* proto, Dwarf_Die* subroutine, struct protolib* plib,
-                                                 struct dict* type_dieoffset_hash)
+// returns a newly-allocated prototype*, or NULL on error
+static struct prototype* get_prototype( Dwarf_Die* subroutine, struct protolib* plib,
+                                                                               struct dict* type_dieoffset_hash)
 {
+
+#define CLEANUP_AND_RETURN_ERROR(ret) do {             \
+               if(argument_type != NULL && newly_allocated_argument_type) {    \
+                       type_destroy(argument_type);            \
+                       free(argument_type);                            \
+               }                                                                               \
+               if(result != NULL) {                                    \
+                       prototype_destroy(result);                      \
+                       free(result);                                           \
+               }                                                                               \
+               return ret;                                                             \
+       } while(0)
+
+
+       struct prototype*                       result                                            = NULL;
+       struct arg_type_info*           argument_type                             = NULL;
+       int                                                     newly_allocated_argument_type = 0;
+
+       result = calloc(1, sizeof(struct prototype));
+       if (result == NULL) {
+               complain(die, "couldn't alloc prototype");
+               CLEANUP_AND_RETURN_ERROR(NULL);
+       }
+       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;
+               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) {
+               result->return_info = calloc(1, sizeof(struct arg_type_info));
+               if (result->return_info == NULL) {
                        complain(subroutine, "Couldn't alloc return type");
-                       return false;
+                       CLEANUP_AND_RETURN_ERROR(NULL);
                }
-               proto->own_return_info = 0;
+               result->own_return_info = 1;
 
-               if (!get_type(&proto->return_info, &return_type_die, plib, type_dieoffset_hash)) {
+               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(NULL);
                }
+               result->own_return_info = newly_allocated_return_type;
        }
 
 
@@ -658,7 +848,7 @@ static bool get_prototype(struct prototype* proto, Dwarf_Die* subroutine, struct
        Dwarf_Die arg_die;
        if (dwarf_child(subroutine, &arg_die) != 0) {
                // no args. We're done
-               return true;
+               return result;
        }
 
        while(1) {
@@ -666,35 +856,41 @@ static bool get_prototype(struct prototype* proto, Dwarf_Die* subroutine, struct
 
                        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;
+                               CLEANUP_AND_RETURN_ERROR(NULL);
                        }
 
-                       struct arg_type_info* arg_type_info = NULL;
-                       if (!get_type(&arg_type_info, &type_die, plib, type_dieoffset_hash)) {
+
+                       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");
-                               return false;
+                               CLEANUP_AND_RETURN_ERROR(NULL);
                        }
 
                        struct param param;
-                       param_init_type(&param, arg_type_info, 0);
-                       if (prototype_push_param(proto, &param) <0) {
+                       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");
-                               return false;
+                               CLEANUP_AND_RETURN_ERROR(NULL);
                        }
 
 #ifdef DUMP_PROTOTYPES
                        fprintf(stderr, "Adding argument:\n");
-                       dump_ltrace_tree(arg_type_info);
+                       dump_ltrace_tree(argument_type);
 #endif
                }
 
                NEXT_SIBLING(&arg_die);
        }
 
-       return true;
+       return result;
+#undef CLEANUP_AND_RETURN_ERROR
 }
 
 static bool import_subprogram(struct protolib* plib, struct library* lib,
@@ -732,19 +928,20 @@ static bool import_subprogram(struct protolib* plib, struct library* lib,
                return true;
        }
 
-       proto = malloc(sizeof(struct prototype));
-       if (proto == NULL) {
-               complain(die, "couldn't alloc prototype");
+       proto = get_prototype(die, plib, type_dieoffset_hash);
+       if(proto == NULL) {
+               complain(die, "couldn't get prototype");
                return false;
        }
-       prototype_init(proto);
 
-       if (!get_prototype(proto, die, plib, type_dieoffset_hash)) {
-               complain(die, "couldn't get prototype");
+       const char* function_name_dup = strdup(function_name);
+       if( function_name_dup == NULL ) {
+               complain(die, "couldn't strdup");
+               prototype_destroy(proto);
+               free(proto);
                return false;
        }
-
-       protolib_add_prototype(plib, function_name, 0, proto);
+       protolib_add_prototype(plib, function_name_dup, 1, proto);
        return true;
 }
 
@@ -761,7 +958,7 @@ 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, type_dieoffset_hash, &die))
-                               return false;
+                               complain(&die, "Error importing subprogram. Skipping");
 
                NEXT_SIBLING(&die);
        }
@@ -769,7 +966,7 @@ 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* dwfl)
 {
        // 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
@@ -779,30 +976,16 @@ static bool import(struct protolib* plib, struct library* lib, Dwfl* dwfl)
        dict_init(&type_dieoffset_hash, sizeof(Dwarf_Off), sizeof(struct arg_type_info*),
                          dwarf_die_hash, dwarf_die_eq, NULL);
 
-       bool result = true;
-
        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, &type_dieoffset_hash, die)) {
-                complain(die, "Error reading compile unit");
-                               exit(1);
-
-                               result = false;
-                               break;
-            }
-        } else {
-            complain(die, "DW_TAG_compile_unit expected");
-                       exit(1);
-
-                       result = false;
-                       break;
-        }
+        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);
-       return result;
 }
 
 bool import_DWARF_prototypes(struct library* lib)
@@ -811,18 +994,24 @@ bool import_DWARF_prototypes(struct library* lib)
        Dwfl*                           dwfl = lib->dwfl;
 
        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));
                }
        }
 
-       if (import(plib, lib, dwfl)) {
-               lib->protolib = plib;
-               return true;
-       }
-       return false;
+       import(plib, lib, dwfl);
+       lib->protolib = plib;
+
+       return true;
 }
 
 /*
@@ -836,5 +1025,4 @@ bool import_DWARF_prototypes(struct library* lib)
 
 - all my *allocs leak
 
-
 */