/* Copyright (C) 2008 Arnaldo Carvalho de Melo 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 Free Software Foundation. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "config.h" #include "list.h" #include "dwarves.h" #include "dutil.h" static void *zalloc(const size_t size) { void *s = malloc(size); if (s != NULL) memset(s, 0, size); return s; } static void *memdup(const void *src, size_t len) { void *s = malloc(len); if (s != NULL) memcpy(s, src, len); return s; } /* Number decoding macros. See 7.6 Variable Length Data. */ #define get_uleb128_step(var, addr, nth, break) \ __b = *(addr)++; \ var |= (uintmax_t) (__b & 0x7f) << (nth * 7); \ if ((__b & 0x80) == 0) \ break #define get_uleb128_rest_return(var, i, addrp) \ do { \ for (; i < 10; ++i) { \ get_uleb128_step(var, *addrp, i, \ return var); \ } \ /* Other implementations set VALUE to UINT_MAX in this \ case. So we better do this as well. */ \ return UINT64_MAX; \ } while (0) static uint64_t __libdw_get_uleb128(uint64_t acc, uint32_t i, const uint8_t **addrp) { uint8_t __b; get_uleb128_rest_return (acc, i, addrp); } #define get_uleb128(var, addr) \ do { \ uint8_t __b; \ var = 0; \ get_uleb128_step(var, addr, 0, break); \ var = __libdw_get_uleb128 (var, 1, &(addr)); \ } while (0) static uint64_t attr_numeric(Dwarf_Die *die, uint32_t name) { Dwarf_Attribute attr; uint32_t form; if (dwarf_attr(die, name, &attr) == NULL) return 0; 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_data1: case DW_FORM_data2: case DW_FORM_data4: case DW_FORM_data8: case DW_FORM_sdata: case DW_FORM_udata: { Dwarf_Word value; if (dwarf_formudata(&attr, &value) == 0) return value; } break; case DW_FORM_flag: return 1; default: fprintf(stderr, "DW_AT_<0x%x>=0x%x\n", name, form); break; } return 0; } static uint64_t dwarf_expr(const uint8_t *expr, uint32_t len __unused) { /* Common case: offset from start of the class */ if (expr[0] == DW_OP_plus_uconst || expr[0] == DW_OP_constu) { uint64_t result; ++expr; get_uleb128(result, expr); return result; } fprintf(stderr, "%s: unhandled %#x DW_OP_ operation\n", __func__, *expr); return UINT64_MAX; } static Dwarf_Off attr_offset(Dwarf_Die *die, const uint32_t name) { Dwarf_Attribute attr; Dwarf_Block block; if (dwarf_attr(die, name, &attr) != NULL && dwarf_formblock(&attr, &block) == 0) return dwarf_expr(block.data, block.length); return 0; } static const char *attr_string(Dwarf_Die *die, uint32_t name) { Dwarf_Attribute attr; if (dwarf_attr(die, name, &attr) != NULL) return dwarf_formstring(&attr); return NULL; } static Dwarf_Off attr_type(Dwarf_Die *die, uint32_t attr_name) { Dwarf_Attribute attr; if (dwarf_attr(die, attr_name, &attr) != NULL) { Dwarf_Die type_die; if (dwarf_formref_die(&attr, &type_die) != NULL) return dwarf_dieoffset(&type_die); } return 0; } static int attr_location(Dwarf_Die *die, Dwarf_Op **expr, size_t *exprlen) { Dwarf_Attribute attr; if (dwarf_attr(die, DW_AT_location, &attr) != NULL) { if (dwarf_getlocation(&attr, expr, exprlen) == 0) return 0; } return 1; } static void tag__init(struct tag *self, Dwarf_Die *die) { int32_t decl_line; self->tag = dwarf_tag(die); self->id = dwarf_dieoffset(die); if (self->tag == DW_TAG_imported_module || self->tag == DW_TAG_imported_declaration) self->type = attr_type(die, DW_AT_import); else self->type = attr_type(die, DW_AT_type); self->decl_file = strings__add(dwarf_decl_file(die)); dwarf_decl_line(die, &decl_line); self->decl_line = decl_line; self->recursivity_level = 0; INIT_LIST_HEAD(&self->hash_node); } static struct tag *tag__new(Dwarf_Die *die) { struct tag *self = malloc(sizeof(*self)); if (self != NULL) tag__init(self, die); return self; } static struct base_type *base_type__new(Dwarf_Die *die) { struct base_type *self = zalloc(sizeof(*self)); if (self != NULL) { tag__init(&self->tag, die); self->name = strings__add(attr_string(die, DW_AT_name)); self->size = attr_numeric(die, DW_AT_byte_size); } return self; } static struct array_type *array_type__new(Dwarf_Die *die) { struct array_type *self = zalloc(sizeof(*self)); if (self != NULL) tag__init(&self->tag, die); return self; } static void namespace__init(struct namespace *self, Dwarf_Die *die) { tag__init(&self->tag, die); INIT_LIST_HEAD(&self->tags); self->name = strings__add(attr_string(die, DW_AT_name)); self->nr_tags = 0; } static struct namespace *namespace__new(Dwarf_Die *die) { struct namespace *self = malloc(sizeof(*self)); if (self != NULL) namespace__init(self, die); return self; } static void type__init(struct type *self, Dwarf_Die *die) { namespace__init(&self->namespace, die); INIT_LIST_HEAD(&self->node); self->size = attr_numeric(die, DW_AT_byte_size); self->declaration = attr_numeric(die, DW_AT_declaration); self->specification = attr_type(die, DW_AT_specification); self->definition_emitted = 0; self->fwd_decl_emitted = 0; self->resized = 0; self->nr_members = 0; } static struct type *type__new(Dwarf_Die *die) { struct type *self = malloc(sizeof(*self)); if (self != NULL) type__init(self, die); return self; } static struct enumerator *enumerator__new(Dwarf_Die *die) { struct enumerator *self = zalloc(sizeof(*self)); if (self != NULL) { tag__init(&self->tag, die); self->name = strings__add(attr_string(die, DW_AT_name)); self->value = attr_numeric(die, DW_AT_const_value); } return self; } static enum vlocation dwarf__location(Dwarf_Die *die) { Dwarf_Op *expr; size_t exprlen; enum vlocation location = LOCATION_UNKNOWN; if (attr_location(die, &expr, &exprlen) != 0) location = LOCATION_OPTIMIZED; else if (exprlen != 0) switch (expr->atom) { case DW_OP_addr: location = LOCATION_GLOBAL; break; case DW_OP_reg1 ... DW_OP_reg31: case DW_OP_breg0 ... DW_OP_breg31: location = LOCATION_REGISTER; break; case DW_OP_fbreg: location = LOCATION_LOCAL; break; } return location; } static struct variable *variable__new(Dwarf_Die *die) { struct variable *self = malloc(sizeof(*self)); if (self != NULL) { tag__init(&self->tag, die); self->name = strings__add(attr_string(die, DW_AT_name)); self->abstract_origin = attr_type(die, DW_AT_abstract_origin); /* variable is visible outside of its enclosing cu */ self->external = dwarf_hasattr(die, DW_AT_external); /* non-defining declaration of an object */ self->declaration = dwarf_hasattr(die, DW_AT_declaration); self->location = LOCATION_UNKNOWN; if (!self->declaration) self->location = dwarf__location(die); } return self; } static struct class_member *class_member__new(Dwarf_Die *die) { struct class_member *self = zalloc(sizeof(*self)); if (self != NULL) { tag__init(&self->tag, die); self->offset = attr_offset(die, DW_AT_data_member_location); self->bit_size = attr_numeric(die, DW_AT_bit_size); self->bit_offset = attr_numeric(die, DW_AT_bit_offset); self->accessibility = attr_numeric(die, DW_AT_accessibility); self->virtuality = attr_numeric(die, DW_AT_virtuality); self->name = strings__add(attr_string(die, DW_AT_name)); } return self; } static struct parameter *parameter__new(Dwarf_Die *die) { struct parameter *self = zalloc(sizeof(*self)); if (self != NULL) { tag__init(&self->tag, die); self->name = strings__add(attr_string(die, DW_AT_name)); self->abstract_origin = attr_type(die, DW_AT_abstract_origin); } return self; } static struct inline_expansion *inline_expansion__new(Dwarf_Die *die) { struct inline_expansion *self = zalloc(sizeof(*self)); if (self != NULL) { tag__init(&self->tag, die); self->tag.decl_file = strings__add(attr_string(die, DW_AT_call_file)); self->tag.decl_line = attr_numeric(die, DW_AT_call_line); self->tag.type = attr_type(die, DW_AT_abstract_origin); if (dwarf_lowpc(die, &self->low_pc)) self->low_pc = 0; if (dwarf_lowpc(die, &self->high_pc)) self->high_pc = 0; self->size = self->high_pc - self->low_pc; if (self->size == 0) { Dwarf_Addr base, start; ptrdiff_t offset = 0; while (1) { offset = dwarf_ranges(die, offset, &base, &start, &self->high_pc); start = (unsigned long)start; self->high_pc = (unsigned long)self->high_pc; if (offset <= 0) break; self->size += self->high_pc - start; if (self->low_pc == 0) self->low_pc = start; } } } return self; } static struct label *label__new(Dwarf_Die *die) { struct label *self = malloc(sizeof(*self)); if (self != NULL) { tag__init(&self->tag, die); self->name = strings__add(attr_string(die, DW_AT_name)); if (dwarf_lowpc(die, &self->low_pc)) self->low_pc = 0; } return self; } static struct class *class__new(Dwarf_Die *die) { struct class *self = zalloc(sizeof(*self)); if (self != NULL) { type__init(&self->type, die); INIT_LIST_HEAD(&self->vtable); } return self; } static void lexblock__init(struct lexblock *self, Dwarf_Die *die) { if (dwarf_highpc(die, &self->high_pc)) self->high_pc = 0; if (dwarf_lowpc(die, &self->low_pc)) self->low_pc = 0; INIT_LIST_HEAD(&self->tags); self->nr_inline_expansions = self->nr_labels = self->nr_lexblocks = self->nr_variables = 0; } static struct lexblock *lexblock__new(Dwarf_Die *die) { struct lexblock *self = malloc(sizeof(*self)); if (self != NULL) { tag__init(&self->tag, die); lexblock__init(self, die); } return self; } static void ftype__init(struct ftype *self, Dwarf_Die *die) { const uint16_t tag = dwarf_tag(die); assert(tag == DW_TAG_subprogram || tag == DW_TAG_subroutine_type); tag__init(&self->tag, die); INIT_LIST_HEAD(&self->parms); self->nr_parms = 0; self->unspec_parms = 0; } static struct ftype *ftype__new(Dwarf_Die *die) { struct ftype *self = malloc(sizeof(*self)); if (self != NULL) ftype__init(self, die); return self; } static struct function *function__new(Dwarf_Die *die) { struct function *self = zalloc(sizeof(*self)); if (self != NULL) { ftype__init(&self->proto, die); lexblock__init(&self->lexblock, die); self->name = strings__add(attr_string(die, DW_AT_name)); self->linkage_name = strings__add(attr_string(die, DW_AT_MIPS_linkage_name)); self->inlined = attr_numeric(die, DW_AT_inline); self->external = dwarf_hasattr(die, DW_AT_external); self->abstract_origin = attr_type(die, DW_AT_abstract_origin); self->specification = attr_type(die, DW_AT_specification); self->accessibility = attr_numeric(die, DW_AT_accessibility); self->virtuality = attr_numeric(die, DW_AT_virtuality); INIT_LIST_HEAD(&self->vtable_node); INIT_LIST_HEAD(&self->tool_node); self->vtable_entry = -1; if (dwarf_hasattr(die, DW_AT_vtable_elem_location)) self->vtable_entry = attr_offset(die, DW_AT_vtable_elem_location); } return self; } static void oom(const char *msg) { fprintf(stderr, "libclasses: out of memory(%s)\n", msg); exit(EXIT_FAILURE); } static uint64_t attr_upper_bound(Dwarf_Die *die) { Dwarf_Attribute attr; if (dwarf_attr(die, DW_AT_upper_bound, &attr) != NULL) { Dwarf_Word num; if (dwarf_formudata(&attr, &num) == 0) { return (uintmax_t)num + 1; } } return 0; } static void __cu__tag_not_handled(Dwarf_Die *die, const char *fn) { fprintf(stderr, "%s: DW_TAG_%s @ <%#llx> not handled!\n", fn, dwarf_tag_name(dwarf_tag(die)), (unsigned long long)dwarf_dieoffset(die)); } #define cu__tag_not_handled(die) __cu__tag_not_handled(die, __FUNCTION__) static struct tag *__die__process_tag(Dwarf_Die *die, struct cu *cu, const char *fn); #define die__process_tag(die, cu) __die__process_tag(die, cu, __FUNCTION__) static struct tag *die__create_new_tag(Dwarf_Die *die) { struct tag *self = tag__new(die); if (self == NULL) oom("tag__new"); if (dwarf_haschildren(die)) fprintf(stderr, "%s: %s WITH children!\n", __FUNCTION__, dwarf_tag_name(self->tag)); return self; } static void die__process_class(Dwarf_Die *die, struct type *class, struct cu *cu); static struct tag *die__create_new_class(Dwarf_Die *die, struct cu *cu) { Dwarf_Die child; struct class *class = class__new(die); if (class == NULL) oom("class__new"); if (dwarf_haschildren(die) != 0 && dwarf_child(die, &child) == 0) die__process_class(&child, &class->type, cu); return &class->type.namespace.tag; } static void die__process_namespace(Dwarf_Die *die, struct namespace *namespace, struct cu *cu); static struct tag *die__create_new_namespace(Dwarf_Die *die, struct cu *cu) { Dwarf_Die child; struct namespace *namespace = namespace__new(die); if (namespace == NULL) oom("namespace__new"); if (dwarf_haschildren(die) != 0 && dwarf_child(die, &child) == 0) die__process_namespace(&child, namespace, cu); return &namespace->tag; } static struct tag *die__create_new_union(Dwarf_Die *die, struct cu *cu) { Dwarf_Die child; struct type *utype = type__new(die); if (utype == NULL) oom("type__new"); if (dwarf_haschildren(die) != 0 && dwarf_child(die, &child) == 0) die__process_class(&child, utype, cu); return &utype->namespace.tag; } static struct tag *die__create_new_base_type(Dwarf_Die *die) { struct base_type *base = base_type__new(die); if (base == NULL) oom("base_type__new"); if (dwarf_haschildren(die)) fprintf(stderr, "%s: DW_TAG_base_type WITH children!\n", __FUNCTION__); return &base->tag; } static struct tag *die__create_new_typedef(Dwarf_Die *die) { struct type *tdef = type__new(die); if (tdef == NULL) oom("type__new"); if (dwarf_haschildren(die)) fprintf(stderr, "%s: DW_TAG_typedef WITH children!\n", __FUNCTION__); return &tdef->namespace.tag; } static struct tag *die__create_new_array(Dwarf_Die *die) { Dwarf_Die child; /* "64 dimensions will be enough for everybody." acme, 2006 */ const uint8_t max_dimensions = 64; uint32_t nr_entries[max_dimensions]; struct array_type *array = array_type__new(die); if (array == NULL) oom("array_type__new"); if (!dwarf_haschildren(die) || dwarf_child(die, &child) != 0) { fprintf(stderr, "%s: DW_TAG_array_type with no children!\n", __FUNCTION__); return NULL; } die = &child; array->dimensions = 0; do { if (dwarf_tag(die) == DW_TAG_subrange_type) { nr_entries[array->dimensions++] = attr_upper_bound(die); if (array->dimensions == max_dimensions) { fprintf(stderr, "%s: only %u dimensions are " "supported!\n", __FUNCTION__, max_dimensions); break; } } else cu__tag_not_handled(die); } while (dwarf_siblingof(die, die) == 0); array->nr_entries = memdup(nr_entries, array->dimensions * sizeof(uint32_t)); if (array->nr_entries == NULL) oom("memdup(array.nr_entries)"); return &array->tag; } static void die__create_new_parameter(Dwarf_Die *die, struct ftype *ftype, struct lexblock *lexblock) { struct parameter *parm = parameter__new(die); if (parm == NULL) oom("parameter__new"); if (ftype != NULL) ftype__add_parameter(ftype, parm); else { /* * DW_TAG_formal_parameters on a non DW_TAG_subprogram nor * DW_TAG_subroutine_type tag happens sometimes, likely due to * compiler optimizing away a inline expansion (at least this * was observed in some cases, such as in the Linux kernel * current_kernel_time function circa 2.6.20-rc5), keep it in * the lexblock tag list because it can be referenced as an * DW_AT_abstract_origin in another DW_TAG_formal_parameter. */ lexblock__add_tag(lexblock, &parm->tag); } } static void die__create_new_label(Dwarf_Die *die, struct lexblock *lexblock) { struct label *label = label__new(die); if (label == NULL) oom("label__new"); lexblock__add_label(lexblock, label); } static struct tag *die__create_new_variable(Dwarf_Die *die) { struct variable *var = variable__new(die); if (var == NULL) oom("variable__new"); return &var->tag; } static struct tag *die__create_new_subroutine_type(Dwarf_Die *die) { Dwarf_Die child; struct ftype *ftype = ftype__new(die); if (ftype == NULL) oom("ftype__new"); if (!dwarf_haschildren(die) || dwarf_child(die, &child) != 0) goto out; die = &child; do { switch (dwarf_tag(die)) { case DW_TAG_formal_parameter: die__create_new_parameter(die, ftype, NULL); break; case DW_TAG_unspecified_parameters: ftype->unspec_parms = 1; break; default: cu__tag_not_handled(die); break; } } while (dwarf_siblingof(die, die) == 0); out: return &ftype->tag; } static struct tag *die__create_new_enumeration(Dwarf_Die *die, struct cu *cu) { Dwarf_Die child; struct type *enumeration = type__new(die); if (enumeration == NULL) oom("class__new"); if (!dwarf_haschildren(die) || dwarf_child(die, &child) != 0) { fprintf(stderr, "%s: DW_TAG_enumeration_type with no " "children!\n", __FUNCTION__); return NULL; } die = &child; do { struct enumerator *enumerator; if (dwarf_tag(die) != DW_TAG_enumerator) { cu__tag_not_handled(die); continue; } enumerator = enumerator__new(die); if (enumerator == NULL) oom("enumerator__new"); enumeration__add(enumeration, enumerator); hashtags__hash(cu->hash_tags, &enumerator->tag); } while (dwarf_siblingof(die, die) == 0); return &enumeration->namespace.tag; } static void die__process_class(Dwarf_Die *die, struct type *class, struct cu *cu) { do { switch (dwarf_tag(die)) { case DW_TAG_inheritance: case DW_TAG_member: { struct class_member *member = class_member__new(die); if (member == NULL) oom("class_member__new"); type__add_member(class, member); hashtags__hash(cu->hash_tags, &member->tag); } continue; default: { struct tag *tag = die__process_tag(die, cu); if (tag != NULL) { namespace__add_tag(&class->namespace, tag); hashtags__hash(cu->hash_tags, tag); if (tag->tag == DW_TAG_subprogram) { struct function *fself = tag__function(tag); if (fself->vtable_entry != -1) class__add_vtable_entry(type__class(class), fself); } } continue; } } } while (dwarf_siblingof(die, die) == 0); } static void die__process_namespace(Dwarf_Die *die, struct namespace *namespace, struct cu *cu) { do { struct tag *tag = die__process_tag(die, cu); if (tag != NULL) { namespace__add_tag(namespace, tag); hashtags__hash(cu->hash_tags, tag); } } while (dwarf_siblingof(die, die) == 0); } static void die__process_function(Dwarf_Die *die, struct ftype *ftype, struct lexblock *lexblock, struct cu *cu); static void die__create_new_lexblock(Dwarf_Die *die, struct cu *cu, struct lexblock *father) { struct lexblock *lexblock = lexblock__new(die); if (lexblock == NULL) oom("lexblock__new"); die__process_function(die, NULL, lexblock, cu); lexblock__add_lexblock(father, lexblock); } static void die__create_new_inline_expansion(Dwarf_Die *die, struct lexblock *lexblock) { struct inline_expansion *exp = inline_expansion__new(die); if (exp == NULL) oom("inline_expansion__new"); lexblock__add_inline_expansion(lexblock, exp); } static void die__process_function(Dwarf_Die *die, struct ftype *ftype, struct lexblock *lexblock, struct cu *cu) { Dwarf_Die child; if (!dwarf_haschildren(die) || dwarf_child(die, &child) != 0) return; die = &child; do { switch (dwarf_tag(die)) { case DW_TAG_formal_parameter: die__create_new_parameter(die, ftype, lexblock); continue; case DW_TAG_variable: { struct tag *tag = die__create_new_variable(die); lexblock__add_variable(lexblock, tag__variable(tag)); } continue; case DW_TAG_unspecified_parameters: if (ftype != NULL) ftype->unspec_parms = 1; continue; case DW_TAG_label: die__create_new_label(die, lexblock); continue; case DW_TAG_inlined_subroutine: die__create_new_inline_expansion(die, lexblock); continue; case DW_TAG_lexical_block: die__create_new_lexblock(die, cu, lexblock); continue; default: { struct tag *tag = die__process_tag(die, cu); if (tag != NULL) cu__add_tag(cu, tag); } } } while (dwarf_siblingof(die, die) == 0); } static struct tag *die__create_new_function(Dwarf_Die *die, struct cu *cu) { struct function *function = function__new(die); if (function == NULL) oom("function__new"); die__process_function(die, &function->proto, &function->lexblock, cu); return &function->proto.tag; } static struct tag *__die__process_tag(Dwarf_Die *die, struct cu *cu, const char *fn) { switch (dwarf_tag(die)) { case DW_TAG_array_type: return die__create_new_array(die); case DW_TAG_base_type: return die__create_new_base_type(die); case DW_TAG_const_type: case DW_TAG_imported_declaration: case DW_TAG_imported_module: case DW_TAG_pointer_type: case DW_TAG_reference_type: case DW_TAG_volatile_type: return die__create_new_tag(die); case DW_TAG_enumeration_type: return die__create_new_enumeration(die, cu); case DW_TAG_namespace: return die__create_new_namespace(die, cu); case DW_TAG_structure_type: return die__create_new_class(die, cu); case DW_TAG_subprogram: return die__create_new_function(die, cu); case DW_TAG_subroutine_type: return die__create_new_subroutine_type(die); case DW_TAG_typedef: return die__create_new_typedef(die); case DW_TAG_union_type: return die__create_new_union(die, cu); case DW_TAG_variable: return die__create_new_variable(die); default: __cu__tag_not_handled(die, fn); } return NULL; } static void die__process_unit(Dwarf_Die *die, struct cu *cu) { do { struct tag *tag = die__process_tag(die, cu); if (tag != NULL) cu__add_tag(cu, tag); } while (dwarf_siblingof(die, die) == 0); } static void die__process(Dwarf_Die *die, struct cu *cu) { Dwarf_Die child; const uint16_t tag = dwarf_tag(die); if (tag != DW_TAG_compile_unit) { fprintf(stderr, "%s: DW_TAG_compile_unit expected got %s!\n", __FUNCTION__, dwarf_tag_name(tag)); return; } cu->language = attr_numeric(die, DW_AT_language); if (dwarf_child(die, &child) == 0) die__process_unit(&child, cu); if (dwarf_siblingof(die, die) == 0) fprintf(stderr, "%s: got %s unexpected tag after " "DW_TAG_compile_unit!\n", __FUNCTION__, dwarf_tag_name(tag)); } static int cus__load_module(Dwfl_Module *mod, void **userdata __unused, const char *name __unused, Dwarf_Addr base __unused, Dwarf *dw, Dwarf_Addr bias __unused, void *self) { Dwarf_Off off = 0, noff; size_t cuhl; GElf_Addr vaddr; const unsigned char *build_id = NULL; #ifdef HAVE_DWFL_MODULE_BUILD_ID int build_id_len = dwfl_module_build_id(mod, &build_id, &vaddr); #else int build_id_len = 0; #endif while (dwarf_nextcu(dw, off, &noff, &cuhl, NULL, NULL, NULL) == 0) { Dwarf_Die die_mem, tmp; Dwarf_Die *cu_die = dwarf_offdie(dw, off + cuhl, &die_mem); struct cu *cu; uint8_t pointer_size, offset_size; dwarf_diecu(cu_die, &tmp, &pointer_size, &offset_size); cu = cu__new(attr_string(cu_die, DW_AT_name), pointer_size, build_id, build_id_len); if (cu == NULL) oom("cu__new"); die__process(cu_die, cu); cus__add(self, cu); off = noff; } return DWARF_CB_OK; } int dwarf__load_filename(struct cus *self, const char *filename) { Dwarf_Off offset, last_offset, abbrev_offset; uint8_t addr_size, offset_size; size_t hdr_size; Dwarf *dwarf; int fd = open(filename, O_RDONLY); int err; if (fd < 0) { err = errno; goto out; } err = -EINVAL; dwarf = dwarf_begin(fd, DWARF_C_READ); if (dwarf == NULL) goto out_close; offset = last_offset = 0; while (dwarf_nextcu(dwarf, offset, &offset, &hdr_size, &abbrev_offset, &addr_size, &offset_size) == 0) { Dwarf_Die die; if (dwarf_offdie(dwarf, last_offset + hdr_size, &die) != NULL) { struct cu *cu = cu__new(attr_string(&die, DW_AT_name), addr_size, NULL, 0); if (cu == NULL) oom("cu__new"); die__process(&die, cu); cus__add(self, cu); } last_offset = offset; } dwarf_end(dwarf); err = 0; out_close: close(fd); out: return err; } static int with_executable_option(int argc, char *argv[]) { while (--argc != 0) if (strcmp(argv[argc], "--help") == 0 || strcmp(argv[argc], "-?") == 0 || strcmp(argv[argc], "-h") == 0 || strcmp(argv[argc], "--usage") == 0 || strcmp(argv[argc], "--executable") == 0 || (argv[argc][0] == '-' && argv[argc][1] != '-' && strchr(argv[argc] + 1, 'e') != NULL)) return 1; return 0; } int dwarf__load(struct cus *self, struct argp *argp, int argc, char *argv[]) { Dwfl *dwfl = NULL; char **new_argv = NULL; ptrdiff_t offset; int err = -1; if (argc == 1) { argp_help(argp ? : dwfl_standard_argp(), stderr, ARGP_HELP_SEE, argv[0]); return -1; } if (!with_executable_option(argc, argv)) { new_argv = malloc((argc + 2) * sizeof(char *)); if (new_argv == NULL) { fprintf(stderr, "%s: not enough memory!\n", __func__); return -1; } memcpy(new_argv, argv, (argc - 1) * sizeof(char *)); new_argv[argc - 1] = "-e"; new_argv[argc] = argv[argc - 1]; new_argv[argc + 1] = NULL; argv = new_argv; argc++; } if (argp != NULL) { const struct argp_child argp_children[] = { { .argp = dwfl_standard_argp(), }, { .argp = NULL } }; argp->children = argp_children; argp_parse(argp, argc, argv, 0, NULL, &dwfl); } else argp_parse(dwfl_standard_argp(), argc, argv, 0, NULL, &dwfl); if (dwfl == NULL) goto out; offset = 0; do { offset = dwfl_getdwarf(dwfl, cus__load_module, self, offset); } while (offset > 0); dwfl_end(dwfl); err = 0; out: free(new_argv); return err; }