| /* Copyright (C) 2021-2025 Free Software Foundation, Inc. |
| Contributed by Oracle. |
| |
| This file is part of GNU Binutils. |
| |
| This program is free software; you can redistribute it and/or modify |
| it under the terms of the GNU General Public License as published by |
| the Free Software Foundation; either version 3, or (at your option) |
| any later version. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with this program; if not, write to the Free Software |
| Foundation, 51 Franklin Street - Fifth Floor, Boston, |
| MA 02110-1301, USA. */ |
| |
| #include "config.h" |
| #include "util.h" |
| #include "DbeSession.h" |
| #include "Elf.h" |
| #include "Stabs.h" |
| #include "Dwarf.h" |
| #include "DataObject.h" |
| #include "Function.h" |
| #include "LoadObject.h" |
| #include "Module.h" |
| #include "DefaultMap.h" |
| #include "Symbol.h" |
| |
| static int |
| datatypeCmp (const void *a, const void *b) |
| { |
| uint32_t o1 = ((datatype_t *) a)->datatype_id; |
| uint32_t o2 = ((datatype_t *) b)->datatype_id; |
| return o1 == o2 ? 0 : (o1 < o2 ? -1 : 1); |
| } |
| |
| static int |
| targetOffsetCmp (const void *a, const void *b) |
| { |
| uint32_t o1 = ((target_info_t *) a)->offset; |
| uint32_t o2 = ((target_info_t *) b)->offset; |
| return o1 == o2 ? 0 : (o1 < o2 ? -1 : 1); |
| } |
| |
| ////////////////////////////////////////////////////////// |
| // class Dwr_type |
| class Dwr_type |
| { |
| public: |
| |
| Dwr_type (int64_t _cu_die_offset, int _tag) |
| { |
| cu_die_offset = _cu_die_offset; |
| tag = _tag; |
| name = NULL; |
| dobj_name = NULL; |
| dtype = NULL; |
| extent = 0; |
| parent = 0; |
| child = 0; |
| next = 0; |
| ref_type = 0; |
| size = 0; |
| elems = 0; |
| offset = -1; |
| bit_size = 0; |
| }; |
| |
| char *name, *dobj_name; |
| int64_t cu_die_offset, ref_type, extent, parent, child, next; |
| int64_t size, elems, offset; |
| int tag, bit_size; |
| |
| DataObject *get_dobj (Dwarf_cnt *ctx); |
| char *get_dobjname (Dwarf_cnt *ctx); |
| char *dump (); |
| |
| private: |
| datatype_t *dtype; |
| datatype_t *get_datatype (Dwarf_cnt *ctx); |
| void get_dobj_for_members (Dwarf_cnt *ctx); |
| void set_dobjname (char *spec, char *nm); |
| }; |
| |
| |
| ////////////////////////////////////////////////////////// |
| // class Dwarf_cnt |
| Dwarf_cnt::Dwarf_cnt () |
| { |
| cu_offset = 0; |
| parent = 0; |
| module = NULL; |
| name = NULL; |
| func = NULL; |
| fortranMAIN = NULL; |
| dwr_types = NULL; |
| inlinedSubr = NULL; |
| level = 0; |
| } |
| |
| Dwr_type * |
| Dwarf_cnt::get_dwr_type (int64_t cu_die_offset) |
| { |
| Dwr_type *t = dwr_types->get (cu_die_offset); |
| if (t == NULL) |
| { |
| Dprintf (DUMP_DWARFLIB, "DWARF_ERROR: %s:%d wrong cu_die_offset=%lld in Dwarf_cnt::get_dwr_type\n", |
| get_basename (__FILE__), (int) __LINE__, |
| (long long) cu_die_offset); |
| t = put_dwr_type (cu_die_offset, 0); // DOBJ_UNSPECIFIED |
| } |
| return t; |
| } |
| |
| Dwr_type * |
| Dwarf_cnt::put_dwr_type (int64_t cu_die_offset, int tag) |
| { |
| Dwr_type *t = new Dwr_type (cu_die_offset, tag); |
| dwr_types->put (cu_die_offset, t); |
| return t; |
| } |
| |
| Dwr_type * |
| Dwarf_cnt::put_dwr_type (Dwr_Tag *dwrTag) |
| { |
| Dwr_type *t = new Dwr_type (dwrTag->die, dwrTag->tag); |
| dwr_types->put (dwrTag->die, t); |
| return t; |
| } |
| |
| ////////////////////////////////////////////////////////// |
| // class Dwr_type |
| char * |
| Dwr_type::dump () |
| { |
| char *s = dbe_sprintf ("%lld %-15s name='%s' parent=%lld next=%lld child=%lld dtype=%llx", |
| (long long) cu_die_offset, DwrCU::tag2str (tag), |
| STR (name), (long long) parent, (long long) next, |
| (long long) child, (long long) dtype); |
| return s; |
| } |
| |
| void |
| Dwr_type::set_dobjname (char *spec, char *nm) |
| { |
| if (spec) |
| { |
| if (nm) |
| dobj_name = dbe_sprintf ("%s%s", spec, nm); |
| else |
| dobj_name = dbe_sprintf ("%s<ANON=%lld>", spec, |
| (long long) cu_die_offset); |
| } |
| else |
| { |
| if (nm) |
| dobj_name = dbe_sprintf ("%s", nm); |
| else |
| dobj_name = dbe_sprintf ("<ANON=%lld>", (long long) cu_die_offset); |
| } |
| } |
| |
| char * |
| Dwr_type::get_dobjname (Dwarf_cnt *ctx) |
| { |
| if (dobj_name) |
| return dobj_name; |
| switch (tag) |
| { |
| case DW_TAG_base_type: |
| set_dobjname (NULL, name); |
| for (int i = 0, len = (int) strlen (dobj_name); i < len; i++) |
| { |
| if (dobj_name[i] == ' ') |
| dobj_name[i] = '_'; |
| } |
| break; |
| case DW_TAG_constant: |
| case DW_TAG_formal_parameter: |
| case DW_TAG_variable: |
| { |
| Dwr_type *t = ctx->get_dwr_type (ref_type); |
| set_dobjname (NULL, t->get_dobjname (ctx)); |
| break; |
| } |
| case DW_TAG_unspecified_type: |
| set_dobjname (NTXT ("unspecified:"), name); |
| break; |
| case DW_TAG_enumeration_type: |
| set_dobjname (NTXT ("enumeration:"), name); |
| break; |
| case DW_TAG_typedef: |
| { |
| Dwr_type *t = ctx->get_dwr_type (ref_type); |
| dobj_name = dbe_sprintf ("%s=%s", name, t->get_dobjname (ctx)); |
| break; |
| } |
| case DW_TAG_const_type: |
| set_dobjname (NTXT ("const+"), name); |
| break; |
| case DW_TAG_volatile_type: |
| set_dobjname (NTXT ("volatile+"), name); |
| break; |
| case DW_TAG_pointer_type: |
| { |
| Dwr_type *t = ctx->get_dwr_type (ref_type); |
| set_dobjname (NTXT ("pointer+"), t->get_dobjname (ctx)); |
| break; |
| } |
| case DW_TAG_reference_type: |
| { |
| Dwr_type *t = ctx->get_dwr_type (ref_type); |
| set_dobjname (NTXT ("reference+"), t->get_dobjname (ctx)); |
| break; |
| } |
| case DW_TAG_array_type: |
| { |
| Dwr_type *t = ctx->get_dwr_type (ref_type); |
| if (elems > 0) |
| dobj_name = dbe_sprintf ("array[%lld]:%s", |
| (long long) elems, t->get_dobjname (ctx)); |
| else |
| dobj_name = dbe_sprintf ("array[]:%s", t->get_dobjname (ctx)); |
| break; |
| } |
| case DW_TAG_structure_type: |
| set_dobjname (NTXT ("structure:"), name); |
| break; |
| case DW_TAG_union_type: |
| set_dobjname (NTXT ("union:"), name); |
| break; |
| case DW_TAG_class_type: |
| set_dobjname (NTXT ("class:"), name); |
| break; |
| case DW_TAG_member: |
| { |
| Dwr_type *t = ctx->get_dwr_type (ref_type); |
| if (bit_size > 0) |
| dobj_name = dbe_sprintf (NTXT ("%s:%lld"), t->get_dobjname (ctx), |
| (long long) bit_size); |
| else |
| dobj_name = dbe_sprintf (NTXT ("%s"), t->get_dobjname (ctx)); |
| break; |
| } |
| default: |
| Dprintf (DUMP_DWARFLIB, NTXT ("DWARF_ERROR: %s:%d No case for %s cu_die_offset=%lld\n"), |
| get_basename (__FILE__), (int) __LINE__, |
| DwrCU::tag2str (tag), (long long) cu_die_offset); |
| set_dobjname (NTXT ("Undefined:"), NULL); |
| break; |
| } |
| return dobj_name; |
| } |
| |
| datatype_t* |
| Dwr_type::get_datatype (Dwarf_cnt *ctx) |
| { |
| if (dtype) |
| return dtype; |
| dtype = new datatype_t; |
| dtype->datatype_id = (unsigned) cu_die_offset; |
| dtype->memop_refs = 0; |
| dtype->event_data = 0; |
| dtype->dobj = NULL; |
| ctx->module->datatypes->incorporate (dtype, datatypeCmp); |
| return dtype; |
| } |
| |
| DataObject * |
| Dwr_type::get_dobj (Dwarf_cnt *ctx) |
| { |
| if (dtype == NULL) |
| dtype = get_datatype (ctx); |
| dtype->memop_refs++; |
| DataObject *dobj = dtype->dobj; |
| if (dobj) |
| return dobj; |
| |
| if (tag == 0) |
| dobj = dbeSession->find_dobj_by_name (PTXT (DOBJ_UNSPECIFIED)); |
| else |
| { |
| dobj = dbeSession->createDataObject (); |
| dobj->size = size; |
| dobj->offset = offset; |
| dobj->scope = ctx->func ? (Histable*) ctx->func : (Histable*) ctx->module; |
| } |
| dtype->dobj = dobj; |
| if (parent) |
| { |
| Dwr_type *t = ctx->get_dwr_type (parent); |
| dobj->parent = t->get_dobj (ctx); |
| } |
| |
| if (ref_type) |
| { |
| Dwr_type *t = ctx->get_dwr_type (ref_type); |
| t->get_dobj (ctx); |
| if (size == 0) |
| { |
| size = t->size; |
| dobj->size = size; |
| } |
| } |
| |
| switch (tag) |
| { |
| case 0: |
| break; |
| case DW_TAG_array_type: |
| case DW_TAG_base_type: |
| case DW_TAG_unspecified_type: |
| case DW_TAG_enumeration_type: |
| case DW_TAG_typedef: |
| case DW_TAG_const_type: |
| case DW_TAG_volatile_type: |
| case DW_TAG_pointer_type: |
| case DW_TAG_reference_type: |
| dobj->set_dobjname (get_dobjname (ctx), NULL); |
| break; |
| case DW_TAG_structure_type: |
| case DW_TAG_union_type: |
| case DW_TAG_class_type: |
| dobj->set_dobjname (get_dobjname (ctx), NULL); |
| dobj->master = dbeSession->find_dobj_by_name (dobj_name); |
| get_dobj_for_members (ctx); |
| break; |
| case DW_TAG_constant: |
| case DW_TAG_formal_parameter: |
| case DW_TAG_member: |
| case DW_TAG_variable: |
| if (dobj->parent == NULL) |
| dobj->parent = dbeSession->get_Scalars_DataObject (); |
| dobj->set_dobjname (get_dobjname (ctx), name); |
| break; |
| default: |
| Dprintf (DUMP_DWARFLIB, NTXT ("DWARF_ERROR: %s:%d No case for %s cu_die_offset=%lld\n"), |
| get_basename (__FILE__), (int) __LINE__, |
| DwrCU::tag2str (tag), (long long) cu_die_offset); |
| break; |
| } |
| return dobj; |
| } |
| |
| void |
| Dwr_type::get_dobj_for_members (Dwarf_cnt *ctx) |
| { |
| for (int64_t i = child; i != 0;) |
| { |
| Dwr_type *t = ctx->get_dwr_type (i); |
| t->get_dobj (ctx); |
| i = t->next; |
| } |
| } |
| |
| ////////////////////////////////////////////////////////// |
| // class Dwarf |
| Dwarf::Dwarf (Stabs *_stabs) |
| { |
| stabs = _stabs; |
| status = Stabs::DBGD_ERR_NONE; |
| dwrCUs = 0; |
| debug_infoSec = NULL; |
| debug_abbrevSec = NULL; |
| debug_strSec = NULL; |
| debug_alt_strSec = NULL; |
| debug_lineSec = NULL; |
| debug_line_strSec = NULL; |
| debug_rangesSec = NULL; |
| elf = stabs->openElf (true); |
| if (elf == NULL) |
| { |
| status = Stabs::DBGD_ERR_BAD_ELF_FORMAT; |
| return; |
| } |
| debug_infoSec = dwrGetSec (NTXT (".debug_info")); |
| debug_abbrevSec = dwrGetSec (NTXT (".debug_abbrev")); |
| debug_strSec = dwrGetSec (NTXT (".debug_str")); |
| debug_lineSec = dwrGetSec (NTXT (".debug_line")); |
| debug_rangesSec = dwrGetSec (NTXT (".debug_ranges")); |
| debug_line_strSec = dwrGetSec (".debug_line_str"); |
| debug_rnglists = NULL; |
| if (elf->gnu_debugalt_file) |
| debug_alt_strSec = elf->gnu_debugalt_file->get_dwr_section (".debug_str"); |
| |
| if ((debug_infoSec == NULL) || (debug_abbrevSec == NULL) || (debug_lineSec == NULL)) |
| { |
| status = Stabs::DBGD_ERR_NO_DWARF; |
| return; |
| } |
| } |
| |
| Dwarf::~Dwarf () |
| { |
| Destroy (dwrCUs); |
| } |
| |
| DwrSec * |
| Dwarf::dwrGetSec (const char *sec_name) |
| { |
| DwrSec *p = elf->get_dwr_section (sec_name); |
| if (p) |
| p->offset = 0; |
| return p; |
| } |
| |
| uint64_t |
| DwrCU::get_low_pc () |
| { |
| uint64_t pc = Dwarf_addr (DW_AT_low_pc); |
| if (pc) |
| return pc; |
| return pc; |
| } |
| |
| char * |
| DwrCU::get_linkage_name () |
| { |
| char *nm = Dwarf_string (DW_AT_linkage_name); |
| if (nm != NULL) |
| return nm; |
| nm = Dwarf_string (DW_AT_SUN_link_name); |
| if (nm != NULL) |
| return nm; |
| if (nm != NULL) |
| return nm; |
| nm = Dwarf_string (DW_AT_MIPS_linkage_name); |
| if (nm != NULL) |
| return nm; |
| return Dwarf_string (DW_AT_name); |
| } |
| |
| void |
| DwrCU::parseChild (Dwarf_cnt *ctx) |
| { |
| if (!dwrTag.hasChild) |
| return; |
| uint64_t old_size = debug_infoSec->size; |
| uint64_t next_die_offset = 0; |
| Dwarf_Die next_die; |
| if (read_ref_attr (DW_AT_sibling, &next_die) == DW_DLV_OK) |
| { |
| next_die_offset = next_die; |
| if (next_die_offset <= debug_infoSec->offset) |
| { |
| Dprintf (DEBUG_ERR_MSG, NTXT ("DwrCU::parseChild: next_die(0x%llx) <= debug_infoSec->offset(%llx)\n"), |
| (long long) next_die, (long long) debug_infoSec->offset); |
| next_die_offset = 0; |
| } |
| else if (debug_infoSec->size > next_die_offset) |
| debug_infoSec->size = next_die_offset; |
| } |
| dwrTag.level++; |
| ctx->level++; |
| for (;;) |
| { |
| if (set_die (0) != DW_DLV_OK) |
| break; |
| Function *func; |
| char *old_name; |
| int hasChild = dwrTag.hasChild; |
| switch (dwrTag.tag) |
| { |
| case DW_TAG_imported_declaration: |
| if (Stabs::is_fortran (ctx->module->lang_code)) |
| { |
| char *link_name = Dwarf_string (DW_AT_name); |
| ctx->fortranMAIN = NULL; |
| parseChild (ctx); |
| hasChild = 0; |
| if (ctx->fortranMAIN) |
| { |
| ctx->fortranMAIN->set_match_name (link_name); |
| ctx->fortranMAIN = NULL; |
| } |
| } |
| break; |
| case DW_TAG_subprogram: |
| { |
| Symbol *sym = NULL; |
| Vector<Symbol *> *syms = NULL; |
| Dwr_Attr *dwrAttr = dwrTag.get_attr (DW_AT_abstract_origin); |
| if (dwrAttr) |
| { |
| // Set up functions from DW_AT_{ranges,low_pc,linkage_name} |
| set_up_funcs (dwrAttr->u.offset); |
| break; |
| } |
| |
| dwrAttr = dwrTag.get_attr (DW_AT_specification); |
| if (dwrAttr) |
| { |
| // Set up functions from DW_AT_{ranges,low_pc,linkage_name} |
| set_up_funcs (dwrAttr->u.offset); |
| break; |
| } |
| |
| if (dwrTag.get_attr (DW_AT_declaration)) |
| { |
| // Only declaration |
| if (Stabs::is_fortran (ctx->module->lang_code)) |
| { |
| char *link_name = Dwarf_string (DW_AT_name); |
| if (link_name && streq (link_name, NTXT ("MAIN"))) |
| ctx->fortranMAIN = Stabs::find_func (NTXT ("MAIN"), |
| ctx->module->functions, true, true); |
| } |
| sym = Symbol::get_symbol (symbols_sorted_by_name, |
| get_linkage_name ()); |
| if (sym != NULL) |
| func = append_Function (sym, ctx->name); |
| break; |
| } |
| |
| func = NULL; |
| syms = get_symbols (tmp_syms); |
| for (int i = 0, sz = VecSize (syms); i < sz; i++) |
| { |
| sym = syms->get (i); |
| func = append_Function (sym, ctx->name); |
| if (Stabs::is_fortran (ctx->module->lang_code) && |
| streq (func->get_match_name (), "MAIN")) |
| ctx->fortranMAIN = func; |
| } |
| if (func == NULL) |
| break; |
| |
| old_name = ctx->name; |
| Function *old_func = ctx->func; |
| ctx->name = func->get_match_name (); |
| ctx->func = func; |
| parseChild (ctx); |
| hasChild = 0; |
| ctx->name = old_name; |
| ctx->func = old_func; |
| break; |
| } |
| case DW_TAG_module: |
| old_name = ctx->name; |
| ctx->name = Dwarf_string (DW_AT_SUN_link_name); |
| parseChild (ctx); |
| hasChild = 0; |
| ctx->name = old_name; |
| break; |
| case DW_TAG_class_type: |
| old_name = ctx->name; |
| ctx->name = Dwarf_string (DW_AT_name); |
| parseChild (ctx); |
| hasChild = 0; |
| ctx->name = old_name; |
| break; |
| case DW_TAG_structure_type: |
| old_name = ctx->name; |
| ctx->name = NULL; |
| parseChild (ctx); |
| hasChild = 0; |
| ctx->name = old_name; |
| break; |
| case DW_TAG_namespace: |
| old_name = ctx->name; |
| ctx->name = Dwarf_string (DW_AT_name); |
| parseChild (ctx); |
| hasChild = 0; |
| ctx->name = old_name; |
| break; |
| case DW_TAG_lexical_block: |
| old_name = ctx->name; |
| ctx->name = NULL; |
| parseChild (ctx); |
| hasChild = 0; |
| ctx->name = old_name; |
| break; |
| case DW_TAG_SUN_memop_info: |
| isMemop = true; |
| break; |
| case DW_TAG_inlined_subroutine: |
| if (ctx->module) |
| { |
| parse_inlined_subroutine (ctx); |
| hasChild = 0; |
| } |
| break; |
| default: // No more special cases |
| break; |
| } |
| if (hasChild) |
| parseChild (ctx); |
| } |
| ctx->level--; |
| dwrTag.level--; |
| if (next_die_offset != 0) |
| debug_infoSec->offset = next_die_offset; |
| debug_infoSec->size = old_size; |
| } |
| |
| bool |
| Dwarf::archive_Dwarf (LoadObject *lo) |
| { |
| if (debug_infoSec == NULL) |
| return false; |
| if (dwrCUs) |
| return true; |
| dwrCUs = new Vector<DwrCU *>; |
| |
| debug_infoSec->offset = 0; |
| while (debug_infoSec->offset < debug_infoSec->sizeSec) |
| { |
| DwrCU *dwrCU = new DwrCU (this); |
| dwrCUs->append (dwrCU); |
| debug_infoSec->size = debug_infoSec->sizeSec; |
| debug_infoSec->offset = dwrCU->next_cu_offset; |
| |
| if (dwrCU->set_die (dwrCU->cu_header_offset) != DW_DLV_OK) |
| { |
| Dprintf (1, "DwrCU::archive_Dwarf: CU=%lld (offset=0x%llx); set_die(0x%llx) failed\n", |
| (long long) dwrCUs->size (), (long long) dwrCU->cu_offset, |
| (long long) dwrCU->cu_header_offset); |
| continue; |
| } |
| |
| Module *mod = dwrCU->parse_cu_header (lo); |
| if (mod) |
| { |
| mod->hdrOffset = dwrCUs->size (); |
| DwrLineRegs *lineReg = dwrCU->get_dwrLineReg (); |
| if (lineReg != NULL) |
| { |
| dwrCU->srcFiles = new Vector<SourceFile *> (VecSize (lineReg->file_names)); |
| for (long i = 0, sz = VecSize (lineReg->file_names); i < sz; i++) |
| { |
| char *fname = lineReg->getPath (i); |
| if (fname) |
| dwrCU->srcFiles->append (mod->findSource (fname, true)); |
| } |
| } |
| |
| Dwarf_cnt ctx; |
| ctx.module = mod; |
| dwrCU->parseChild (&ctx); |
| if (dwrCU->dwrInlinedSubrs && DUMP_DWARFLIB) |
| { |
| char msg[128]; |
| char *lo_name = mod->loadobject ? mod->loadobject->get_name () |
| : NULL; |
| snprintf (msg, sizeof (msg), NTXT ("\ndwrCUs[%lld]: %s:%s\n"), |
| (long long) dwrCUs->size (), |
| STR (lo_name), STR (mod->get_name ())); |
| dwrCU->dwrInlinedSubrs->dump (msg); |
| } |
| for (int i = 0, sz = VecSize (dwrCU->symbols); i < sz; i++) |
| { |
| Symbol *sp = dwrCU->symbols->get (i); |
| Function *f = sp->func; |
| if (f == NULL) |
| { |
| f = sp->createFunction (mod); |
| if (sp->alias && sp->alias->func) |
| { |
| Function *func = sp->alias->func; |
| f->setLineFirst (func->line_first); |
| f->setDefSrc (func->def_source); |
| } |
| } |
| } |
| } |
| } |
| return true; |
| } |
| |
| void |
| Dwarf::srcline_Dwarf (Module *module) |
| { |
| if (module == NULL || module->hdrOffset == 0) |
| return; |
| DwrCU *dwrCU = dwrCUs->get (module->hdrOffset - 1); |
| dwrCU->map_dwarf_lines (module); |
| } |
| |
| Vector<Range *> * |
| Dwarf::get_ranges (uint64_t offset) |
| { |
| if (debug_rangesSec == NULL) |
| return NULL; |
| if (offset >= debug_rangesSec->size) |
| { |
| Dprintf (DUMP_DWARFLIB, "ERROR: Dwarf::get_ranges(0x%llx). size=0x%llx\n", |
| (long long) offset, (long long) debug_rangesSec->size); |
| return NULL; |
| } |
| Vector<Range*> *ranges = new Vector<Range*>(); |
| debug_rangesSec->offset = offset; |
| for (;;) |
| { |
| uint64_t low_pc = debug_rangesSec->GetADDR (); |
| uint64_t high_pc = debug_rangesSec->GetADDR (); |
| if (low_pc == 0 || low_pc > high_pc) |
| break; |
| ranges->append (new Range (low_pc, high_pc)); |
| } |
| return ranges; |
| } |
| |
| // parse hwcprof info for given module in loadobject |
| |
| void |
| Dwarf::read_hwcprof_info (Module *module) |
| { |
| if (module->datatypes || (module->hdrOffset == 0)) |
| return; |
| DwrCU *dwrCU = dwrCUs->get (module->hdrOffset - 1); |
| if (!dwrCU->isMemop) |
| return; |
| module->datatypes = new Vector<datatype_t*>; |
| if (dwrCU->set_die (dwrCU->cu_header_offset) != DW_DLV_OK) |
| { |
| Dprintf (1, "Dwarf::read_hwcprof_info: CU=%lld (offset=0x%llx); set_die(0x%llx) failed\n", |
| (long long) module->hdrOffset, (long long) dwrCU->cu_offset, |
| (long long) dwrCU->cu_header_offset); |
| return; |
| } |
| Dwarf_cnt ctx; |
| ctx.module = module; |
| ctx.cu_offset = dwrCU->cu_offset; // CU header offset; |
| ctx.dwr_types = new DefaultMap<int64_t, Dwr_type*>; |
| ctx.put_dwr_type (0, 0); // for DOBJ_UNSPECIFIED |
| dwrCU->read_hwcprof_info (&ctx); |
| |
| Vector<inst_info_t*> *infoList = module->infoList; |
| Dprintf (DUMP_DWARFLIB, |
| "\n\n ### Dwarf::read_hwcprof_info: module: '%s' infoList->size()=%lld\n", |
| STR (module->get_name ()), |
| (long long) (infoList ? infoList->size () : -1)); |
| for (int i = 0, sz = infoList ? infoList->size () : -1; i < sz; i++) |
| { |
| inst_info_t *ip = infoList->fetch (i); |
| memop_info_t *mp = ip->memop; |
| Dwr_type *t = ctx.get_dwr_type (mp->datatype_id); |
| t->get_dobj (&ctx); |
| } |
| |
| #ifdef DEBUG |
| Dprintf (DUMP_DWARFLIB, |
| "\n\n ### Dwarf::read_hwcprof_info: '%s' infoList->size()=%lld\n", |
| STR (module->get_name ()), |
| (long long) (infoList ? infoList->size () : 1)); |
| for (int i = 0, sz = infoList ? infoList->size () : -1; i < sz; i++) |
| { |
| inst_info_t *ip = infoList->fetch (i); |
| memop_info_t *mp = ip->memop; |
| Dprintf (DUMP_DWARFLIB, |
| " %d id=%lld offset=%lld signature=%lld datatype_id=%lld \n", |
| i, (long long) mp->id, (long long) mp->offset, |
| (long long) mp->signature, (long long) mp->datatype_id); |
| } |
| |
| Vector<int64_t> *keys = ctx.dwr_types->keySet (); |
| Dprintf (DUMP_DWARFLIB, |
| "\n\n ### Dwarf::read_hwcprof_info: '%s' keys->size()=%lld\n", |
| STR (module->get_name ()), (long long) (keys ? keys->size () : -1)); |
| for (int i = 0, sz = keys->size (); i < sz; i++) |
| { |
| int64_t ind = keys->fetch (i); |
| Dwr_type *t = ctx.get_dwr_type (ind); |
| Dprintf (DUMP_DWARFLIB, NTXT (" %d %lld %s\n"), i, |
| (long long) ind, t->dump ()); |
| } |
| #endif |
| } |
| |
| void |
| DwrCU::read_hwcprof_info (Dwarf_cnt *ctx) |
| { |
| if (!dwrTag.hasChild) |
| return; |
| uint64_t old_size = debug_infoSec->size; |
| uint64_t next_die_offset = 0; |
| Dwarf_Die next_die; |
| if (read_ref_attr (DW_AT_sibling, &next_die) == DW_DLV_OK) |
| { |
| next_die_offset = next_die; |
| if (next_die_offset <= debug_infoSec->offset) |
| next_die_offset = 0; |
| else if (debug_infoSec->size > next_die_offset) |
| debug_infoSec->size = next_die_offset; |
| } |
| dwrTag.level++; |
| ctx->level++; |
| for (;;) |
| { |
| if (set_die (0) != DW_DLV_OK) |
| break; |
| Dprintf (DUMP_DWARFLIB, NTXT ("%s:%d <%lld:%lld> cu_die=%lld %-15s\n"), |
| get_basename (__FILE__), (int) __LINE__, (long long) ctx->level, |
| (long long) dwrTag.die, (long long) dwrTag.offset, |
| DwrCU::tag2str (dwrTag.tag)); |
| switch (dwrTag.tag) |
| { |
| case DW_TAG_SUN_memop_info: |
| { |
| if (ctx->func == NULL) |
| break; |
| Dwarf_Unsigned mid = Dwarf_data (DW_AT_SUN_profile_id); |
| Dwarf_Unsigned off = Dwarf_data (DW_AT_SUN_func_offset); |
| Dwarf_Unsigned sig = Dwarf_data (DW_AT_SUN_memop_signature); |
| Dwarf_Off ref = Dwarf_ref (DW_AT_SUN_memop_type_ref); |
| |
| // define memop entry |
| memop_info_t *memop = new memop_info_t; |
| memop->id = (unsigned) mid; |
| memop->signature = (unsigned) sig; |
| memop->datatype_id = ref ? (unsigned) ref : 0; |
| memop->offset = (unsigned) (ctx->func->img_offset + off); |
| |
| // define instop entry |
| inst_info_t *instop = new inst_info_t; |
| instop->type = CPF_INSTR_TYPE_PREFETCH; // XXXX UNKNOWN |
| instop->offset = memop->offset; |
| instop->memop = memop; |
| if (ctx->module->infoList == NULL) |
| ctx->module->infoList = new Vector<inst_info_t*>; |
| ctx->module->infoList->append (instop); |
| break; |
| } |
| case DW_TAG_SUN_codeflags: |
| { |
| if (ctx->func == NULL) |
| break; |
| Dwarf_Unsigned kind = Dwarf_data (DW_AT_SUN_cf_kind); |
| if (kind == DW_ATCF_SUN_branch_target) |
| { |
| DwrSec *secp = Dwarf_block (DW_AT_SUN_func_offsets); |
| if (secp) |
| { |
| int foffset = 0; |
| for (int i = 0; secp->offset < secp->size; i++) |
| { |
| int val = (int) secp->GetSLEB128 (); |
| if (i == 0 || val != 0) |
| { |
| foffset += val; |
| target_info_t *t = new target_info_t; |
| t->offset = (unsigned) (ctx->func->img_offset + foffset); |
| ctx->module->bTargets.incorporate (t, targetOffsetCmp); |
| } |
| } |
| delete(secp); |
| } |
| } |
| break; |
| } |
| case DW_TAG_subprogram: |
| { |
| Function *old_func = ctx->func; |
| ctx->func = NULL; |
| if (dwrTag.get_attr (DW_AT_abstract_origin) == NULL |
| && dwrTag.get_attr (DW_AT_declaration) == NULL) |
| { |
| Symbol *sym = Symbol::get_symbol (symbols_sorted_by_name, |
| get_linkage_name ()); |
| if (sym == NULL) |
| sym = Symbol::get_symbol (symbols, get_low_pc ()); |
| if (sym != NULL) |
| ctx->func = sym->func; |
| } |
| read_hwcprof_info (ctx); |
| ctx->func = old_func; |
| break; |
| } |
| case DW_TAG_base_type: |
| { |
| Dwr_type *t = ctx->put_dwr_type (dwrTag.die, dwrTag.tag); |
| t->name = Dwarf_string (DW_AT_name); |
| t->size = Dwarf_data (DW_AT_byte_size); |
| break; |
| } |
| case DW_TAG_unspecified_type: |
| ctx->put_dwr_type (dwrTag.die, dwrTag.tag); |
| break; |
| case DW_TAG_enumeration_type: |
| { |
| Dwr_type *t = ctx->put_dwr_type (dwrTag.die, dwrTag.tag); |
| t->name = Dwarf_string (DW_AT_name); |
| t->size = Dwarf_data (DW_AT_byte_size); |
| break; |
| } |
| case DW_TAG_constant: |
| case DW_TAG_formal_parameter: |
| case DW_TAG_variable: |
| case DW_TAG_typedef: |
| case DW_TAG_const_type: |
| case DW_TAG_volatile_type: |
| { |
| Dwr_type *t = ctx->put_dwr_type (dwrTag.die, dwrTag.tag); |
| t->name = Dwarf_string (DW_AT_name); |
| t->ref_type = Dwarf_ref (DW_AT_type); |
| break; |
| } |
| case DW_TAG_pointer_type: |
| case DW_TAG_reference_type: |
| { |
| Dwr_type *t = ctx->put_dwr_type (dwrTag.die, dwrTag.tag); |
| t->name = Dwarf_string (DW_AT_name); |
| t->ref_type = Dwarf_ref (DW_AT_type); |
| t->size = (dwarf->stabs->get_class () == W64) ? 8 : 4; |
| break; |
| } |
| case DW_TAG_array_type: |
| { |
| Dwr_type *t = ctx->put_dwr_type (dwrTag.die, dwrTag.tag); |
| t->name = Dwarf_string (DW_AT_name); |
| t->ref_type = Dwarf_ref (DW_AT_type); |
| t->size = Dwarf_data (DW_AT_byte_size); |
| ctx->size = -1; |
| read_hwcprof_info (ctx); |
| t->elems = ctx->size; |
| break; |
| } |
| case DW_TAG_subrange_type: |
| { |
| int64_t ref_type = Dwarf_ref (DW_AT_type); |
| int64_t hi = Dwarf_data (DW_AT_upper_bound); |
| int64_t lo = Dwarf_data (DW_AT_lower_bound); |
| int64_t ss = Dwarf_data (DW_AT_stride_size); |
| ctx->size = 1 + hi - lo; |
| if (ss != 0) |
| ctx->size /= ss; |
| Dprintf (DUMP_DWARFLIB, |
| "Got subrange [%lld:%lld:%lld] indexed <%lld>: size=%lld\n", |
| (long long) lo, (long long) hi, (long long) ss, |
| (long long) ref_type, (long long) ctx->size); |
| break; |
| } |
| case DW_TAG_structure_type: |
| case DW_TAG_union_type: |
| case DW_TAG_class_type: |
| { |
| Dwr_type *t = ctx->put_dwr_type (dwrTag.die, dwrTag.tag); |
| t->name = Dwarf_string (DW_AT_name); |
| t->size = Dwarf_data (DW_AT_byte_size); |
| t->extent = Dwarf_ref (DW_AT_sibling); |
| int64_t old_parent = ctx->parent; |
| ctx->parent = t->cu_die_offset; |
| |
| char *old_name = ctx->name; |
| ctx->name = (dwrTag.tag == DW_TAG_class_type) ? Dwarf_string (DW_AT_name) : NULL; |
| read_hwcprof_info (ctx); |
| ctx->name = old_name; |
| ctx->parent = old_parent; |
| // Reverse children |
| for (int64_t i = t->child, last = 0; i != 0;) |
| { |
| Dwr_type *t1 = ctx->get_dwr_type (i); |
| t->child = i; |
| i = t1->next; |
| t1->next = last; |
| last = t->child; |
| } |
| break; |
| } |
| case DW_TAG_member: |
| { |
| if (ctx->parent == 0) |
| { |
| Dprintf (DUMP_DWARFLIB, NTXT ("DWARF_ERROR: %s:%d %s cu_die_offset=%lld\n"), |
| get_basename (__FILE__), (int) __LINE__, |
| DwrCU::tag2str (dwrTag.tag), (long long) dwrTag.die); |
| break; |
| } |
| Dwr_type *t = ctx->put_dwr_type (dwrTag.die, dwrTag.tag); |
| t->name = Dwarf_string (DW_AT_name); |
| t->ref_type = Dwarf_ref (DW_AT_type); |
| t->offset = Dwarf_location (DW_AT_data_member_location); |
| Dwr_type *parent = ctx->get_dwr_type (ctx->parent); |
| t->parent = ctx->parent; |
| t->next = parent->child; // a reverse order of members |
| parent->child = t->cu_die_offset; |
| t->bit_size = (uint32_t) Dwarf_data (DW_AT_bit_size); |
| break; |
| } |
| case DW_TAG_module: |
| { |
| char *old_name = ctx->name; |
| ctx->name = Dwarf_string (DW_AT_SUN_link_name); |
| read_hwcprof_info (ctx); |
| ctx->name = old_name; |
| break; |
| } |
| case DW_TAG_namespace: |
| { |
| char *old_name = ctx->name; |
| ctx->name = Dwarf_string (DW_AT_name); |
| read_hwcprof_info (ctx); |
| ctx->name = old_name; |
| break; |
| } |
| case DW_TAG_lexical_block: |
| { |
| char *old_name = ctx->name; |
| ctx->name = NULL; |
| read_hwcprof_info (ctx); |
| ctx->name = old_name; |
| break; |
| } |
| default: // No more special cases |
| read_hwcprof_info (ctx); |
| break; |
| } |
| } |
| ctx->level--; |
| dwrTag.level--; |
| if (next_die_offset != 0) |
| debug_infoSec->offset = next_die_offset; |
| debug_infoSec->size = old_size; |
| } |
| |
| // Append function to module |
| Function * |
| DwrCU::append_Function (Symbol *sym, const char *outerName) |
| { |
| if (sym->func != NULL) |
| return sym->func; |
| Function *func = sym->createFunction (module); |
| |
| char *fname = Dwarf_string (DW_AT_name); |
| if (fname) |
| { |
| if (outerName && !strchr (fname, '.')) |
| { |
| char *tmpname; |
| int outerlen = (int) strlen (outerName); |
| if (outerlen > 0 && outerName[outerlen - 1] == '_') |
| tmpname = dbe_sprintf ("%.*s.%s_", outerlen - 1, outerName, fname); |
| else |
| tmpname = dbe_sprintf ("%s.%s", outerName, fname); |
| func->set_match_name (tmpname); |
| Dprintf (DUMP_DWARFLIB, "Generated innerfunc name %s\n", tmpname); |
| free(tmpname); |
| } |
| else |
| func->set_match_name (fname); |
| } |
| set_source (func); |
| return func; |
| } |
| |
| // Get language code |
| Sp_lang_code |
| DwrCU::Dwarf_lang () |
| { |
| char *str = Dwarf_string (DW_AT_producer); |
| if (str && strncmp (str, NTXT ("GNU"), 3) == 0) |
| isGNU = true; |
| int64_t lang = Dwarf_data (DW_AT_language); |
| switch (lang) |
| { |
| case DW_LANG_C89: |
| case DW_LANG_C: |
| return Sp_lang_c; // Sp_lang_ansic? |
| case DW_LANG_C99: |
| return Sp_lang_c99; |
| case DW_LANG_C_plus_plus: |
| return isGNU ? Sp_lang_gcc : Sp_lang_cplusplus; |
| case DW_LANG_Fortran90: |
| return Sp_lang_fortran90; |
| case DW_LANG_Fortran77: |
| return Sp_lang_fortran; |
| case DW_LANG_Java: |
| return Sp_lang_java; |
| case DW_LANG_Mips_Assembler: |
| case DW_LANG_SUN_Assembler: |
| return Sp_lang_asm; |
| case DW_LANG_Pascal83: |
| return Sp_lang_pascal; |
| default: |
| case DW_LANG_Ada83: |
| case DW_LANG_Cobol74: |
| case DW_LANG_Cobol85: |
| case DW_LANG_Modula2: |
| case DW_LANG_Ada95: |
| case DW_LANG_Fortran95: |
| case DW_LANG_lo_user: |
| return Sp_lang_unknown; |
| } |
| } |
| |
| Vector <Dwr_rng_entry *> * |
| Dwarf::get_debug_rnglists () |
| { |
| if (debug_rnglists != NULL) |
| return debug_rnglists; |
| debug_rnglists = new Vector <Dwr_rng_entry *> (); |
| |
| DwrSec *debug_rnglistsSec = dwrGetSec (".debug_rnglists"); |
| if (debug_rnglistsSec == NULL) |
| { |
| Dprintf (1, "No section .debug_rnglists\n"); |
| return debug_rnglists; |
| } |
| while (debug_rnglistsSec->offset < debug_rnglistsSec->sizeSec) |
| { |
| uint64_t base_address = 0; |
| uint64_t length = debug_rnglistsSec->ReadLength (); |
| Dwr_rng_entry *rng = new Dwr_rng_entry (); |
| debug_rnglists->append (rng); |
| rng->offset = debug_rnglistsSec->offset; |
| rng->length = length; |
| rng->fmt64 = debug_rnglistsSec->fmt64; |
| rng->version = debug_rnglistsSec->Get_16 (); |
| rng->address_size = debug_rnglistsSec->Get_8 (); |
| rng->segment_selector_size = debug_rnglistsSec->Get_8 (); |
| rng->offset_entry_count = debug_rnglistsSec->Get_32 (); |
| while (debug_rnglistsSec->offset < debug_rnglistsSec->size) |
| { |
| uint64_t off = debug_rnglistsSec->offset; |
| uint64_t low_pc; |
| uint64_t high_pc; |
| int re = debug_rnglistsSec->Get_8 (); |
| switch (re) |
| { |
| case DW_RLE_end_of_list: |
| low_pc = 0; |
| high_pc = 0; |
| break; |
| case DW_RLE_base_address: |
| base_address = debug_rnglistsSec->GetADDR (); |
| low_pc = base_address; |
| high_pc = 0; |
| continue; |
| case DW_RLE_start_length: |
| low_pc = debug_rnglistsSec->GetADDR (); |
| high_pc = low_pc + debug_rnglistsSec->GetULEB128 (); |
| break; |
| case DW_RLE_offset_pair: |
| low_pc = base_address + debug_rnglistsSec->GetULEB128 (); |
| high_pc = base_address + debug_rnglistsSec->GetULEB128 (); |
| break; |
| case DW_RLE_start_end: |
| low_pc = debug_rnglistsSec->GetADDR (); |
| high_pc = debug_rnglistsSec->GetADDR (); |
| break; |
| case DW_RLE_base_addressx: |
| base_address = debug_rnglistsSec->GetULEB128 (); |
| low_pc = base_address; |
| high_pc = 0; |
| continue; |
| |
| // TODO x-variants need .debug_addr support used for split-dwarf |
| case DW_RLE_startx_endx: |
| low_pc = debug_rnglistsSec->GetRef (); |
| high_pc = debug_rnglistsSec->GetRef (); |
| Dprintf (1, "DW_RLE_startx_endx is not implemented\n"); |
| continue; |
| case DW_RLE_startx_length: |
| low_pc = debug_rnglistsSec->GetRef (); |
| high_pc = low_pc + debug_rnglistsSec->GetULEB128 (); |
| Dprintf (1, "DW_RLE_startx_length is not implemented\n"); |
| continue; |
| default: |
| Dprintf (1, "Unknown tag DW_RLE: %d, offset=0x%llx\n", re, |
| (long long) off); |
| debug_rnglistsSec->offset = debug_rnglistsSec->size; |
| continue; |
| } |
| Dprintf (DUMP_DWARFLIB, "0x%08llx %d-%-20s [0x%08llx - 0x%08llx)\n", |
| (long long) off, re, Dwr_rng_entry::rng_entry2str (re), |
| (long long) low_pc, (long long) high_pc); |
| rng->ranges->append (new ExtRange (off, low_pc, high_pc)); |
| } |
| debug_rnglistsSec->size = debug_rnglistsSec->sizeSec; |
| debug_rnglistsSec->offset = length; |
| } |
| debug_rnglists->dump ("Dwarf::get_debug_rnglists"); |
| return debug_rnglists; |
| } |