/* * Trivial dwarf parser to extract part of a struct from debug infos * * Author: Dominique Martinet * License: WTFPLv2 * */ #include #include #include #include #include #include #include #include #include "libdwarf/dwarf.h" #include "libdwarf/libdwarf.h" static void parse_dwarf(Dwarf_Debug dbg, const char *struct_name, const char *field_names[], int field_count); static void find_struct(Dwarf_Debug dbg, Dwarf_Die die, const char *struct_name, const char *field_names[], int field_count, int level); static void find_fields(Dwarf_Debug dbg, Dwarf_Die die, const char *struct_name, const char *field_names[], int field_count, int level); static void print_field(Dwarf_Debug dbg, Dwarf_Die die, const char *field_name, int pad_num); int debug = 0; void usage(const char *argv[]) { printf("%s debug_file struct_name field [field...]\n", argv[0]); } int main(int argc, const char *argv[]) { Dwarf_Debug dbg = 0; int fd = -1; const char *filepath; const char *struct_name; int res = DW_DLV_ERROR; Dwarf_Error error; Dwarf_Handler errhand = 0; Dwarf_Ptr errarg = 0; if(argc < 4) { usage(argv); exit(1); } filepath = argv[1]; struct_name = argv[2]; fd = open(filepath,O_RDONLY); if(fd < 0) { printf("Failure attempting to open %s\n",filepath); } res = dwarf_init(fd, DW_DLC_READ, errhand, errarg, &dbg, &error); if(res != DW_DLV_OK) { printf("Giving up, cannot do DWARF processing\n"); exit(1); } parse_dwarf(dbg, struct_name, argv + 3, argc - 3); res = dwarf_finish(dbg,&error); if(res != DW_DLV_OK) { printf("dwarf_finish failed!\n"); } close(fd); return 0; } static void parse_dwarf(Dwarf_Debug dbg, const char *struct_name, const char *field_names[], int field_count) { Dwarf_Bool is_info = 1; Dwarf_Unsigned cu_length; Dwarf_Half cu_version; Dwarf_Off cu_abbrev_offset; Dwarf_Half cu_pointer_size; Dwarf_Half cu_offset_size; Dwarf_Half cu_extension_size; Dwarf_Sig8 type_signature; Dwarf_Unsigned type_offset; Dwarf_Unsigned cu_next_offset; Dwarf_Error err; int rc; while (1) { Dwarf_Die die; rc = dwarf_next_cu_header_c(dbg, is_info, &cu_length, &cu_version, &cu_abbrev_offset, &cu_pointer_size, &cu_offset_size, &cu_extension_size, &type_signature, &type_offset, &cu_next_offset, &err); if (rc == DW_DLV_NO_ENTRY) break; if (rc != DW_DLV_OK) { printf("error dwarf_next_cu_header_c: %d %s\n", rc, dwarf_errmsg(err)); exit(1); } rc = dwarf_siblingof(dbg, NULL, &die, &err); if (rc != DW_DLV_OK) { printf("first dwarf_siblingof failed: %d %s\n", rc, dwarf_errmsg(err)); exit(1); } find_struct(dbg, die, struct_name, field_names, field_count, 0); } printf("struct %s not found\n", struct_name); exit(2); } static void find_struct(Dwarf_Debug dbg, Dwarf_Die die, const char *struct_name, const char *field_names[], int field_count, int level) { Dwarf_Die next; Dwarf_Error err; int rc; if (level > 1) return; do { char *name; const char *tag_name; Dwarf_Half tag; rc = dwarf_diename(die, &name, &err); if (rc == DW_DLV_NO_ENTRY) { name = NULL; } else if (rc != DW_DLV_OK) { printf("dwarf_diename error: %d %s\n", rc, dwarf_errmsg(err)); exit(1); } rc = dwarf_tag(die, &tag, &err); if (rc != DW_DLV_OK) { printf("dwarf_tag error: %d %s\n", rc, dwarf_errmsg(err)); exit(1); } if (debug) { rc = dwarf_get_TAG_name(tag, &tag_name); if (rc != DW_DLV_OK) { printf("dwarf_get_TAG_name error: %d\n", rc); exit(1); } printf("<%d> %p <%d> %s: %s\n", level, die, tag, tag_name, name ? name : ""); } rc = dwarf_child(die, &next, &err); if (rc == DW_DLV_ERROR) { printf("dwarf_child error: %d %s\n", rc, dwarf_errmsg(err)); exit(1); } if (rc == DW_DLV_OK) { if (tag == DW_TAG_structure_type && name && strcasecmp(name, struct_name) == 0) { find_fields(dbg, next, struct_name, field_names, field_count, level + 1); printf("Found struct %s but it did not have all members given!\nMissing:\n", struct_name); for (rc = 0; rc < field_count; rc++) { if (field_names[rc]) printf("%s\n", field_names[rc]); } exit(3); } find_struct(dbg, next, struct_name, field_names, field_count, level + 1); dwarf_dealloc(dbg, next, DW_DLA_DIE); } rc = dwarf_siblingof(dbg, die, &next, &err); dwarf_dealloc(dbg, die, DW_DLA_DIE); if (name) dwarf_dealloc(dbg, name, DW_DLA_STRING); if (rc != DW_DLV_OK) break; die = next; } while (die); } static void find_fields(Dwarf_Debug dbg, Dwarf_Die die, const char *struct_name, const char *field_names[], int field_count, int level) { Dwarf_Die next; Dwarf_Error err; int rc, i, printed_count = 0; printf("struct %s {\n\tunion {\n", struct_name); do { char *name; const char *tag_name; Dwarf_Half tag; rc = dwarf_diename(die, &name, &err); if (rc == DW_DLV_NO_ENTRY) { name = NULL; } else if (rc != DW_DLV_OK) { printf("dwarf_diename error: %d %s\n", rc, dwarf_errmsg(err)); exit(1); } rc = dwarf_tag(die, &tag, &err); if (rc != DW_DLV_OK) { printf("dwarf_tag error: %d %s\n", rc, dwarf_errmsg(err)); exit(1); } if (debug) { rc = dwarf_get_TAG_name(tag, &tag_name); if (rc != DW_DLV_OK) { printf("dwarf_get_TAG_name error: %d\n", rc); exit(1); } printf("<%d> %p <%d> %s: %s\n", level, die, tag, tag_name, name ? name : ""); } if (tag == DW_TAG_member && name) { for (i = 0; i < field_count; i++) { if (!field_names[i]) continue; if (strcasecmp(name, field_names[i]) == 0) { print_field(dbg, die, field_names[i], printed_count); field_names[i] = NULL; printed_count++; break; } } if (printed_count == field_count) { printf("\t};\n};\n"); exit(0); } } rc = dwarf_siblingof(dbg, die, &next, &err); dwarf_dealloc(dbg, die, DW_DLA_DIE); if (name) dwarf_dealloc(dbg, name, DW_DLA_STRING); if (rc != DW_DLV_OK) break; die = next; } while (die); } static void print_field(Dwarf_Debug dbg, Dwarf_Die die, const char *field_name, int padnum) { Dwarf_Attribute attr; Dwarf_Error err; Dwarf_Unsigned offset = 0; char type_buf[1024]; int rc; rc = dwarf_attr(die, DW_AT_data_member_location, &attr, &err); if (rc == DW_DLV_NO_ENTRY) { printf("Found %s but no offset, assuming 0\n", field_name); } else if (rc != DW_DLV_OK) { printf("Error getting dwarf attr offset: %s\n", dwarf_errmsg(err)); exit(4); } else { Dwarf_Half form; rc = dwarf_whatform(attr, &form, &err); if (rc != DW_DLV_OK) { printf("Error getting whatform: %s\n", dwarf_errmsg(err)); exit(5); } if (form == DW_FORM_data1 || form == DW_FORM_data2 || form == DW_FORM_data2 || form == DW_FORM_data4 || form == DW_FORM_data8 || form == DW_FORM_udata) { dwarf_formudata(attr, &offset, 0); } else if (form == DW_FORM_sdata) { Dwarf_Signed soffset; dwarf_formsdata(attr, &soffset, 0); if (soffset < 0) { printf("unsupported negative offset\n"); /* FAIL */ } offset = (Dwarf_Unsigned) soffset; } else { Dwarf_Locdesc **locdescs; Dwarf_Signed len; if (dwarf_loclist_n(attr, &locdescs, &len, &err) == DW_DLV_ERROR) { printf("unsupported member offset\n"); /* FAIL */ } if (len != 1 || locdescs[0]->ld_cents != 1 || (locdescs[0]->ld_s[0]).lr_atom != DW_OP_plus_uconst) { printf("unsupported location expression\n"); /* FAIL */ } offset = (locdescs[0]->ld_s[0]).lr_number; } dwarf_dealloc(dbg, attr, DW_DLA_ATTR); } rc = dwarf_attr(die, DW_AT_type, &attr, &err); if (rc == DW_DLV_NO_ENTRY) { printf("Found %s but no type, can't assume that one out..\n", field_name); exit(6); } else if (rc != DW_DLV_OK) { printf("Error getting dwarf attrlist: %s\n", dwarf_errmsg(err)); exit(6); } else { Dwarf_Die type_die; Dwarf_Off type_off; Dwarf_Half type_tag; char *type_name; int pointer = 0; rc = dwarf_global_formref(attr, &type_off, &err); if (rc != DW_DLV_OK) { printf("Error getting ref offset for type: %s\n", dwarf_errmsg(err)); exit(7); } rc = dwarf_offdie_b(dbg, type_off, 1, &type_die, &err); if (rc != DW_DLV_OK) { printf("Error getting die from offset for type: %s\n", dwarf_errmsg(err)); exit(7); } rc = dwarf_tag(type_die, &type_tag, &err); if (rc != DW_DLV_OK) { printf("dwarf_tag error: %d %s\n", rc, dwarf_errmsg(err)); exit(7); } if (type_tag == DW_TAG_pointer_type) { Dwarf_Attribute pointer_attr; Dwarf_Off pointer_off; Dwarf_Die pointer_die; rc = dwarf_attr(type_die, DW_AT_type, &pointer_attr, &err); if (rc != DW_DLV_OK) { printf("Error getting pointer attr for type: %s\n", dwarf_errmsg(err)); exit(7); } rc = dwarf_global_formref(pointer_attr, &pointer_off, &err); if (rc != DW_DLV_OK) { printf("Error getting pointer ref offset for type: %s\n", dwarf_errmsg(err)); exit(7); } rc = dwarf_offdie_b(dbg, pointer_off, 1, &pointer_die, &err); if (rc != DW_DLV_OK) { printf("Error getting pointer die from offset for type: %s\n", dwarf_errmsg(err)); exit(7); } dwarf_dealloc(dbg, attr, DW_DLA_ATTR); dwarf_dealloc(dbg, type_die, DW_DLA_DIE); attr = pointer_attr; type_die = pointer_die; pointer++; rc = dwarf_tag(type_die, &type_tag, &err); if (rc != DW_DLV_OK) { printf("dwarf_tag error: %d %s\n", rc, dwarf_errmsg(err)); exit(7); } } rc = dwarf_diename(type_die, &type_name, &err); if (rc != DW_DLV_OK) { printf("dwarf_diename error: %d %s\n", rc, dwarf_errmsg(err)); const char *tag_name; rc = dwarf_get_TAG_name(type_tag, &tag_name); if (rc != DW_DLV_OK) { printf("dwarf_get_TAG_name error: %d\n", rc); } printf("Bad tag %s (%d)?\n", tag_name, type_tag); exit(7); } if (type_tag == DW_TAG_structure_type) { snprintf(type_buf, 1024, "struct %s%s", pointer ? "*" : "", type_name); } else if (type_tag == DW_TAG_base_type || type_tag == DW_TAG_typedef) { snprintf(type_buf, 1024, "%s%s", pointer ? "*" : "", type_name); } else { const char *tag_name; rc = dwarf_get_TAG_name(type_tag, &tag_name); if (rc != DW_DLV_OK) { printf("dwarf_get_TAG_name error: %d\n", rc); } printf("Type tag %s (%d) is not implemented, please add it\n", tag_name, type_tag); exit(7); } dwarf_dealloc(dbg, type_name, DW_DLA_STRING); dwarf_dealloc(dbg, attr, DW_DLA_ATTR); dwarf_dealloc(dbg, type_die, DW_DLA_DIE); } printf("\t\tstruct {\n\t\t\tchar padding%i[%u]\n\t\t\t%s %s\n\t\t};\n", padnum, (unsigned int) offset, type_buf, field_name); }