-/* Most of this is Copyright Dima Kogan <dima@secretsauce.net>
- *
- * Pieces of this were taken from dwarf_prototypes.c in the dwarves project.
- * Those are Copyright (C) 2008 Arnaldo Carvalho de Melo <acme@redhat.com>.
+/* Copyright Dima Kogan <dima@secretsauce.net>
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of version 2 of the GNU General Public License as published by the
#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
-
-// 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;
+#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 */
+static struct arg_type_info *get_type(int *newly_allocated_info,
+ Dwarf_Die *type_die,
+ struct protolib *plib,
+ struct dict *type_dieoffset_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;
- }
-
- int res = dwarf_siblingof(die, die);
- if (res == 0 ) continue; // sibling exists
- if (res < 0 ) return false; // error
- break; // no sibling exists
- }
-
- return true;
-}
-
-static bool dump_dwarf_tree(Dwarf_Die* die)
-{
- return _dump_dwarf_tree( die, 0 );
-}
-#endif
+// debugging functions to dump types that I already parsed
#ifdef DUMP_PROTOTYPES
-static bool _dump_ltrace_tree(const struct arg_type_info* info, int indent)
+static bool _dump_ltrace_tree(const struct arg_type_info *info, int indent)
{
if (indent > 7) {
fprintf(stderr, "%*s%p ...\n", indent*4, "", (void*)info);
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_info* info)
+static bool dump_ltrace_tree(const struct arg_type_info *info)
{
- return _dump_ltrace_tree( info, 0 );
+ return _dump_ltrace_tree(info, 0);
}
#endif
-
-static uint64_t attr_numeric(Dwarf_Die *die, uint32_t name)
+// pulls a numerical value out of a particular attribute in a die. Returns true
+// if successful. The result is returned in *result. Note that this is cast to
+// (uint64_t), regardless of the actual type of the input
+static bool get_die_numeric(uint64_t *result,
+ Dwarf_Die *die, unsigned int attr_name)
{
- Dwarf_Attribute attr;
- uint32_t form;
+ Dwarf_Attribute attr ;
+
+ union {
+ Dwarf_Word udata;
+ Dwarf_Sword sdata;
+ Dwarf_Addr addr;
+ bool flag;
+ } u;
+
+ if (dwarf_attr(die, attr_name, &attr) == NULL)
+ return false;
+
+ unsigned int form = dwarf_whatform(&attr);
- if (dwarf_attr(die, name, &attr) == NULL)
- return 0;
+#define PROCESS_NUMERIC(type) \
+ if (dwarf_form ## type(&attr, &u.type) != 0) \
+ return false; \
+ *result = (uint64_t)u.type; \
+ return true
- form = dwarf_whatform(&attr);
switch (form) {
- case DW_FORM_addr: {
- Dwarf_Addr addr;
- if (dwarf_formaddr(&attr, &addr) == 0)
- return addr;
- }
- break;
+ case DW_FORM_addr:
+ PROCESS_NUMERIC(addr);
+
case DW_FORM_data1:
case DW_FORM_data2:
case DW_FORM_data4:
case DW_FORM_data8:
+ case DW_FORM_udata:
+ PROCESS_NUMERIC(udata);
+
case DW_FORM_sdata:
- case DW_FORM_udata: {
- Dwarf_Word value;
- if (dwarf_formudata(&attr, &value) == 0)
- return value;
- }
- break;
+ PROCESS_NUMERIC(sdata);
+
case DW_FORM_flag:
- case DW_FORM_flag_present: {
- bool value;
- if (dwarf_formflag(&attr, &value) == 0)
- return value;
- }
- break;
+ PROCESS_NUMERIC(flag);
+
default:
- complain(die, "DW_AT_<0x%x>=0x%x", name, form);
- break;
+ 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)
+{
+ // 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;
+ }
+
+ if (byte_size == sizeof(short)) {
+ *type = is_signed ? ARGTYPE_SHORT : ARGTYPE_USHORT;
+ return true;
+ }
+
+ if (byte_size == sizeof(int)) {
+ *type = is_signed ? ARGTYPE_INT : ARGTYPE_UINT;
+ return true;
}
- return 0;
+ if (byte_size == sizeof(long)) {
+ *type = is_signed ? ARGTYPE_LONG : ARGTYPE_ULONG;
+ return true;
+ }
+
+ 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)
{
- int encoding = attr_numeric(die, DW_AT_encoding);
+ uint64_t encoding;
+ if (!get_die_numeric(&encoding, die, DW_AT_encoding))
+ return ARGTYPE_VOID;
- if (encoding == DW_ATE_void )
+ if (encoding == DW_ATE_void)
return ARGTYPE_VOID;
- if (encoding == DW_ATE_signed_char || encoding == DW_ATE_unsigned_char )
+ 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;
+
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);
- switch (attr_numeric(die, DW_AT_byte_size)) {
- case sizeof(char):
- return ARGTYPE_CHAR;
- case sizeof(short):
- return is_signed ? ARGTYPE_SHORT : ARGTYPE_USHORT;
+ enum arg_type type;
+ if (!get_integer_base_type(&type, (int)byte_size, is_signed)) {
+ complain(die, "Unknown integer base type. "
+ "Using 'void'");
+ return ARGTYPE_VOID;
+ }
+ return type;
+ }
- case sizeof(int):
- return is_signed ? ARGTYPE_INT : ARGTYPE_UINT;
+ if (encoding == DW_ATE_float) {
+ switch (byte_size) {
+ case sizeof(float):
+ return ARGTYPE_FLOAT;
- case sizeof(long):
- return is_signed ? ARGTYPE_LONG : ARGTYPE_ULONG;
+ case sizeof(double):
+ return ARGTYPE_DOUBLE;
default:
- complain(die, "");
- exit(1);
+ // things like long doubles. ltrace has no support yet,
+ // so I just say "void"
+ return ARGTYPE_VOID;
}
}
- if (encoding == DW_ATE_float) {
- switch (attr_numeric(die, DW_AT_byte_size)) {
- case sizeof(float):
+#if 0
+ if (encoding == DW_ATE_complex_float) {
+ switch (byte_size) {
+ case 2*sizeof(float):
return ARGTYPE_FLOAT;
- case sizeof(double):
+ case 2*sizeof(double):
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;
}
}
+#endif
// Unknown encoding. I just say void
complain(die, "Unknown base type. Returning 'void'");
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
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)
{
- enum_info->type = ARGTYPE_INT;
- struct enum_lens *lens = calloc(1, sizeof(struct enum_lens));
+#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'
+ result->type = ARGTYPE_INT;
+ } else {
+ if (!get_integer_base_type(&result->type,
+ (int)byte_size, true)) {
+ complain(parent, "Unknown integer base type. "
+ "Using 'int'");
+ result->type = ARGTYPE_INT;
+ }
+ }
+
+ lens = calloc(1, sizeof(struct enum_lens));
if (lens == NULL) {
complain(parent, "alloc error");
- return false;
+ CLEANUP_AND_RETURN_ERROR(NULL);
}
lens_init_enum(lens);
- enum_info->lens = &lens->super;
+ result->lens = &lens->super;
+ result->own_lens = 1;
Dwarf_Die die;
if (dwarf_child(parent, &die) != 0) {
// empty enum. we're done
- return true;
+ CLEANUP_AND_RETURN_ERROR(NULL);
}
- while(1) {
+
+ while (1) {
complain(&die, "enum element: 0x%02x/'%s'", dwarf_tag(&die),
- dwarf_diename(&die));
+ dwarf_diename(&die));
+
+ dupkey = NULL;
+ value = NULL;
if (dwarf_tag(&die) != DW_TAG_enumerator) {
- complain(&die, "Enums can have ONLY DW_TAG_enumerator elements");
- return false;
+ complain(&die, "Enums can have ONLY DW_TAG_enumerator "
+ "elements");
+ CLEANUP_AND_RETURN_ERROR(NULL);
}
if (!dwarf_hasattr(&die, DW_AT_const_value)) {
- complain(&die, "Enums MUST have DW_AT_const_value values");
- return false;
+ complain(&die, "Enums MUST have DW_AT_const_value "
+ "values");
+ CLEANUP_AND_RETURN_ERROR(NULL);
}
- const char* key = dwarf_diename(&die);
+ const char *key = dwarf_diename(&die);
if (key == NULL) {
complain(&die, "Enums must have a DW_AT_name key");
- return false;
+ CLEANUP_AND_RETURN_ERROR(NULL);
}
- const char* dupkey = strdup(key);
+ dupkey = strdup(key);
if (dupkey == NULL) {
complain(&die, "Couldn't duplicate enum key");
- return false;
+ CLEANUP_AND_RETURN_ERROR(NULL);
}
- struct value* value = calloc( 1, sizeof(struct value));
+ value = calloc(1, sizeof(struct value));
if (value == NULL) {
complain(&die, "Couldn't alloc enum value");
- return false;
+ CLEANUP_AND_RETURN_ERROR(NULL);
+ }
+
+ value_init_detached(value, NULL, type_get_simple(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");
+ CLEANUP_AND_RETURN_ERROR(NULL);
}
- value_init_detached(value, NULL, type_get_simple( ARGTYPE_INT ), 0);
- value_set_word(value, attr_numeric(&die, DW_AT_const_value));
+ 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);
}
- int res = dwarf_siblingof(&die, &die);
- if (res == 0) continue; /* sibling exists */
- if (res < 0) return false; /* error */
- break; /* no sibling exists */
+ 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)) {
- if (attr_numeric(&subrange, DW_AT_lower_bound) != 0) {
- complain( parent,
- "Array subrange has a nonzero lower bound. Don't know what to do");
- return false;
+ uint64_t lower_bound;
+ 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");
+ CLEANUP_AND_RETURN_ERROR(NULL);
}
}
- int N;
+ 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
- N = attr_numeric(&subrange, DW_AT_upper_bound)+1;
-
- // 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;
+ {
+ if (!get_die_numeric(&N, &subrange, DW_AT_upper_bound)) {
+ complain(parent, "Couldn't read upper bound");
+ CLEANUP_AND_RETURN_ERROR(NULL);
+ }
+ N++;
}
- 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 );
- int res = dwarf_siblingof(&die, &die);
- if (res == 0) continue; /* sibling exists */
- if (res < 0) return false; /* error */
- break; /* no sibling exists */
+ 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_typedef:
+ case DW_TAG_const_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;
}
return true;
}
- while(1) {
- if (dwarf_tag(&arg_die) != DW_TAG_formal_parameter )
- goto next_prototype_argument;
+ while (1) {
+ if (dwarf_tag(&arg_die) == DW_TAG_formal_parameter) {
- complain(&arg_die, "arg: 0x%02x", dwarf_tag(&arg_die));
+ complain(&arg_die, "arg: 0x%02x", dwarf_tag(&arg_die));
- Dwarf_Die type_die;
- if (!get_type_die(&type_die, &arg_die )) {
- complain(&arg_die, "Couldn't get the argument type die");
- return false;
- }
+ argument_type = NULL;
+ newly_allocated_argument_type = 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;
- }
+ Dwarf_Die type_die;
+ if (!get_type_die(&type_die, &arg_die)) {
+ complain(&arg_die, "Couldn't get the argument "
+ "type die");
+ CLEANUP_AND_RETURN_ERROR(false);
+ }
- struct param param;
- param_init_type(¶m, arg_type_info, 0);
- if (prototype_push_param(proto, ¶m) <0) {
- complain(&arg_die, "couldn't add argument to the prototype");
- return false;
- }
+
+ argument_type = get_type(&newly_allocated_argument_type,
+ &type_die, plib,
+ type_dieoffset_hash);
+ if (argument_type==NULL) {
+ complain(&arg_die, "Couldn't parse arg "
+ "type from DWARF data");
+ CLEANUP_AND_RETURN_ERROR(false);
+ }
+
+ struct param param;
+ param_init_type(¶m, argument_type,
+ newly_allocated_argument_type);
+ if (prototype_push_param(result, ¶m) <0) {
+ complain(&arg_die, "couldn't add argument to "
+ "the prototype");
+ CLEANUP_AND_RETURN_ERROR(false);
+ }
#ifdef DUMP_PROTOTYPES
- fprintf(stderr, "Adding argument:\n");
- dump_ltrace_tree(arg_type_info);
+ fprintf(stderr, "Adding argument:\n");
+ dump_ltrace_tree(argument_type);
#endif
+ }
- next_prototype_argument: ;
- int res = dwarf_siblingof(&arg_die, &arg_die);
- if (res == 0) continue; /* sibling exists */
- if (res < 0) return false; /* error */
- break; /* no sibling exists */
+ NEXT_SIBLING(&arg_die);
}
return true;
+#undef CLEANUP_AND_RETURN_ERROR
}
-static bool process_die_compileunit(struct protolib* plib, struct library* lib,
- Dwarf_Die* parent)
+static bool import_subprogram_name(struct protolib *plib, struct library *lib,
+ struct dict *type_dieoffset_hash,
+ Dwarf_Die *die, const char* function_name)
{
- Dwarf_Die die;
- if (dwarf_child(parent, &die) != 0) {
- // no child nodes, so nothing to do
+ 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;
}
- while (1) {
- if (dwarf_tag(&die) == DW_TAG_subprogram) {
- const char* function_name = dwarf_diename(&die);
-
- complain(&die, "subroutine_type: 0x%02x; function '%s'",
- dwarf_tag(&die), function_name);
+ complain(die, "subroutine_type: 0x%02x; function '%s'",
+ dwarf_tag(die), function_name);
- struct prototype* proto =
- protolib_lookup_prototype(plib, function_name, true );
+ struct prototype *proto_already_there =
+ protolib_lookup_prototype(plib, function_name, false);
- if (proto != NULL) {
- complain(&die, "Prototype already exists. Skipping");
- goto next_prototype;
- }
+ 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");
- goto next_prototype;
- }
+ 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");
- return false;
- }
- prototype_init( proto );
+ const char *function_name_dup = strdup(function_name);
+ if (function_name_dup == NULL) {
+ complain(die, "couldn't strdup");
+ prototype_destroy(&proto);
+ return false;
+ }
+ protolib_add_prototype(plib, function_name_dup, 1, &proto);
+ return true;
+}
- if (!get_prototype(proto, &die )) {
- complain(&die, "couldn't get prototype");
- return false;
- }
+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;
- protolib_add_prototype(plib, function_name, 0, proto);
- }
+ 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;
+ }
- next_prototype:;
- int res = dwarf_siblingof(&die, &die);
- if (res == 0) continue; /* sibling exists */
- if (res < 0) return false; /* error */
- break; /* no sibling exists */
+ if ((function_name = dwarf_diename(die)) != NULL &&
+ !import_subprogram_name(plib, lib, type_dieoffset_hash, die,
+ function_name)) {
+ return false;
}
return true;
}
-static bool import(struct protolib* plib, struct library* lib, Dwfl* dwfl)
+static bool process_die_compileunit(struct protolib *plib, struct library *lib,
+ struct dict *type_dieoffset_hash,
+ Dwarf_Die *parent)
{
- dict_init(&type_hash, sizeof(Dwarf_Off), sizeof(struct arg_type_info*),
- dwarf_die_hash, dwarf_die_eq, NULL );
+ complain(parent, "Processing compile unit");
+ Dwarf_Die die;
+ if (dwarf_child(parent, &die) != 0) {
+ // no child nodes, so nothing to do
+ return true;
+ }
+
+ while (1) {
+ if (dwarf_tag(&die) == DW_TAG_subprogram)
+ if (!import_subprogram_die(plib, lib, type_dieoffset_hash,
+ &die))
+ complain(&die, "Error importing subprogram. "
+ "Skipping");
+
+ NEXT_SIBLING(&die);
+ }
- 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;
}
-bool import_DWARF_prototypes(struct protolib* plib, struct library* lib,
- Dwfl *dwfl)
+static void import(struct protolib *plib, struct library *lib,
+ Dwfl_Module *dwfl_module)
{
- if (plib == NULL) {
- plib = protolib_cache_default(&g_protocache, lib->soname, 0);
- if (plib == NULL) {
- fprintf(stderr, "Error loading protolib %s: %s.\n",
- lib->soname, strerror(errno));
- }
+ // 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_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");
}
- return import(plib, lib, dwfl);
+ dict_destroy(&type_dieoffset_hash, NULL, NULL, NULL);
}
-/*
-- I handle static functions now. Should I? Those do not have DW_AT_external==1
-
-- should process existing prototypes to make sure they match
+bool import_DWARF_prototypes(struct library *lib)
+{
+ struct protolib *plib = lib->protolib;
-- what do function pointers look like? I'm doing void*
+ debug(DEBUG_FUNCTION, "Importing DWARF prototypes from '%s'",
+ lib->soname);
+ if (plib == NULL) {
-- unions
+ const char *soname_dup = strdup(lib->soname);
+ if (soname_dup == NULL) {
+ fprintf(stderr, "couldn't strdup");
+ return false;
+ }
-- all my *allocs leak
+ plib = protolib_cache_default(&g_protocache, soname_dup, 1);
+ if (plib == NULL) {
+ fprintf(stderr, "Error loading protolib %s: %s.\n",
+ lib->soname, strerror(errno));
+ }
+ }
-- protolib_lookup_prototype should look for imports?
+ import(plib, lib, lib->dwfl_module);
+ lib->protolib = plib;
-*/
+ return true;
+}