/* Copyright (C) 2021-2024 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"

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_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"));
  if (debug_infoSec)
    {
      debug_infoSec->reloc = ElfReloc::get_elf_reloc (elf, NTXT (".rela.debug_info"), NULL);
      debug_infoSec->reloc = ElfReloc::get_elf_reloc (elf, NTXT (".rel.debug_info"), debug_infoSec->reloc);
      if (debug_infoSec->reloc)
	debug_infoSec->reloc->dump ();
    }
  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");

  if ((debug_infoSec == NULL) || (debug_abbrevSec == NULL) || (debug_lineSec == NULL))
    {
      status = Stabs::DBGD_ERR_NO_DWARF;
      return;
    }
}

Dwarf::~Dwarf ()
{
  delete debug_infoSec;
  delete debug_abbrevSec;
  delete debug_strSec;
  delete debug_lineSec;
  delete debug_rangesSec;
  Destroy (dwrCUs);
}

DwrSec *
Dwarf::dwrGetSec (const char *sec_name)
{
  int secN = elf->elf_get_sec_num (sec_name);
  if (secN > 0)
    {
      Elf_Data *elfData = elf->elf_getdata (secN);
      if (elfData)
	return new DwrSec ((unsigned char *) elfData->d_buf, elfData->d_size,
			   elf->need_swap_endian,
			   elf->elf_getclass () == ELFCLASS32);
    }
  return NULL;
}

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;
  return Dwarf_string (DW_AT_MIPS_linkage_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 + cu_offset;
      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:
	  if (dwrTag.get_attr (DW_AT_abstract_origin))
	    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);
		}
	      break;
	    }
	  func = append_Function (ctx);
	  if (func)
	    {
	      if (Stabs::is_fortran (ctx->module->lang_code) &&
		  streq (func->get_match_name (), NTXT ("MAIN")))
		ctx->fortranMAIN = func;
	      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);
	    }
	}
    }
  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);
}


// 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 + cu_offset;
      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;
	    if (dwrTag.get_attr (DW_AT_abstract_origin)
				 || dwrTag.get_attr (DW_AT_declaration))
	      ctx->func = NULL;
	    else
	      ctx->func = append_Function (ctx);
	    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 (Dwarf_cnt *ctx)
{
  char *outerName = ctx->name, *name, tmpname[2048];
  Function *func;
  char *fname = Dwarf_string (DW_AT_name);
  if (fname && outerName && !strchr (fname, '.'))
    {
      size_t outerlen = strlen (outerName);
      if (outerlen > 0 && outerName[outerlen - 1] == '_')
	{
	  outerlen--;
	  snprintf (tmpname, sizeof (tmpname), NTXT ("%s"), outerName);
	  snprintf (tmpname + outerlen, sizeof (tmpname) - outerlen, NTXT (".%s_"), fname);
	}
      else
	snprintf (tmpname, sizeof (tmpname), NTXT ("%s.%s"), outerName, fname);
      name = tmpname;
      Dprintf (DUMP_DWARFLIB, NTXT ("Generated innerfunc name %s\n"), name);
    }
  else
    name = fname;

  char *link_name = get_linkage_name ();
  if (link_name == NULL)
    link_name = name;

  uint64_t pc = get_low_pc ();
  func = dwarf->stabs->append_Function (module, link_name, pc);
  if (func != NULL)
    {
      int lineno = (int) Dwarf_data (DW_AT_decl_line);
      func->set_match_name (name);
      if (lineno > 0)
	{
	  func->setLineFirst (lineno);
	  int fileno = ((int) Dwarf_data (DW_AT_decl_file));
	  SourceFile *sf = ((fileno >= 0) && (fileno < VecSize (srcFiles))) ? srcFiles->get (fileno)
		  : module->getMainSrc ();
	  func->setDefSrc (sf);
	  func->pushSrcFile (func->def_source, 0);
	  func->popSrcFile ();
	}
    }
  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;
    }
}
