/* Copyright (C) 2021-2026 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 <sys/param.h>

#include "util.h"
#include "Elf.h"
#include "Dwarf.h"
#include "stab.h"
#include "DbeSession.h"
#include "CompCom.h"
#include "Stabs.h"
#include "LoadObject.h"
#include "Module.h"
#include "Function.h"
#include "info.h"
#include "StringBuilder.h"
#include "DbeFile.h"
#include "StringMap.h"
#include "Symbol.h"

///////////////////////////////////////////////////////////////////////////////
// class StabReader
class StabReader
{
public:
  StabReader (Elf *_elf, Platform_t platform, int StabSec, int StabStrSec);
  ~StabReader () { };
  char *get_type_name (int t);
  char *get_stab (struct stab *np, bool comdat);
  void parse_N_OPT (Module *mod, char *str);
  int stabCnt;
  int stabNum;

private:
  Elf *elf;
  char *StabData;
  char *StabStrtab;
  char *StabStrtabEnd;
  int StrTabSize;
  int StabEntSize;
};

enum
{
  SYM_PLT       = 1 << 0,
  SYM_UNDEF     = 1 << 1
};

enum Section_type
{
  COMM1_SEC = 0x10000000,
  COMM_SEC  = 0x20000000,
  INFO_SEC  = 0x30000000,
  LOOP_SEC  = 0x40000000
};

struct cpf_stabs_t
{
  uint32_t type;    // Archive::AnalyzerInfoType
  uint32_t offset;  // offset in .__analyzer_info
  Module *module;   // table for appropriate Module
};

static char *get_info_com (int type, int32_t copy_inout);
static char *get_lp_com (unsigned hints, int parallel, char *dep);
static int ComCmp (const void *a, const void *b);
static ino64_t _src_inode = 0;
static char *_src_name;

// Comparing name
static int
SymNameCmp (const void *a, const void *b)
{
  Symbol *item1 = *((Symbol **) a);
  Symbol *item2 = *((Symbol **) b);
  return (item1->name == NULL) ? -1 :
	  (item2->name == NULL) ? 1 : strcmp (item1->name, item2->name);
}

// Comparing value: for sorting
static int
SymValueCmp (const void *a, const void *b)
{
  Symbol *item1 = *((Symbol **) a);
  Symbol *item2 = *((Symbol **) b);
  return (item1->value > item2->value) ? 1 :
	  (item1->value == item2->value) ? SymNameCmp (a, b) : -1;
}

// Comparing value: for searching (source name is always NULL)
static int
SymFindCmp (const void *a, const void *b)
{
  Symbol *item1 = *((Symbol **) a);
  Symbol *item2 = *((Symbol **) b);
  if (item1->value < item2->value)
    return -1;
  if (item1->value < item2->value + item2->size
      || item1->value == item2->value) // item2->size == 0
    return 0;
  return 1;
}

// Comparing value for sorting. It is used only for searching aliases.
static int
SymImgOffsetCmp (const void *a, const void *b)
{
  Symbol *item1 = *((Symbol **) a);
  Symbol *item2 = *((Symbol **) b);
  return (item1->img_offset > item2->img_offset) ? 1 :
	  (item1->img_offset == item2->img_offset) ? SymNameCmp (a, b) : -1;
}

/* Remove all duplicate symbols which can be in SymLst.  The
   duplication is due to processing of both static and dynamic
   symbols.  This function is called before computing symbol
   aliases.  */

void
Stabs::removeDupSyms ()
{
  long ind, i, last;
  Symbol *symA, *symB;
  SymLst->sort (SymImgOffsetCmp);

  last = 0;
  ind = SymLst->size ();
  for (i = 0; i < ind; i++)
    {
      symA = SymLst->fetch (i);
      if (symA->img_offset == 0) // Ignore this bad symbol
	continue;

      SymLst->put (last++, symA);
      for (long k = i + 1; k < ind; k++, i++)
	{
	  symB = SymLst->fetch (k);
	  if (symA->img_offset != symB->img_offset)
	    break;
	  if (strcmp (symA->name, symB->name) != 0)
	    break;
	}
    }
  SymLst->truncate (last);
}

Stabs::Stabs (Elf *elf, char *_lo_name)
{
  elfDis = elf;
  elfDbg = elf->gnu_debug_file ? elf->gnu_debug_file : elf;
  path = dbe_strdup (elf->get_location ());
  lo_name = dbe_strdup (_lo_name);
  SymLstByName = NULL;
  pltSym = NULL;
  SymLst = new Vector<Symbol*>;
  LocalLst = new Vector<Symbol*>;
  LocalFile = new Vector<char*>;
  LocalFileIdx = new Vector<int>;
  last_PC_to_sym = NULL;
  dwarf = NULL;
  stabsModules = NULL;
  textsz = 0;
  wsize = Wnone;
  st_check_symtab = false;
  status = DBGD_ERR_NONE;

  switch (elfDis->elf_getclass ())
    {
    case ELFCLASS32:
      wsize = W32;
      break;
    case ELFCLASS64:
      wsize = W64;
      break;
    }
  switch (elfDis->elf_getehdr ()->e_machine)
    {
    case EM_SPARC:
      platform = Sparc;
      break;
    case EM_SPARC32PLUS:
      platform = Sparcv8plus;
      break;
    case EM_SPARCV9:
      platform = Sparcv9;
      break;
    case EM_386:
      //    case EM_486:
      platform = Intel;
      break;
    case EM_X86_64:
      platform = Amd64;
      break;
    case EM_AARCH64:
      platform = Aarch64;
      break;
    default:
      platform = Unknown;
      break;
    }
  isRelocatable = elfDis->elf_getehdr ()->e_type == ET_REL;
  for (unsigned int pnum = 0; pnum < elfDis->elf_getehdr ()->e_phnum; pnum++)
    {
      Elf_Internal_Phdr *phdr = elfDis->get_phdr (pnum);
      if (phdr->p_type == PT_LOAD && phdr->p_flags == (PF_R | PF_X))
	{
	  if (textsz == 0)
	    textsz = phdr->p_memsz;
	  else
	    {
	      textsz = 0;
	      break;
	    }
	}
    }
}

Stabs::~Stabs ()
{
  delete SymLstByName;
  Destroy (SymLst);
  Destroy (LocalFile);
  delete dwarf;
  delete LocalLst;
  delete LocalFileIdx;
  delete stabsModules;
  free (path);
  free (lo_name);
}

Elf *
Stabs::openElf (bool dbg_info)
{
  if (dbg_info)
    return elfDbg;
  return elfDis;
}

bool
Stabs::read_symbols (Vector<Function*> *functions)
{
  if (openElf (true) == NULL)
    return false;
  check_Symtab ();
  if (functions)
    {
      Function *fp;
      int index;
      Vec_loop (Function*, functions, index, fp)
      {
	fp->img_fname = path;
      }
    }
  return true;
}

Symbol *
Stabs::map_PC_to_sym (uint64_t pc)
{
  if (pc == 0)
    return NULL;
  if (last_PC_to_sym && last_PC_to_sym->value <= pc
      && last_PC_to_sym->value + last_PC_to_sym->size > pc)
    return last_PC_to_sym;
  Symbol *sym = new Symbol;
  sym->value = pc;
  long index = SymLst->bisearch (0, -1, &sym, SymFindCmp);
  delete sym;
  if (index >= 0)
    {
      last_PC_to_sym = SymLst->fetch (index)->cardinal ();
      return last_PC_to_sym;
    }
  return NULL;
}

Function *
Stabs::map_PC_to_func (uint64_t pc, uint64_t &low_pc, Vector<Function*> *functions)
{
  int index;
  Function *func;
  Symbol *sptr = map_PC_to_sym (pc);
  if (sptr == NULL)
    return NULL;
  if (sptr->func)
    {
      low_pc = sptr->value;
      return sptr->func;
    }
  if (functions)
    {
      Vec_loop (Function*, functions, index, func)
      {
	if (func->img_offset == sptr->img_offset)
	  {
	    sptr->func = func->cardinal ();
	    low_pc = sptr->value;
	    return sptr->func;
	  }
      }
    }
  return NULL;
}

Stabs::Stab_status
Stabs::read_stabs (ino64_t srcInode, Module *module, Vector<ComC*> *comComs,
		   bool readDwarf)
{
  if (module)
    module->setIncludeFile (NULL);

  if (openElf (true) == NULL)
    return status;
  check_Symtab ();

  // read compiler commentary from .compcom1, .compcom,
  // .info, .loops, and .loopview sections
  if (comComs)
    {
      _src_inode = srcInode;
      _src_name = module && module->file_name ? get_basename (module->file_name) : NULL;
      if (!check_Comm (comComs))
	// .loops, and .loopview are now in .compcom
	check_Loop (comComs);

      // should not read it after .info goes into .compcom
      check_Info (comComs);
      comComs->sort (ComCmp);
    }

  // get stabs info
  Stab_status statusStabs = DBGD_ERR_NO_STABS;
#define SRC_LINE_STABS(sec, secStr, comdat) \
    if ((elfDbg->sec)  && (elfDbg->secStr) && \
	srcline_Stabs(module, elfDbg->sec, elfDbg->secStr, comdat) == DBGD_ERR_NONE) \
	statusStabs = DBGD_ERR_NONE

  SRC_LINE_STABS (stabExcl, stabExclStr, false);
  SRC_LINE_STABS (stab, stabStr, false);
  SRC_LINE_STABS (stabIndex, stabIndexStr, true);

  // read Dwarf, if any sections found
  if (elfDbg->dwarf && readDwarf)
    {
      openDwarf ()->srcline_Dwarf (module);
      if (dwarf && dwarf->status == DBGD_ERR_NONE)
	return DBGD_ERR_NONE;
    }
  return statusStabs;
}

static int
ComCmp (const void *a, const void *b)
{
  ComC *item1 = *((ComC **) a);
  ComC *item2 = *((ComC **) b);
  return (item1->line > item2->line) ? 1 :
	  (item1->line < item2->line) ? -1 :
	  (item1->sec > item2->sec) ? 1 :
	  (item1->sec < item2->sec) ? -1 : 0;
}

static int
check_src_name (char *srcName)
{
  if (_src_name && srcName && streq (_src_name, get_basename (srcName)))
    return 1;
  if (_src_inode == (ino64_t) - 1)
    return 0;
  DbeFile *dbeFile = dbeSession->getDbeFile (srcName, DbeFile::F_SOURCE);
  char *path = dbeFile->get_location ();
  return (path == NULL || dbeFile->sbuf.st_ino != _src_inode) ? 0 : 1;
}

bool
Stabs::check_Comm (Vector<ComC*> *comComs)
{
  int sz = comComs->size ();
  Elf *elf = openElf (true);
  if (elf == NULL)
    return false;

  for (unsigned int sec = 1; sec < elf->elf_getehdr ()->e_shnum; sec++)
    {
      char *name = elf->get_sec_name (sec);
      if (name == NULL)
	continue;
      Section_type sec_type;
      if (streq (name, NTXT (".compcom")))
	sec_type = COMM_SEC;
      else if (streq (name, NTXT (".compcom1")))
	sec_type = COMM1_SEC;
      else
	continue;

      // find header, set messages id & visibility if succeed
      CompComment *cc = new CompComment (elf, sec);
      int cnt = cc->compcom_open ((CheckSrcName) check_src_name);
      // process messages
      for (int index = 0; index < cnt; index++)
	{
	  int visible;
	  compmsg msg;
	  char *str = cc->compcom_format (index, &msg, visible);
	  if (str)
	    {
	      ComC *citem = new ComC;
	      citem->sec = sec_type + index;
	      citem->type = msg.msg_type;
	      citem->visible = visible;
	      citem->line = (msg.lineno < 1) ? 1 : msg.lineno;
	      citem->com_str = str;
	      comComs->append (citem);
	    }
	}
      delete cc;
    }
  return (sz != comComs->size ());
}

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);
}

void
Stabs::check_AnalyzerInfo ()
{
  Elf *elf = openElf (true);
  if ((elf == NULL) || (elf->analyzerInfo == 0))
    {
      Dprintf (DEBUG_STABS, NTXT ("Stabs::check_AnalyzerInfo: Null AnalyzerInfo section\n"));
      return; // inappropriate, but ignored anyway
    }
  Elf_Data *data = elf->elf_getdata (elf->analyzerInfo);
  int InfoSize = (int) data->d_size;
  char *InfoData = (char *) data->d_buf;
  int InfoAlign = (int) data->d_align;
  AnalyzerInfoHdr h;
  unsigned infoHdr_sz = sizeof (AnalyzerInfoHdr);
  int table, entry;
  int read = 0;
  Module *mitem;
  int index = 0;
  if (InfoSize <= 0)
    return;
  uint64_t baseAddr = elf->get_baseAddr ();
  Dprintf (DEBUG_STABS, NTXT ("Stabs::check_AnalyzerInfo size=%d @0x%lx (align=%d) base=0x%llx\n"),
	   InfoSize, (ul_t) InfoData, InfoAlign, (long long) baseAddr);
  Dprintf (DEBUG_STABS, NTXT ("analyzerInfoMap has %lld entries\n"), (long long) analyzerInfoMap.size ());
  if (analyzerInfoMap.size () == 0)
    {
      Dprintf (DEBUG_STABS, NTXT ("No analyzerInfoMap available!\n"));
      return;
    }

  // verify integrity of analyzerInfoMap before reading analyzerInfo
  unsigned count = 0;
  Module *lastmod = NULL;
  for (index = 0; index < analyzerInfoMap.size (); index++)
    {
      cpf_stabs_t map = analyzerInfoMap.fetch (index);
      if (map.type > 3)
	{
	  Dprintf (DEBUG_STABS, NTXT ("analyzerInfo contains table of unknown type %d for %s\n"),
		   map.type, map.module->get_name ());
	  return;
	}
      if (map.module != lastmod)
	{
	  if (lastmod != NULL)
	    Dprintf (DEBUG_STABS, "analyzerInfo contains %d 0x0 offset tables for %s\n",
		     count, lastmod->get_name ());
	  count = 0;
	}
      count += (map.offset == 0x0); // only check for 0x0 tables for now
      if (count > 4)
	{
	  Dprintf (DEBUG_STABS, NTXT ("analyzerInfo contains too many 0x0 offset tables for %s\n"),
		   map.module->get_name ());
	  return;
	}
      lastmod = map.module;
    }

  index = 0;
  while ((index < analyzerInfoMap.size ()) && (read < InfoSize))
    {
      for (table = 0; table < 3; table++)
	{ // memory operations (ld, st, prefetch)
	  // read the table header
	  memcpy ((void *) &h, (const void *) InfoData, infoHdr_sz);
	  InfoData += infoHdr_sz;
	  read += infoHdr_sz;

	  // use map for appropriate module
	  cpf_stabs_t map = analyzerInfoMap.fetch (index);
	  index++;
	  mitem = map.module;
	  Dprintf (DEBUG_STABS, "Table %d offset=0x%04x "
		   "text_labelref=0x%08llx entries=%d version=%d\n"
		   "itype %d offset=0x%04x module=%s\n", table, read,
		   (long long) (h.text_labelref - baseAddr), h.entries,
		   h.version, map.type, map.offset, map.module->get_name ());
	  // read the table entries
	  for (entry = 0; entry < h.entries; entry++)
	    {
	      memop_info_t *m = new memop_info_t;
	      unsigned memop_info_sz = sizeof (memop_info_t);
	      memcpy ((void *) m, (const void *) InfoData, memop_info_sz);
	      InfoData += memop_info_sz;
	      read += memop_info_sz;
	      m->offset += (uint32_t) (h.text_labelref - baseAddr);
	      Dprintf (DEBUG_STABS, NTXT ("%4d(%d): offset=0x%04x id=0x%08x sig=0x%08x dtid=0x%08x\n"),
		       entry, table, m->offset, m->id, m->signature, m->datatype_id);
	      switch (table)
		{
		case CPF_INSTR_TYPE_LD:
		  mitem->ldMemops.append (m);
		  break;
		case CPF_INSTR_TYPE_ST:
		  mitem->stMemops.append (m);
		  break;
		case CPF_INSTR_TYPE_PREFETCH:
		  mitem->pfMemops.append (m);
		  break;
		}
	    }
	  // following re-alignment should be redundant
	  //InfoData+=(read%InfoAlign); read+=(read%InfoAlign); // re-align
	}
      for (table = 3; table < 4; table++)
	{ // branch targets
	  memcpy ((void *) &h, (const void *) InfoData, infoHdr_sz);
	  InfoData += infoHdr_sz;
	  read += infoHdr_sz;

	  // use map for appropriate module
	  cpf_stabs_t map = analyzerInfoMap.fetch (index);
	  index++;
	  mitem = map.module;
	  Dprintf (DEBUG_STABS, "Table %d offset=0x%04x "
		   "text_labelref=0x%08llx entries=%d version=%d\n"
		   "itype %d offset=0x%04x module=%s\n", table, read,
		   (long long) (h.text_labelref - baseAddr), h.entries,
		   h.version, map.type, map.offset, map.module->get_name ());
	  for (entry = 0; entry < h.entries; entry++)
	    {
	      target_info_t *t = new target_info_t;
	      unsigned target_info_sz = sizeof (target_info_t);
	      memcpy ((void *) t, (const void *) InfoData, target_info_sz);
	      InfoData += target_info_sz;
	      read += target_info_sz;
	      t->offset += (uint32_t) (h.text_labelref - baseAddr);
	      Dprintf (DEBUG_STABS, NTXT ("%4d(%d): offset=0x%04x\n"), entry,
		       table, t->offset);
	      // the list of branch targets needs to be in offset sorted order
	      // and doing it here before archiving avoids the need to do it
	      // each time the archive is read.
	      mitem->bTargets.incorporate (t, targetOffsetCmp);
	    }
	  Dprintf (DEBUG_STABS, NTXT ("bTargets for %s has %lld items (last=0x%04x)\n"),
		   mitem->get_name (), (long long) mitem->bTargets.size (),
		   (mitem->bTargets.fetch (mitem->bTargets.size () - 1))->offset);
	  Dprintf (DEBUG_STABS, "read=%d at end of bTargets (InfoData=0x%lx)\n",
		   read, (ul_t) InfoData);
	  InfoData += (read % InfoAlign);
	  read += (read % InfoAlign); // re-align
	  Dprintf (DEBUG_STABS, "read=%d at end of bTargets (InfoData=0x%lx)\n",
		   read, (ul_t) InfoData);
	}
      Dprintf (DEBUG_STABS, "Stabs::check_AnalyzerInfo bytes read=%lld (index=%lld/%lld)\n",
	       (long long) read, (long long) index,
	       (long long) analyzerInfoMap.size ());
    }
}

void
Stabs::check_Info (Vector<ComC*> *comComs)
{
  Elf *elf = openElf (true);
  if (elf == NULL || elf->info == 0)
    return;
  Elf_Data *data = elf->elf_getdata (elf->info);
  uint64_t InfoSize = data->d_size;
  char *InfoData = (char *) data->d_buf;
  bool get_src = false;
  for (int h_num = 0; InfoSize; h_num++)
    {
      if (InfoSize < sizeof (struct info_header))
	return;
      struct info_header *h = (struct info_header*) InfoData;
      if (h->endian != '\0' || h->magic[0] != 'S' || h->magic[1] != 'U'
	  || h->magic[2] != 'N')
	return;
      if (h->len < InfoSize || h->len < sizeof (struct info_header) || (h->len & 3))
	return;

      char *fname = InfoData + sizeof (struct info_header);
      InfoData += h->len;
      InfoSize -= h->len;
      get_src = check_src_name (fname);
      for (uint32_t e_num = 0; e_num < h->cnt; ++e_num)
	{
	  if (InfoSize < sizeof (struct entry_header))
	    return;
	  struct entry_header *e = (struct entry_header*) InfoData;
	  if (InfoSize < e->len)
	    return;
	  int32_t copy_inout = 0;
	  if (e->len > sizeof (struct entry_header))
	    if (e->type == F95_COPYINOUT)
	      copy_inout = *(int32_t*) (InfoData + sizeof (struct entry_header));
	  InfoData += e->len;
	  InfoSize -= e->len;
	  if (get_src)
	    {
	      ComC *citem = new ComC;
	      citem->sec = INFO_SEC + h_num;
	      citem->type = e->msgnum & 0xFFFFFF;
	      citem->visible = CCMV_ALL;
	      citem->line = e->line;
	      citem->com_str = get_info_com (citem->type, copy_inout);
	      comComs->append (citem);
	    }
	}
      if (get_src)
	break;
    }
}

static char *
get_info_com (int type, int32_t copy_inout)
{
  switch (type)
    {
    case 1:
      return dbe_sprintf (GTXT ("In the call below, parameter number %d caused a copy-in -- loop(s) inserted"),
			  copy_inout);
    case 2:
      return dbe_sprintf (GTXT ("In the call below, parameter number %d caused a copy-out -- loop(s) inserted"),
			  copy_inout);
    case 3:
      return dbe_sprintf (GTXT ("In the call below, parameter number %d caused a copy-in and a copy-out -- loops inserted"),
			  copy_inout);
    case 4:
      return dbe_strdup (GTXT ("Alignment of variables in common block may cause performance degradation"));
    case 5:
      return dbe_strdup (GTXT ("DO statement bounds lead to no executions of the loop"));
    default:
      return dbe_strdup (NTXT (""));
    }
}

void
Stabs::check_Loop (Vector<ComC*> *comComs)
{
  Elf *elf = openElf (true);
  if (elf == NULL)
    return;

  StringBuilder sb;
  for (unsigned int sec = 1; sec < elf->elf_getehdr ()->e_shnum; sec++)
    {
      char *name = elf->get_sec_name (sec);
      if (name == NULL)
	continue;
      if (!streq (name, NTXT (".loops")) && !streq (name, NTXT (".loopview")))
	continue;

      Elf_Data *data = elf->elf_getdata (sec);
      size_t LoopSize = (size_t) data->d_size, len;
      char *LoopData = (char *) data->d_buf;
      int remainder, i;
      char src[2 * MAXPATHLEN], buf1[MAXPATHLEN], buf2[MAXPATHLEN];
      char **dep_str = NULL;
      bool get_src = false;
      while ((LoopSize > 0) && !get_src &&
	     (strncmp (LoopData, NTXT ("Source:"), 7) == 0))
	{
	  // The first three items in a .loops subsection are three strings.
	  //	Source: ...
	  //	Version: ...
	  //	Number of loops: ...
	  sscanf (LoopData, NTXT ("%*s%s"), src);
	  len = strlen (LoopData) + 1;
	  LoopData += len;
	  LoopSize -= len;
	  sscanf (LoopData, NTXT ("%*s%*s%s"), buf1);
	  //	double version   = atof(buf1);
	  len = strlen (LoopData) + 1;
	  LoopData += len;
	  LoopSize -= len;
	  get_src = check_src_name (src);
	  sscanf (LoopData, NTXT ("%*s%*s%*s%s%s"), buf1, buf2);
	  int n_loop = atoi (buf1);
	  int n_depend = atoi (buf2);
	  len = strlen (LoopData) + 1;
	  LoopData += len;
	  LoopSize -= len;
	  if (get_src && (n_loop > 0))
	    {
	      dep_str = new char*[n_loop];
	      for (i = 0; i < n_loop; i++)
		dep_str[i] = NULL;
	    }

	  // printf("Source: %s\nVersion: %f\nLoop#: %d\nDepend#: %d\n",
	  //	src, version, n_loop, n_depend);

	  // Read in the strings that contain the list of variables that cause
	  // data dependencies inside of loops. Not every loop has such a list
	  // of variables.
	  //
	  //	Example: if loop #54 has data dependencies caused by the
	  //	variables named i, j and foo, then the string that represents
	  //	this in the .loops section looks like this:
	  //
	  //	.asciz "54:i.j.foo"
	  //
	  //	The variable names are delimited with .
	  //
	  //	For now, store these strings in an array, and add them into
	  //	the loop structure when we read in the numeric loop info
	  //	(that's what we read in next.)
	  //
	  // printf("\tDependenncies:\n");
	  for (i = 0; i < n_depend; i++)
	    {
	      len = strlen (LoopData) + 1;
	      LoopData += len;
	      LoopSize -= len;
	      if (dep_str != NULL)
		{
		  char *dep_buf1 = dbe_strdup (LoopData);
		  char *ptr = strtok (dep_buf1, NTXT (":"));
		  if (ptr != NULL)
		    {
		      int index = atoi (ptr);
		      bool dep_first = true;
		      sb.setLength (0);
		      while ((ptr = strtok (NULL, NTXT (", "))) != NULL)
			{
			  if (dep_first)
			    dep_first = false;
			  else
			    sb.append (NTXT (", "));
			  sb.append (ptr);
			}
		      if (sb.length () > 0 && index < n_loop)
			dep_str[index] = sb.toString ();
		    }
		  free (dep_buf1);
		}
	    }

	  // Adjust Data pointer so that it is word aligned.
	  remainder = (int) (((unsigned long) LoopData) % 4);
	  if (remainder != 0)
	    {
	      len = 4 - remainder;
	      LoopData += len;
	      LoopSize -= len;
	    }

	  // Read in the loop info, one loop at a time.
	  for (i = 0; i < n_loop; i++)
	    {
	      int loopid = *((int *) LoopData);
	      LoopData += 4;
	      int line_no = *((int *) LoopData);
	      if (line_no < 1) // compiler has trouble on this
		line_no = 1;
	      LoopData += 4;
	      //	    int nest = *((int *) LoopData);
	      LoopData += 4;
	      int parallel = *((int *) LoopData);
	      LoopData += 4;
	      unsigned hints = *((unsigned *) LoopData);
	      LoopData += 4;
	      //	    int count = *((int *) LoopData);
	      LoopData += 4;
	      LoopSize -= 24;
	      if (!get_src || (loopid >= n_loop))
		continue;
	      ComC *citem = new ComC;
	      citem->sec = LOOP_SEC + i;
	      citem->type = hints;
	      citem->visible = CCMV_ALL;
	      citem->line = line_no;
	      citem->com_str = get_lp_com (hints, parallel, dep_str[loopid]);
	      comComs->append (citem);
	    }
	  if (dep_str)
	    {
	      for (i = 0; i < n_loop; i++)
		free (dep_str[i]);
	      delete[] dep_str;
	      dep_str = NULL;
	    }
	}
    }
}

static char *
get_lp_com (unsigned hints, int parallel, char *dep)
{
  StringBuilder sb;
  if (parallel == -1)
    sb.append (GTXT ("Loop below is serial, but parallelizable: "));
  else if (parallel == 0)
    sb.append (GTXT ("Loop below is not parallelized: "));
  else
    sb.append (GTXT ("Loop below is parallelized: "));
  switch (hints)
    {
    case 0:
      // No loop mesg will print
      // strcat(com, GTXT("no hint available"));
      break;
    case 1:
      sb.append (GTXT ("loop contains procedure call"));
      break;
    case 2:
      sb.append (GTXT ("compiler generated two versions of this loop"));
      break;
    case 3:
      {
	StringBuilder sb_tmp;
	sb_tmp.sprintf (GTXT ("the variable(s) \"%s\" cause a data dependency in this loop"),
			dep ? dep : GTXT ("<Unknown>"));
	sb.append (&sb_tmp);
      }
      break;
    case 4:
      sb.append (GTXT ("loop was significantly transformed during optimization"));
      break;
    case 5:
      sb.append (GTXT ("loop may or may not hold enough work to be profitably parallelized"));
      break;
    case 6:
      sb.append (GTXT ("loop was marked by user-inserted pragma"));
      break;
    case 7:
      sb.append (GTXT ("loop contains multiple exits"));
      break;
    case 8:
      sb.append (GTXT ("loop contains I/O, or other function calls, that are not MT safe"));
      break;
    case 9:
      sb.append (GTXT ("loop contains backward flow of control"));
      break;
    case 10:
      sb.append (GTXT ("loop may have been distributed"));
      break;
    case 11:
      sb.append (GTXT ("two loops or more may have been fused"));
      break;
    case 12:
      sb.append (GTXT ("two or more loops may have been interchanged"));
      break;
    default:
      break;
    }
  return sb.toString ();
}

StabReader::StabReader (Elf *_elf, Platform_t platform, int StabSec, int StabStrSec)
{
  stabCnt = -1;
  stabNum = 0;
  if (_elf == NULL)
    return;
  elf = _elf;

  // Get ELF data
  Elf_Data *data = elf->elf_getdata (StabSec);
  if (data == NULL)
    return;
  uint64_t stabSize = data->d_size;
  StabData = (char *) data->d_buf;
  Elf_Internal_Shdr *shdr = elf->get_shdr (StabSec);
  if (shdr == NULL)
    return;

  // GCC bug: sh_entsize is 20 for 64 apps on Linux
  StabEntSize = (platform == Amd64 || platform == Sparcv9) ? 12 : (unsigned) shdr->sh_entsize;
  if (stabSize == 0 || StabEntSize == 0)
    return;
  data = elf->elf_getdata (StabStrSec);
  if (data == NULL)
    return;
  shdr = elf->get_shdr (StabStrSec);
  if (shdr == NULL)
    return;
  StabStrtab = (char *) data->d_buf;
  StabStrtabEnd = StabStrtab + shdr->sh_size;
  StrTabSize = 0;
  stabCnt = (int) (stabSize / StabEntSize);
}

char *
StabReader::get_stab (struct stab *np, bool comdat)
{
  struct stab *stbp = (struct stab *) (StabData + stabNum * StabEntSize);
  stabNum++;
  *np = *stbp;
  np->n_desc = elf->decode (stbp->n_desc);
  np->n_strx = elf->decode (stbp->n_strx);
  np->n_value = elf->decode (stbp->n_value);
  switch (np->n_type)
    {
    case N_UNDF:
    case N_ILDPAD:
      // Start of new stab section (or padding)
      StabStrtab += StrTabSize;
      StrTabSize = np->n_value;
    }

  char *str = NULL;
  if (np->n_strx)
    {
      if (comdat && np->n_type == N_FUN && np->n_other == 1)
	{
	  if (np->n_strx == 1)
	    StrTabSize++;
	  str = StabStrtab + StrTabSize;
	  // Each COMDAT string must be sized to find the next string:
	  StrTabSize += strlen (str) + 1;
	}
      else
	str = StabStrtab + np->n_strx;
      if (str >= StabStrtabEnd)
	str = NULL;
    }
  if (DEBUG_STABS)
    {
      char buf[128];
      char *s = get_type_name (np->n_type);
      if (s == NULL)
	{
	  snprintf (buf, sizeof (buf), NTXT ("n_type=%d"), np->n_type);
	  s = buf;
	}
      if (str)
	{
	  Dprintf (DEBUG_STABS, NTXT ("%4d:  .stabs \"%s\",%s,0x%x,0x%x,0x%x\n"),
		   stabNum - 1, str, s, (int) np->n_other, (int) np->n_desc,
		   (int) np->n_value);
	}
      else
	Dprintf (DEBUG_STABS, NTXT ("%4d:  .stabn %s,0x%x,0x%x,0x%x\n"),
		 stabNum - 1, s, (int) np->n_other, (int) np->n_desc,
		 (int) np->n_value);
    }
  return str;
}

void
StabReader::parse_N_OPT (Module *mod, char *str)
{
  if (mod == NULL || str == NULL)
      return;
  for (char *s = str; 1; s++)
    {
      switch (*s)
	{
	case 'd':
	  if (s[1] == 'i' && s[2] == ';')
	    {
	      delete mod->dot_o_file;
	      mod->dot_o_file = NULL;
	    }
	  break;
	case 's':
	  if ((s[1] == 'i' || s[1] == 'n') && s[2] == ';')
	    {
	      delete mod->dot_o_file;
	      mod->dot_o_file = NULL;
	    }
	  break;
	}
      s = strchr (s, ';');
      if (s == NULL)
	break;
    }
}

Stabs::Stab_status
Stabs::srcline_Stabs (Module *module, unsigned int StabSec,
		      unsigned int StabStrSec, bool comdat)
{
  StabReader *stabReader = new StabReader (openElf (true), platform, StabSec, StabStrSec);
  int tot = stabReader->stabCnt;
  if (tot < 0)
    {
      delete stabReader;
      return DBGD_ERR_NO_STABS;
    }
  int n, lineno;
  char *sbase, *n_so = NTXT (""), curr_src[2 * MAXPATHLEN];
  Function *newFunc;
  Sp_lang_code _lang_code = module->lang_code;
  Vector<Function*> *functions = module->functions;
  bool no_stabs = true;
  *curr_src = '\0';
  Function *func = NULL;
  int phase = 0;
  int stabs_level = 0;
  int xline = 0;

  // Find module
  for (n = 0; n < tot; n++)
    {
      struct stab stb;
      char *str = stabReader->get_stab (&stb, comdat);
      if (stb.n_type == N_UNDF)
	phase = 0;
      else if (stb.n_type == N_SO)
	{
	  if (str == NULL || *str == '\0')
	    continue;
	  if (phase == 0)
	    {
	      phase = 1;
	      n_so = str;
	      continue;
	    }
	  phase = 0;
	  sbase = str;
	  if (*str == '/')
	    {
	      if (streq (sbase, module->file_name))
		break;
	    }
	  else
	    {
	      size_t last = strlen (n_so);
	      if (n_so[last - 1] == '/')
		last--;
	      if (strncmp (n_so, module->file_name, last) == 0 &&
		  module->file_name[last] == '/' &&
		  streq (sbase, module->file_name + last + 1))
		break;
	    }
	}
    }
  if (n >= tot)
    {
      delete stabReader;
      return DBGD_ERR_NO_STABS;
    }

  Include *includes = new Include;
  includes->new_src_file (module->getMainSrc (), 0, NULL);
  module->hasStabs = true;
  *curr_src = '\0';
  phase = 0;
  for (n++; n < tot; n++)
    {
      struct stab stb;
      char *str = stabReader->get_stab (&stb, comdat);
      int n_desc = (int) ((unsigned short) stb.n_desc);
      switch (stb.n_type)
	{
	case N_UNDF:
	case N_SO:
	case N_ENDM:
	  n = tot;
	  break;
	case N_ALIAS:
	  if (str == NULL)
	    break;
	  if (is_fortran (_lang_code))
	    {
	      char *p = strchr (str, ':');
	      if (p && streq (p + 1, NTXT ("FMAIN")))
		{
		  Function *afunc = find_func (NTXT ("MAIN"), functions, true);
		  if (afunc)
		    afunc->set_match_name (dbe_strndup (str, p - str));
		  break;
		}
	    }
	case N_FUN:
	case N_OUTL:
	  if (str == NULL)
	    break;
	  if (*str == '@')
	    {
	      str++;
	      if (*str == '>' || *str == '<')
		str++;
	    }
	  if (stabs_level != 0)
	    break;

	  // find address of the enclosed function
	  newFunc = find_func (str, functions, is_fortran (_lang_code));
	  if (newFunc == NULL)
	    break;
	  if (func)
	    while (func->popSrcFile ())
	      ;
	  func = newFunc;

	  // First line info to cover function from the beginning
	  lineno = xline + n_desc;
	  if (lineno > 0)
	    {
	      // Set the chain of includes for the new function
	      includes->push_src_files (func);
	      func->add_PC_info (0, lineno);
	      no_stabs = false;
	    }
	  break;
	case N_ENTRY:
	  break;
	case N_CMDLINE:
	  if (str && !module->comp_flags)
	    {
	      char *comp_flags = strchr (str, ';');
	      if (comp_flags)
		{
		  module->comp_flags = dbe_strdup (comp_flags + 1);
		  module->comp_dir = dbe_strndup (str, comp_flags - str);
		}
	    }
	  break;
	case N_LBRAC:
	  stabs_level++;
	  break;
	case N_RBRAC:
	  stabs_level--;
	  break;
	case N_XLINE:
	  xline = n_desc << 16;
	  break;
	case N_SLINE:
	  if (func == NULL)
	    break;
	  no_stabs = false;
	  lineno = xline + n_desc;
	  if (func->line_first <= 0)
	    {
	      // Set the chain of includes for the new function
	      includes->push_src_files (func);
	      func->add_PC_info (0, lineno);
	      break;
	    }
	  if (func->curr_srcfile == NULL)
	    includes->push_src_files (func);
	  if (func->line_first != lineno ||
	      !streq (curr_src, func->getDefSrc ()->get_name ()))
	    func->add_PC_info (stb.n_value, lineno);
	  break;
	case N_OPT:
	  if ((str != NULL) && streq (str, NTXT ("gcc2_compiled.")))
	    _lang_code = Sp_lang_gcc;
	  switch (elfDbg->elf_getehdr ()->e_type)
	    {
	    case ET_EXEC:
	    case ET_DYN:
	      // set the real object timestamp from the executable's N_OPT stab
	      // due to bug #4796329
	      module->real_timestamp = stb.n_value;
	      break;
	    default:
	      module->curr_timestamp = stb.n_value;
	      break;
	    }
	  break;
	case N_GSYM:
	  if ((str == NULL) || strncmp (str, NTXT ("__KAI_K"), 7))
	    break;
	  str += 7;
	  if (!strncmp (str, NTXT ("CC_"), 3))
	    _lang_code = Sp_lang_KAI_KCC;
	  else if (!strncmp (str, NTXT ("cc_"), 3))
	    _lang_code = Sp_lang_KAI_Kcc;
	  else if (!strncmp (str, NTXT ("PTS_"), 4) &&
		   (_lang_code != Sp_lang_KAI_KCC) &&
		   (_lang_code != Sp_lang_KAI_Kcc))
	    _lang_code = Sp_lang_KAI_KPTS;
	  break;
	case N_BINCL:
	  includes->new_include_file (module->setIncludeFile (str), func);
	  break;
	case N_EINCL:
	  includes->end_include_file (func);
	  break;
	case N_SOL:
	  if (str == NULL)
	    break;
	  lineno = xline + n_desc;
	  if (lineno > 0 && func && func->line_first <= 0)
	    {
	      includes->push_src_files (func);
	      func->add_PC_info (0, lineno);
	      no_stabs = false;
	    }
	  if (streq (sbase, str))
	    {
	      module->setIncludeFile (NULL);
	      snprintf (curr_src, sizeof (curr_src), NTXT ("%s"), module->file_name);
	      includes->new_src_file (module->getMainSrc (), lineno, func);
	    }
	  else
	    {
	      if (streq (sbase, get_basename (str)))
		{
		  module->setIncludeFile (NULL);
		  snprintf (curr_src, sizeof (curr_src), NTXT ("%s"), module->file_name);
		  includes->new_src_file (module->setIncludeFile (curr_src), lineno, func);
		}
	      else
		{
		  if (*str == '/')
		    snprintf (curr_src, sizeof (curr_src), NTXT ("%s"), str);
		  else
		    {
		      size_t last = strlen (n_so);
		      if (last == 0 || n_so[last - 1] != '/')
			snprintf (curr_src, sizeof (curr_src), NTXT ("%s/%s"), n_so, str);
		      else
			snprintf (curr_src, sizeof (curr_src), NTXT ("%s%s"), n_so, str);
		    }
		  includes->new_src_file (module->setIncludeFile (curr_src), lineno, func);
		}
	    }
	  break;
	}
    }
  delete includes;
  delete stabReader;
  return no_stabs ? DBGD_ERR_NO_STABS : DBGD_ERR_NONE;
}//srcline_Stabs

static bool
cmp_func_name (char *fname, size_t len, char *name, bool fortran)
{
  return (strncmp (name, fname, len) == 0
	  && (name[len] == 0
	      || (fortran && name[len] == '_' && name[len + 1] == 0)));
}

Function *
Stabs::find_func (char *fname, Vector<Function*> *functions, bool fortran, bool inner_names)
{
  char *arg, *name;
  Function *item;
  int index;
  size_t len;

  len = strlen (fname);
  arg = strchr (fname, ':');
  if (arg != NULL)
    {
      if (arg[1] == 'P') // Prototype for function
	return NULL;
      len -= strlen (arg);
    }

  Vec_loop (Function*, functions, index, item)
  {
    name = item->get_mangled_name ();
    if (cmp_func_name (fname, len, name, fortran))
      return item->cardinal ();
  }

  if (inner_names)
    {
      // Dwarf subprograms may only have plain (non-linker) names
      // Retry with inner names only

      Vec_loop (Function*, functions, index, item)
      {
	name = strrchr (item->get_mangled_name (), '.');
	if (!name) continue;
	name++;
	if (cmp_func_name (fname, len, name, fortran))
	  return item->cardinal ();
      }
    }
  return NULL;
}

Map<const char*, Symbol*> *
Stabs::get_elf_symbols ()
{
  Elf *elf = openElf (false);
  if (elf->elfSymbols == NULL)
    {
      Map<const char*, Symbol*> *elfSymbols = new StringMap<Symbol*>(128, 128);
      elf->elfSymbols = elfSymbols;
      for (int i = 0, sz = SymLst ? SymLst->size () : 0; i < sz; i++)
	{
	  Symbol *sym = SymLst->fetch (i);
	  elfSymbols->put (sym->name, sym);
	}
    }
  return elf->elfSymbols;
}

void
Stabs::read_dwarf_from_dot_o (Module *mod)
{
  Dprintf (DEBUG_STABS, NTXT ("stabsModules: %s\n"), STR (mod->get_name ()));
  Vector<Module*> *mods = mod->dot_o_file->seg_modules;
  char *bname = get_basename (mod->get_name ());
  for (int i1 = 0, sz1 = mods ? mods->size () : 0; i1 < sz1; i1++)
    {
      Module *m = mods->fetch (i1);
      Dprintf (DEBUG_STABS, NTXT ("  MOD: %s\n"), STR (m->get_name ()));
      if (dbe_strcmp (bname, get_basename (m->get_name ())) == 0)
	{
	  mod->indexStabsLink = m;
	  m->indexStabsLink = mod;
	  break;
	}
    }
  if (mod->indexStabsLink)
    {
      mod->dot_o_file->objStabs->openDwarf ()->srcline_Dwarf (mod->indexStabsLink);
      Map<const char*, Symbol*> *elfSymbols = get_elf_symbols ();
      Vector<Function*> *funcs = mod->indexStabsLink->functions;
      for (int i1 = 0, sz1 = funcs ? funcs->size () : 0; i1 < sz1; i1++)
	{
	  Function *f1 = funcs->fetch (i1);
	  Symbol *sym = elfSymbols->get (f1->get_mangled_name ());
	  if (sym == NULL)
	    continue;
	  Dprintf (DEBUG_STABS, NTXT ("  Symbol: %s func=%p\n"), STR (sym->name), sym->func);
	  Function *f = sym->func;
	  if (f->indexStabsLink)
	    continue;
	  f->indexStabsLink = f1;
	  f1->indexStabsLink = f;
	  f->copy_PCInfo (f1);
	}
    }
}

Stabs::Stab_status
Stabs::read_archive (LoadObject *lo)
{
  if (openElf (true) == NULL)
    return status;
  check_Symtab ();
  if (elfDbg->dwarf)
    openDwarf ()->archive_Dwarf (lo);

  // get Module/Function lists from stabs info
  Stab_status statusStabs = DBGD_ERR_NO_STABS;
#define ARCHIVE_STABS(sec, secStr, comdat) \
    if ((elfDbg->sec) != 0  && (elfDbg->secStr) != 0 && \
	archive_Stabs(lo, elfDbg->sec, elfDbg->secStr, comdat) == DBGD_ERR_NONE) \
	statusStabs = DBGD_ERR_NONE

  // prefer index stabs (where they exist) since they're most appropriate
  // for loadobjects and might have N_CPROF stabs for ABS/CPF
  ARCHIVE_STABS (stabIndex, stabIndexStr, true);
  ARCHIVE_STABS (stabExcl, stabExclStr, false);
  ARCHIVE_STABS (stab, stabStr, false);

  // Add all unassigned functions to the <unknown> module
  Symbol *sitem, *alias;
  int index;
  Vec_loop (Symbol*, SymLst, index, sitem)
  {
    if (sitem->func || (sitem->size == 0) || (sitem->flags & SYM_UNDEF))
      continue;
    alias = sitem->alias;
    if (alias)
      {
	if (alias->func == NULL)
	  {
	    alias->func = createFunction (lo, lo->noname, alias);
	    alias->func->alias = alias->func;
	  }
	if (alias != sitem)
	  {
	    sitem->func = createFunction (lo, alias->func->module, sitem);
	    sitem->func->alias = alias->func;
	  }
      }
    else
      sitem->func = createFunction (lo, lo->noname, sitem);
  }
  if (pltSym)
    {
      pltSym->func = createFunction (lo, lo->noname, pltSym);
      pltSym->func->flags |= FUNC_FLAG_PLT;
    }

  // need Module association, so this must be done after handling Modules
  check_AnalyzerInfo ();

  if (dwarf && dwarf->status == DBGD_ERR_NONE)
    return DBGD_ERR_NONE;
  return statusStabs;
}//read_archive

Function *
Stabs::createFunction (LoadObject *lo, Module *module, Symbol *sym)
{
  Function *func = dbeSession->createFunction ();
  func->module = module;
  func->img_fname = path;
  func->img_offset = sym->img_offset;
  func->save_addr = sym->save;
  func->size = sym->size;
  func->set_name (sym->name);
  func->elfSym = sym;
  module->functions->append (func);
  lo->functions->append (func);
  return func;
}

void
Stabs::fixSymtabAlias ()
{
  int ind, i, k;
  Symbol *sym, *bestAlias;
  SymLst->sort (SymImgOffsetCmp);
  ind = SymLst->size () - 1;
  for (i = 0; i < ind; i++)
    {
      bestAlias = SymLst->fetch (i);
      if (bestAlias->img_offset == 0) // Ignore this bad symbol
	continue;
      sym = SymLst->fetch (i + 1);
      if (bestAlias->img_offset != sym->img_offset)
	{
	  if ((bestAlias->size == 0) ||
	      (sym->img_offset < bestAlias->img_offset + bestAlias->size))
	    bestAlias->size = sym->img_offset - bestAlias->img_offset;
	  continue;
	}

      // Find a "best" alias
      size_t bestLen = strlen (bestAlias->name);
      int64_t maxSize = bestAlias->size;
      for (k = i + 1; k <= ind; k++)
	{
	  sym = SymLst->fetch (k);
	  if (bestAlias->img_offset != sym->img_offset)
	    { // no more aliases
	      if ((maxSize == 0) ||
		  (sym->img_offset < bestAlias->img_offset + maxSize))
		maxSize = sym->img_offset - bestAlias->img_offset;
	      break;
	    }
	  if (maxSize < sym->size)
	    maxSize = sym->size;
	  size_t len = strlen (sym->name);
	  if (len < bestLen)
	    {
	      bestAlias = sym;
	      bestLen = len;
	    }
	}
      for (; i < k; i++)
	{
	  sym = SymLst->fetch (i);
	  if (sym != bestAlias)
	    sym->alias = bestAlias;
	  sym->size = maxSize;
	}
      i--;
    }
}

void
Stabs::check_Symtab ()
{
  if (st_check_symtab)
    return;
  st_check_symtab = true;

  Elf *elf = openElf (true);
  if (elf == NULL)
    return;
  if (elf->plt != 0)
    {
      Elf_Internal_Shdr *shdr = elf->get_shdr (elf->plt);
      if (shdr)
	{
	  pltSym = new Symbol (SymLst);
	  pltSym->value = shdr->sh_addr;
	  pltSym->size = shdr->sh_size;
	  pltSym->img_offset = shdr->sh_offset;
	  pltSym->name = dbe_strdup (NTXT ("@plt"));
	  pltSym->flags |= SYM_PLT;
	}
    }

  // Read first static symbols
  readSymSec (elf, false);

  // Read dynamic symbols
  readSymSec (elf, true);
}

void
Stabs::readSymSec (Elf *elf, bool is_dynamic)
{
  Symbol *sitem;
  Sp_lang_code local_lcode;
  unsigned int tot = elf->elf_getSymCount (is_dynamic);

  // read func symbolic table
  for (unsigned int n = 0; n < tot; n++)
    {
      Elf_Internal_Sym Sym;
      asymbol *asym;
      asym = elf->elf_getsym (n, &Sym, is_dynamic);
      // TBD: convert this check to an assert
      if (asym == NULL)
	break;
      const char *st_name = bfd_asymbol_name (asym);
      if (st_name == NULL)
	continue;
      switch (GELF_ST_TYPE (Sym.st_info))
	{
	case STT_FUNC:
	  if (Sym.st_size == 0)
	    break;
	  if (Sym.st_shndx == 0)
	    {
	      if (Sym.st_value == 0)
		break;
	      sitem = new Symbol (SymLst);
	      sitem->flags |= SYM_UNDEF;
	      if (pltSym)
		sitem->img_offset = pltSym->img_offset +
		      Sym.st_value - pltSym->value;
	    }
	  else
	    {
	      Elf_Internal_Shdr *shdrp = elfDis->get_shdr (Sym.st_shndx);
	      if (shdrp == NULL)
		break;
	      sitem = new Symbol (SymLst);
	      sitem->img_offset = shdrp->sh_offset +
		      Sym.st_value - shdrp->sh_addr;
	    }
	  sitem->size = Sym.st_size;
	  sitem->name = dbe_strdup (st_name);
	  sitem->value = is_relocatable () ? sitem->img_offset : Sym.st_value;
	  if (GELF_ST_BIND (Sym.st_info) == STB_LOCAL)
	    {
	      sitem->local_ind = LocalFile->size () - 1;
	      LocalLst->append (sitem);
	    }
	  break;
	case STT_NOTYPE:
	  if (streq (st_name, NTXT ("gcc2_compiled.")))
	    {
	      sitem = new Symbol (SymLst);
	      sitem->lang_code = Sp_lang_gcc;
	      sitem->name = dbe_strdup (st_name);
	      sitem->local_ind = LocalFile->size () - 1;
	      LocalLst->append (sitem);
	    }
	  break;
	case STT_OBJECT:
	  if (!strncmp (st_name, NTXT ("__KAI_KPTS_"), 11))
	    local_lcode = Sp_lang_KAI_KPTS;
	  else if (!strncmp (st_name, NTXT ("__KAI_KCC_"), 10))
	    local_lcode = Sp_lang_KAI_KCC;
	  else if (!strncmp (st_name, NTXT ("__KAI_Kcc_"), 10))
	    local_lcode = Sp_lang_KAI_Kcc;
	  else
	    break;
	  sitem = new Symbol (LocalLst);
	  sitem->lang_code = local_lcode;
	  sitem->name = dbe_strdup (st_name);
	  break;
	case STT_FILE:
	  {
	    int last = LocalFile->size () - 1;
	    if (last >= 0 && LocalFileIdx->fetch (last) == LocalLst->size ())
	      {
		// There were no local functions in the latest file.
		free (LocalFile->get (last));
		LocalFile->store (last, dbe_strdup (st_name));
	      }
	    else
	      {
		LocalFile->append (dbe_strdup (st_name));
		LocalFileIdx->append (LocalLst->size ());
	      }
	    break;
	  }
	}
    }
  removeDupSyms ();
  fixSymtabAlias ();
  SymLst->sort (SymValueCmp);
  get_save_addr (elf->need_swap_endian);
}

void
Stabs::get_save_addr (bool need_swap_endian)
{
  if (elfDis->is_Intel ())
    {
      for (int j = 0, sz = SymLst ? SymLst->size () : 0; j < sz; j++)
	{
	  Symbol *sitem = SymLst->fetch (j);
	  sitem->save = 0;
	}
      return;
    }
  for (int j = 0, sz = SymLst ? SymLst->size () : 0; j < sz; j++)
    {
      Symbol *sitem = SymLst->fetch (j);
      sitem->save = FUNC_NO_SAVE;

      // If an image offset is not known skip it.
      // Works for artificial symbols like '@plt' as well.
      if (sitem->img_offset == 0)
	continue;

      bool is_o7_moved = false;
      int64_t off = sitem->img_offset;
      for (int i = 0; i < sitem->size; i += 4)
	{
	  unsigned int cmd;
	  if (elfDis->get_data (off, sizeof (cmd), &cmd) == NULL)
	    break;
	  if (need_swap_endian)
	    SWAP_ENDIAN (cmd);
	  off += sizeof (cmd);
	  if ((cmd & 0xffffc000) == 0x9de38000)
	    { // save %sp, ??, %sp
	      sitem->save = i;
	      break;
	    }
	  else if ((cmd & 0xc0000000) == 0x40000000 || // call ??
	 (cmd & 0xfff80000) == 0xbfc00000)
	    { // jmpl ??, %o7
	      if (!is_o7_moved)
		{
		  sitem->save = FUNC_ROOT;
		  break;
		}
	    }
	  else if ((cmd & 0xc1ffe01f) == 0x8010000f)    // or %g0,%o7,??
	    is_o7_moved = true;
	}
    }
}

uint64_t
Stabs::mapOffsetToAddress (uint64_t img_offset)
{
  Elf *elf = openElf (false);
  if (elf == NULL)
    return 0;
  if (is_relocatable ())
    return img_offset;
  for (unsigned int sec = 1; sec < elf->elf_getehdr ()->e_shnum; sec++)
    {
      Elf_Internal_Shdr *shdr = elf->get_shdr (sec);
      if (shdr == NULL)
	continue;
      if (img_offset >= (uint64_t) shdr->sh_offset
	  && img_offset < (uint64_t) (shdr->sh_offset + shdr->sh_size))
	return shdr->sh_addr + (img_offset - shdr->sh_offset);
    }
  return 0;
}

Stabs::Stab_status
Stabs::archive_Stabs (LoadObject *lo, unsigned int StabSec,
		      unsigned int StabStrSec, bool comdat)
{
  StabReader *stabReader = new StabReader (openElf (true), platform, StabSec, StabStrSec);
  int tot = stabReader->stabCnt;
  if (tot < 0)
    {
      delete stabReader;
      return DBGD_ERR_NO_STABS;
    }

  char *sbase = NTXT (""), *arg, *fname, sname[2 * MAXPATHLEN];
  int lastMod, phase, stabs_level, modCnt = 0;
  Function *func = NULL;
  Module *mod;
#define INIT_MOD    phase = 0; stabs_level = 0; *sname = '\0'; mod = NULL

  bool updateStabsMod = false;
  if (comdat && ((elfDbg->elf_getehdr ()->e_type == ET_EXEC) || (elfDbg->elf_getehdr ()->e_type == ET_DYN)))
    {
      if (stabsModules == NULL)
	stabsModules = new Vector<Module*>();
      updateStabsMod = true;
    }
  INIT_MOD;
  lastMod = lo->seg_modules->size ();

  for (int n = 0; n < tot; n++)
    {
      struct stab stb;
      char *str = stabReader->get_stab (&stb, comdat);
      switch (stb.n_type)
	{
	case N_FUN:
	  // Ignore a COMDAT function, if there are two or more modules in 'lo'
	  if (comdat && stb.n_other == 1 && modCnt > 1)
	    break;
	case N_OUTL:
	case N_ALIAS:
	case N_ENTRY:
	  if (mod == NULL || str == NULL
	      || (stb.n_type != N_ENTRY && stabs_level != 0))
	    break;
	  if (*str == '@')
	    {
	      str++;
	      if (*str == '>' || *str == '<')
		str++;
	    }

	  fname = dbe_strdup (str);
	  arg = strchr (fname, ':');
	  if (arg != NULL)
	    {
	      if (!strncmp (arg, NTXT (":P"), 2))
		{ // just prototype
		  free (fname);
		  break;
		}
	      *arg = '\0';
	    }

	  func = append_Function (mod, fname);
	  free (fname);
	  break;
	case N_CMDLINE:
	  if (str && mod)
	    {
	      char *comp_flags = strchr (str, ';');
	      if (comp_flags)
		{
		  mod->comp_flags = dbe_strdup (comp_flags + 1);
		  mod->comp_dir = dbe_strndup (str, comp_flags - str);
		}
	    }
	  break;
	case N_LBRAC:
	  stabs_level++;
	  break;
	case N_RBRAC:
	  stabs_level--;
	  break;
	case N_UNDF:
	  INIT_MOD;
	  break;
	case N_ENDM:
	  INIT_MOD;
	  break;
	case N_OPT:
	  stabReader->parse_N_OPT (mod, str);
	  if (mod && (str != NULL) && streq (str, NTXT ("gcc2_compiled.")))
	    // Is it anachronism ?
	    mod->lang_code = Sp_lang_gcc;
	  break;
	case N_GSYM:
	  if (mod && (str != NULL))
	    {
	      if (strncmp (str, NTXT ("__KAI_K"), 7))
		break;
	      str += 7;
	      if (!strncmp (str, NTXT ("CC_"), 3))
		mod->lang_code = Sp_lang_KAI_KCC;
	      else if (!strncmp (str, NTXT ("cc_"), 3))
		mod->lang_code = Sp_lang_KAI_Kcc;
	      else if (!strncmp (str, NTXT ("PTS_"), 4) &&
		       (mod->lang_code != Sp_lang_KAI_KCC) &&
		       (mod->lang_code != Sp_lang_KAI_Kcc))
		mod->lang_code = Sp_lang_KAI_KPTS;
	    }
	  break;
	case N_SO:
	  if (str == NULL || *str == '\0')
	    {
	      INIT_MOD;
	      break;
	    }
	  if (phase == 0)
	    {
	      phase = 1;
	      sbase = str;
	    }
	  else
	    {
	      if (*str == '/')
		sbase = str;
	      else
		{
		  size_t last = strlen (sbase);
		  if (last == 0 || sbase[last - 1] != '/')
		    snprintf (sname, sizeof (sname), NTXT ("%s/%s"), sbase, str);
		  else
		    snprintf (sname, sizeof (sname), NTXT ("%s%s"), sbase, str);
		  sbase = sname;
		}
	      mod = append_Module (lo, sbase, lastMod);
	      if (updateStabsMod)
		stabsModules->append (mod);
	      mod->hasStabs = true;
	      modCnt++;
	      if ((mod->lang_code != Sp_lang_gcc) &&
		  (mod->lang_code != Sp_lang_KAI_KPTS) &&
		  (mod->lang_code != Sp_lang_KAI_KCC) &&
		  (mod->lang_code != Sp_lang_KAI_Kcc))
		mod->lang_code = (Sp_lang_code) stb.n_desc;
	      *sname = '\0';
	      phase = 0;
	    }
	  break;
	case N_OBJ:
	  if (str == NULL)
	    break;
	  if (phase == 0)
	    {
	      phase = 1;
	      sbase = str;
	    }
	  else
	    {
	      if (*str == '/')
		sbase = str;
	      else
		{
		  size_t last = strlen (sbase);
		  if (last == 0 || sbase[last - 1] != '/')
		    snprintf (sname, sizeof (sname), NTXT ("%s/%s"), sbase, str);
		  else
		    snprintf (sname, sizeof (sname), NTXT ("%s%s"), sbase, str);
		  sbase = sname;
		}
	      if (mod && (mod->dot_o_file == NULL))
		{
		  if (strcmp (sbase, NTXT ("/")) == 0)
		    mod->set_name (dbe_strdup (path));
		  else
		    {
		      mod->set_name (dbe_strdup (sbase));
		      mod->dot_o_file = mod->createLoadObject (sbase);
		    }
		}
	      *sname = '\0';
	      phase = 0;
	    }
	  break;
	case N_CPROF:
	  cpf_stabs_t map;
	  Dprintf (DEBUG_STABS, NTXT ("N_CPROF n_desc=%x n_value=0x%04x mod=%s\n"),
		   stb.n_desc, stb.n_value, (mod == NULL) ? NTXT ("???") : mod->get_name ());
	  map.type = stb.n_desc;
	  map.offset = stb.n_value;
	  map.module = mod;
	  analyzerInfoMap.append (map);
	  break;
	}
    }
  delete stabReader;
  return func ? DBGD_ERR_NONE : DBGD_ERR_NO_STABS;
}

Module *
Stabs::append_Module (LoadObject *lo, char *name, int lastMod)
{
  Module *module;
  int size;
  Symbol *sitem;

  if (lo->seg_modules != NULL)
    {
      size = lo->seg_modules->size ();
      if (size < lastMod)
	lastMod = size;
      for (int i = 0; i < lastMod; i++)
	{
	  module = lo->seg_modules->fetch (i);
	  if (module->linkerStabName && streq (module->linkerStabName, name))
	    return module;
	}
    }
  module = dbeSession->createModule (lo, NULL);
  module->set_file_name (dbe_strdup (name));
  module->linkerStabName = dbe_strdup (module->file_name);

  // Append all functions with 'local_ind == -1' to the module.
  if (LocalLst->size () > 0)
    {
      sitem = LocalLst->fetch (0);
      if (!sitem->defined && sitem->local_ind == -1)
	// Append all functions with 'local_ind == -1' to the module.
	append_local_funcs (module, 0);
    }

  // Append local func
  char *basename = get_basename (name);
  size = LocalFile->size ();
  for (int i = 0; i < size; i++)
    {
      if (streq (basename, LocalFile->fetch (i)))
	{
	  int local_ind = LocalFileIdx->fetch (i);
	  if (local_ind >= LocalLst->size ())
	    break;
	  sitem = LocalLst->fetch (local_ind);
	  if (!sitem->defined)
	    {
	      append_local_funcs (module, local_ind);
	      break;
	    }
	}
    }
  return module;
}

void
Stabs::append_local_funcs (Module *module, int first_ind)
{
  Symbol *sitem = LocalLst->fetch (first_ind);
  int local_ind = sitem->local_ind;
  int size = LocalLst->size ();
  for (int i = first_ind; i < size; i++)
    {
      sitem = LocalLst->fetch (i);
      if (sitem->local_ind != local_ind)
	break;
      sitem->defined = true;

      // 3rd party compiled. e.g., Gcc or KAI compiled
      if (sitem->lang_code != Sp_lang_unknown)
	{
	  if (module->lang_code == Sp_lang_unknown)
	    module->lang_code = sitem->lang_code;
	  continue;
	}
      if (sitem->func)
	continue;
      Function *func = dbeSession->createFunction ();
      sitem->func = func;
      func->img_fname = path;
      func->img_offset = sitem->img_offset;
      func->save_addr = sitem->save;
      func->size = sitem->size;
      func->module = module;
      func->set_name (sitem->name);
      module->functions->append (func);
      module->loadobject->functions->append (func);
    }
}

Function *
Stabs::append_Function (Module *module, char *fname)
{
  Symbol *sitem, *sptr;
  Function *func;
  long sid, index;
  char *name;
  if (SymLstByName == NULL)
    {
      SymLstByName = SymLst->copy ();
      SymLstByName->sort (SymNameCmp);
    }
  sptr = new Symbol;
  if (module->lang_code == N_SO_FORTRAN || module->lang_code == N_SO_FORTRAN90)
    {
      char *fortran = dbe_sprintf (NTXT ("%s_"), fname); // FORTRAN name
      sptr->name = fortran;
      sid = SymLstByName->bisearch (0, -1, &sptr, SymNameCmp);
      if (sid == -1)
	{
	  free (fortran);
	  sptr->name = fname;
	  sid = SymLstByName->bisearch (0, -1, &sptr, SymNameCmp);
	}
      else
	fname = fortran;
    }
  else
    {
      sptr->name = fname;
      sid = SymLstByName->bisearch (0, -1, &sptr, SymNameCmp);
    }
  sptr->name = NULL;
  delete sptr;

  if (sid == -1)
    {
      Vec_loop (Symbol*, SymLstByName, index, sitem)
      {
	if (strncmp (sitem->name, NTXT ("$X"), 2) == 0
	    || strncmp (sitem->name, NTXT (".X"), 2) == 0)
	  {
	    char *n = strchr (((sitem->name) + 2), (int) '.');
	    if (n != NULL)
	      name = n + 1;
	    else
	      name = sitem->name;
	  }
	else
	  name = sitem->name;
	if (name != NULL && fname != NULL && (strcmp (name, fname) == 0))
	  {
	    sid = index;
	    break;
	  }
      }
    }
  if (sid != -1)
    {
      sitem = SymLstByName->fetch (sid);
      if (sitem->alias)
	sitem = sitem->alias;
      if (sitem->func)
	return sitem->func;
      sitem->func = func = dbeSession->createFunction ();
      func->img_fname = path;
      func->img_offset = sitem->img_offset;
      func->save_addr = sitem->save;
      func->size = sitem->size;
    }
  else
    func = dbeSession->createFunction ();

  func->module = module;
  func->set_name (fname);
  module->functions->append (func);
  module->loadobject->functions->append (func);
  return func;
}

Dwarf *
Stabs::openDwarf ()
{
  if (dwarf == NULL)
    {
      dwarf = new Dwarf (this);
      check_Symtab ();
      dump();
    }
  return dwarf;
}

void
Stabs::read_hwcprof_info (Module *module)
{
  openDwarf ()->read_hwcprof_info (module);
}

void
Stabs::dump ()
{
  if (!DUMP_ELF_SYM)
    return;
  printf (NTXT ("\n======= Stabs::dump: %s =========\n"), path ? path : NTXT ("NULL"));
  int i, sz;
  if (LocalFile)
    {
      sz = LocalFile->size ();
      for (i = 0; i < sz; i++)
	printf ("  %3d: %5d '%s'\n", i, LocalFileIdx->fetch (i),
		LocalFile->fetch (i));
    }
  SymLst->dump ("SymLst");
  LocalLst->dump ("LocalLst");
  printf (NTXT ("\n===== END of Stabs::dump: %s =========\n\n"),
  path ? path : NTXT ("NULL"));
}

///////////////////////////////////////////////////////////////////////////////
//  Class Include
Include::Include ()
{
  stack = new Vector<SrcFileInfo*>;
}

Include::~Include ()
{
  Destroy (stack);
}

void
Include::new_src_file (SourceFile *source, int lineno, Function *func)
{
  for (int index = stack->size () - 1; index >= 0; index--)
    {
      if (source == stack->fetch (index)->srcfile)
	{
	  for (int i = stack->size () - 1; i > index; i--)
	    {
	      delete stack->remove (i);
	      if (func && func->line_first > 0)
		func->popSrcFile ();
	    }
	  return;
	}
    }
  if (func && func->line_first > 0)
    func->pushSrcFile (source, lineno);

  SrcFileInfo *sfinfo = new SrcFileInfo;
  sfinfo->srcfile = source;
  sfinfo->lineno = lineno;
  stack->append (sfinfo);
}

void
Include::push_src_files (Function *func)
{
  int index;
  SrcFileInfo *sfinfo;

  if (func->line_first <= 0 && stack->size () > 0)
    {
      sfinfo = stack->fetch (stack->size () - 1);
      func->setDefSrc (sfinfo->srcfile);
    }
  Vec_loop (SrcFileInfo*, stack, index, sfinfo)
  {
    func->pushSrcFile (sfinfo->srcfile, sfinfo->lineno);
  }
}

void
Include::new_include_file (SourceFile *source, Function *func)
{
  if (stack->size () == 1 && stack->fetch (0)->srcfile == source)
    // workaroud for gcc; gcc creates 'N_BINCL' stab for main source
    return;
  if (func && func->line_first > 0)
    func->pushSrcFile (source, 0);

  SrcFileInfo *sfinfo = new SrcFileInfo;
  sfinfo->srcfile = source;
  sfinfo->lineno = 0;
  stack->append (sfinfo);
}

void
Include::end_include_file (Function *func)
{
  int index = stack->size () - 1;
  if (index > 0)
    {
      delete stack->remove (index);
      if (func && func->line_first > 0)
	func->popSrcFile ();
    }
}

#define RET_S(x)   if (t == x) return (char *) #x
char *
StabReader::get_type_name (int t)
{
  RET_S (N_UNDF);
  RET_S (N_ABS);
  RET_S (N_TEXT);
  RET_S (N_DATA);
  RET_S (N_BSS);
  RET_S (N_COMM);
  RET_S (N_FN);
  RET_S (N_EXT);
  RET_S (N_TYPE);
  RET_S (N_GSYM);
  RET_S (N_FNAME);
  RET_S (N_FUN);
  RET_S (N_OUTL);
  RET_S (N_STSYM);
  RET_S (N_TSTSYM);
  RET_S (N_LCSYM);
  RET_S (N_TLCSYM);
  RET_S (N_MAIN);
  RET_S (N_ROSYM);
  RET_S (N_FLSYM);
  RET_S (N_TFLSYM);
  RET_S (N_PC);
  RET_S (N_CMDLINE);
  RET_S (N_OBJ);
  RET_S (N_OPT);
  RET_S (N_RSYM);
  RET_S (N_SLINE);
  RET_S (N_XLINE);
  RET_S (N_ILDPAD);
  RET_S (N_SSYM);
  RET_S (N_ENDM);
  RET_S (N_SO);
  RET_S (N_MOD);
  RET_S (N_EMOD);
  RET_S (N_READ_MOD);
  RET_S (N_ALIAS);
  RET_S (N_LSYM);
  RET_S (N_BINCL);
  RET_S (N_SOL);
  RET_S (N_PSYM);
  RET_S (N_EINCL);
  RET_S (N_ENTRY);
  RET_S (N_SINCL);
  RET_S (N_LBRAC);
  RET_S (N_EXCL);
  RET_S (N_USING);
  RET_S (N_ISYM);
  RET_S (N_ESYM);
  RET_S (N_PATCH);
  RET_S (N_CONSTRUCT);
  RET_S (N_DESTRUCT);
  RET_S (N_CODETAG);
  RET_S (N_FUN_CHILD);
  RET_S (N_RBRAC);
  RET_S (N_BCOMM);
  RET_S (N_TCOMM);
  RET_S (N_ECOMM);
  RET_S (N_XCOMM);
  RET_S (N_ECOML);
  RET_S (N_WITH);
  RET_S (N_LENG);
  RET_S (N_CPROF);
  RET_S (N_BROWS);
  RET_S (N_FUN_PURE);
  RET_S (N_FUN_ELEMENTAL);
  RET_S (N_FUN_RECURSIVE);
  RET_S (N_FUN_AMD64_PARMDUMP);
  RET_S (N_SYM_OMP_TLS);
  RET_S (N_SO_AS);
  RET_S (N_SO_C);
  RET_S (N_SO_ANSI_C);
  RET_S (N_SO_CC);
  RET_S (N_SO_FORTRAN);
  RET_S (N_SO_FORTRAN77);
  RET_S (N_SO_PASCAL);
  RET_S (N_SO_FORTRAN90);
  RET_S (N_SO_JAVA);
  RET_S (N_SO_C99);
  return NULL;
}
