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

#include "util.h"
#include "bfd.h"
#include "elf-bfd.h"
#include "Elf.h"
#include "Map.h"
#include "StringBuilder.h"
#include "DbeFile.h"

typedef uint32_t Elf32_Word;
typedef uint32_t Elf64_Word;
typedef uint32_t Elf32_Addr;
typedef uint64_t Elf64_Addr;
typedef uint64_t Elf64_Xword;
typedef int32_t Elf32_Sword;
typedef int64_t Elf64_Sxword;
typedef uint16_t Elf32_Half;
typedef uint16_t Elf64_Half;

// Ancillary entry
typedef struct
{
  Elf32_Word a_tag;         /* how to interpret value */
  union
  {
    Elf32_Word a_val;
    Elf32_Addr a_ptr;
  } a_un;
} Elf32_Ancillary;

struct S_Elf64_Ancillary
{
  Elf64_Xword a_tag;        /* how to interpret value */
  union
  {
    Elf64_Xword a_val;
    Elf64_Addr a_ptr;
  } a_un;
};

/* Dynamic section entry.  */
typedef struct
{
  Elf32_Sword d_tag;        /* Dynamic entry type */

  union
  {
    Elf32_Word d_val;       /* Integer value */
    Elf32_Addr d_ptr;       /* Address value */
  } d_un;
} Elf32_Dyn;

struct S_Elf64_Dyn
{
  Elf64_Sxword d_tag;       /* Dynamic entry type */

  union
  {
    Elf64_Xword d_val;      /* Integer value */
    Elf64_Addr d_ptr;       /* Address value */
  } d_un;
};


// Symbol table
typedef struct
{
  Elf32_Word st_name;
  Elf32_Addr st_value;
  Elf32_Word st_size;
  unsigned char st_info;    /* bind, type: ELF_32_ST_... */
  unsigned char st_other;
  Elf32_Half st_shndx;      /* SHN_... */
} Elf32_Sym;

typedef struct
{
  Elf64_Word st_name;
  unsigned char st_info;    /* bind, type: ELF_64_ST_... */
  unsigned char st_other;
  Elf64_Half st_shndx;      /* SHN_... */
  Elf64_Addr st_value;
  Elf64_Xword st_size;
} Elf64_Sym;


// Relocation
typedef struct
{
  Elf32_Addr r_offset;
  Elf32_Word r_info;        /* sym, type: ELF32_R_... */
} Elf32_Rel;

typedef struct
{
  Elf32_Addr r_offset;
  Elf32_Word r_info;        /* sym, type: ELF32_R_... */
  Elf32_Sword r_addend;
} Elf32_Rela;

typedef struct
{
  Elf64_Addr r_offset;
  Elf64_Xword r_info;       /* sym, type: ELF64_R_... */
} Elf64_Rel;

typedef struct
{
  Elf64_Addr r_offset;
  Elf64_Xword r_info;       /* sym, type: ELF64_R_... */
  Elf64_Sxword r_addend;
} Elf64_Rela;

int Elf::bfd_status = -1;

void
Elf::elf_init ()
{
  if (bfd_status == -1)
    bfd_status = bfd_init ();
}

Elf::Elf (char *filename) : DbeMessages (), Data_window (filename)
{
  ehdrp = NULL;
  data = NULL;
  ancillary_files = NULL;
  elfSymbols = NULL;
  gnu_debug_file = NULL;
  dbeFile = NULL;
  abfd = NULL;
  if (bfd_status != BFD_INIT_MAGIC)
    {
      status = ELF_ERR_CANT_OPEN_FILE;
      return;
    }
  abfd = bfd_openr (filename, NULL);
  if (abfd == NULL)
    {
      status = ELF_ERR_CANT_OPEN_FILE;
      return;
    }
  if (!bfd_check_format (abfd, bfd_object))
    {
      bfd_close (abfd);
      abfd = NULL;
      status = ELF_ERR_CANT_OPEN_FILE;
      return;
    }
  ehdrp = elf_getehdr ();
  if (ehdrp == NULL)
    {
      bfd_close (abfd);
      abfd = NULL;
      status = ELF_ERR_BAD_ELF_FORMAT;
      return;
    }
  elf_class = ehdrp->e_ident[EI_CLASS];
  elf_datatype = ehdrp->e_ident[EI_DATA];

  if (not_opened ())
    {
      status = ELF_ERR_CANT_OPEN_FILE;
      return;
    }
  status = ELF_ERR_NONE;

#if ARCH(SPARC)
  need_swap_endian = is_Intel ();
#else
  need_swap_endian = !is_Intel ();
#endif

  analyzerInfo = 0;
  SUNW_ldynsym = 0;
  gnuLink = 0;
  stab = 0;
  stabStr = 0;
  stabIndex = 0;
  stabIndexStr = 0;
  stabExcl = 0;
  stabExclStr = 0;
  symtab = 0;
  dynsym = 0;
  info = 0;
  plt = 0;
  dwarf = false;

  for (unsigned int sec = 1; sec < elf_getehdr ()->e_shnum; sec++)
    {
      char *name = get_sec_name (sec);
      if (name == NULL)
	continue;
      if (streq (name, NTXT (".stab")))
	stab = sec;
      else if (streq (name, NTXT (".stabstr")))
	stabStr = sec;
      else if (streq (name, NTXT (".stab.index")))
	stabIndex = sec;
      else if (streq (name, NTXT (".stab.indexstr")))
	stabIndexStr = sec;
      else if (streq (name, NTXT (".stab.excl")))
	stabExcl = sec;
      else if (streq (name, NTXT (".stab.exclstr")))
	stabExclStr = sec;
      else if (streq (name, NTXT (".gnu_debuglink")))
	gnuLink = sec;
      else if (streq (name, NTXT (".__analyzer_info")))
	analyzerInfo = sec;
      else if (streq (name, NTXT (".info")))
	info = true;
      else if (streq (name, NTXT (".plt")))
	plt = sec;
      else if (streq (name, NTXT (".SUNW_ldynsym")))
	SUNW_ldynsym = sec;
      else if (streq (name, NTXT (".dynsym")))
	dynsym = sec;
      else if (streq (name, NTXT (".symtab")))
	symtab = sec;
      else if (strncmp (name, NTXT (".debug"), 6) == 0)
	dwarf = true;
    }
  if (fd != -1)
    {
      close (fd);
      fd = -1;
    }
}

Elf::~Elf ()
{
  if (data)
    {
      for (int i = 0; i < (int) ehdrp->e_shnum; i++)
	{
	  Elf_Data *p = data[i];
	  if (p && !mmap_on_file && (p->d_flags & SHF_SUNW_ABSENT) == 0)
	    free (p->d_buf);
	  delete p;
	}
      free (data);
    }
  if (ancillary_files)
    {
      ancillary_files->destroy ();
      delete ancillary_files;
    }
  delete elfSymbols;
  delete gnu_debug_file;
  delete dbeFile;
  if (abfd)
    bfd_close (abfd);
}

Elf_Internal_Ehdr *
Elf::elf_getehdr ()
{
  if (ehdrp == NULL && abfd)
    ehdrp = elf_elfheader (abfd);
  return ehdrp;
}

Elf_Internal_Phdr *
Elf::get_phdr (unsigned int ndx)
{
  if (ehdrp == NULL || ndx >= ehdrp->e_phnum)
    return NULL;
  return &(elf_tdata (abfd)->phdr[ndx]);
}

Elf_Internal_Shdr *
Elf::get_shdr (unsigned int ndx)
{
  if (ehdrp == NULL || ndx >= ehdrp->e_shnum)
    return NULL;
  return elf_elfsections (abfd)[ndx];
}

Elf64_Dyn *
Elf::elf_getdyn (Elf_Internal_Phdr *phdr, unsigned int ndx, Elf64_Dyn *pdyn)
{
  if (elf_getclass () == ELFCLASS32)
    {
      if (ndx * sizeof (Elf32_Dyn) >= phdr->p_filesz)
	return NULL;
      Elf32_Dyn *hdr = (Elf32_Dyn*) bind (phdr->p_offset + ndx * sizeof (Elf32_Dyn),
					  sizeof (Elf32_Dyn));
      if (hdr == NULL)
	return NULL;
      pdyn->d_tag = decode (hdr->d_tag);
      pdyn->d_un.d_val = decode (hdr->d_un.d_val);
    }
  else
    {
      if (ndx * sizeof (Elf64_Dyn) >= phdr->p_filesz)
	return NULL;
      Elf64_Dyn *hdr = (Elf64_Dyn*) bind (phdr->p_offset + ndx * sizeof (Elf64_Dyn),
					  sizeof (Elf64_Dyn));
      if (hdr == NULL)
	return NULL;
      pdyn->d_tag = decode (hdr->d_tag);
      pdyn->d_un.d_val = decode (hdr->d_un.d_val);
    }
  return pdyn;
}

unsigned
Elf::elf_version (unsigned ver)
{
  // We compile locally, no need to check the version
  return ver;
}

Elf *
Elf::elf_begin (char *fname, Elf_status *stp)
{
  if (fname == NULL)
    {
      if (stp)
	*stp = ELF_ERR_CANT_OPEN_FILE;
      return NULL;
    }
  Elf *elf = new Elf (fname);
  if (stp)
    *stp = elf->status;
  if (elf->status != ELF_ERR_NONE)
    {
      delete elf;
      return NULL;
    }
#if DEBUG
  if (DUMP_ELF_SEC)
    {
      char *str = elf->dump ();
      fprintf (stderr, NTXT ("%s\n\n"), str);
      free (str);
    }
#endif /* DEBUG */
  return elf;
}

unsigned int
Elf::elf_get_sec_num (const char *name)
{
  if (name == NULL || ehdrp == NULL)
    return 0;
  for (unsigned int sec = 1; sec < ehdrp->e_shnum; sec++)
    {
      Elf_Internal_Shdr *shdr = get_shdr (sec);
      if (shdr == NULL)
	continue;
      char *sname = elf_strptr (ehdrp->e_shstrndx, shdr->sh_name);
      if (sname != NULL && strcmp (name, sname) == 0)
	return sec;
    }
  return 0;
}

char *
Elf::get_sec_name (unsigned int sec)
{
  Elf_Internal_Shdr *shdr = get_shdr (sec);
  if (ehdrp == NULL || shdr == NULL)
    return NULL;
  return elf_strptr (ehdrp->e_shstrndx, shdr->sh_name);
}

Elf_Data *
Elf::elf_getdata (unsigned int sec)
{
  if (data == NULL)
    {
      data = (Elf_Data **) malloc (ehdrp->e_shnum * sizeof (Elf_Data *));
      for (int i = 0; i < (int) ehdrp->e_shnum; i++)
	data[i] = NULL;
    }
  Elf_Data *edta = data[sec];
  if (edta == NULL)
    {
      Elf_Internal_Shdr *shdr = get_shdr (sec);
      if (shdr == NULL)
	return NULL;
      edta = new Elf_Data;
      data[sec] = edta;
      if ((shdr->sh_flags & SHF_SUNW_ABSENT) != 0)
	{
	  char *sname = get_sec_name (sec);
	  for (int i = 0, sz = VecSize(ancillary_files); i < sz; i++)
	    {
	      Elf *ancElf = ancillary_files->fetch (i);
	      int secNum = sec;
	      if (dbe_strcmp (sname, ancElf->get_sec_name (sec)) != 0)
		{
		  append_msg (CMSG_WARN,
			      "Warning: the section #%d (%s) is mismatch in ancillary file '%s')\n",
			      sec, STR (sname), STR (ancElf->fname));
		  secNum = ancElf->elf_get_sec_num (sname);
		}
	      if (secNum > 0)
		{
		  Elf_Data *ed = ancElf->elf_getdata (secNum);
		  if (ed && ed->d_buf)
		    {
		      *edta = *ed;
		      edta->d_flags |= SHF_SUNW_ABSENT;
		      return edta;
		    }
		}
	    }
	}
      edta->d_buf = get_data (shdr->sh_offset, (size_t) shdr->sh_size, NULL);
      edta->d_flags = shdr->sh_flags;
      edta->d_size = ((edta->d_buf == NULL) || (shdr->sh_type == SHT_NOBITS)) ? 0 : shdr->sh_size;
      edta->d_off = shdr->sh_offset;
      edta->d_align = shdr->sh_addralign;
    }
  return edta;
}

int64_t
Elf::elf_checksum ()
{
  if (ehdrp == NULL)
    return 0;
  int64_t chk = 0;
  for (unsigned int ndx = 0; ndx < ehdrp->e_phnum; ndx++)
    {
      Elf_Internal_Phdr *phdr = get_phdr (ndx);
      if (phdr == NULL)
	continue;
      if (phdr->p_type == PT_DYNAMIC)
	{
	  Elf64_Dyn edyn;
	  for (unsigned int i = 0; elf_getdyn (phdr, i, &edyn) != NULL; i++)
	    {
	      if (!edyn.d_tag)
		break;
	      if (edyn.d_tag == DT_CHECKSUM)
		{
		  chk = edyn.d_un.d_val;
		  break;
		}
	    }
	}
    }
  return normalize_checksum (chk);
}

uint64_t
Elf::get_baseAddr ()
{
  uint64_t addr = 0;
  for (unsigned int pnum = 0; pnum < elf_getehdr ()->e_phnum; pnum++)
    {
      Elf_Internal_Phdr *phdr = get_phdr (pnum);
      if (phdr->p_type == PT_LOAD && phdr->p_flags == (PF_R | PF_X))
	{
	  if (addr == 0)
	    addr = phdr->p_vaddr;
	  else
	    {
	      addr = 0;
	      break;
	    }
	}
    }
  return addr;
}

char *
Elf::elf_strptr (unsigned int sec, uint64_t off)
{
  Elf_Data *edta = elf_getdata (sec);
  if (edta && edta->d_buf && edta->d_size > off)
    return ((char *) edta->d_buf) + off;
  return NULL;
}

Elf_Internal_Sym *
Elf::elf_getsym (Elf_Data *edta, unsigned int ndx, Elf_Internal_Sym *dst)
{
  if (dst == NULL || edta == NULL)
    return NULL;
  if (elf_getclass () == ELFCLASS32)
    {
      if (edta->d_size <= ndx * sizeof (Elf32_Sym))
	return NULL;
      Elf32_Sym *hdr = (Elf32_Sym*) bind (edta->d_off + ndx * sizeof (Elf32_Sym), sizeof (Elf32_Sym));
      if (hdr == NULL)
	return NULL;
      dst->st_name = decode (hdr->st_name);
      dst->st_value = decode (hdr->st_value);
      dst->st_size = decode (hdr->st_size);
      dst->st_info = ELF64_ST_INFO (ELF32_ST_BIND (decode (hdr->st_info)),
				    ELF32_ST_TYPE (decode (hdr->st_info)));
      dst->st_other = decode (hdr->st_other);
      dst->st_shndx = decode (hdr->st_shndx);
    }
  else
    {
      if (edta->d_size <= ndx * sizeof (Elf64_Sym))
	return NULL;
      Elf64_Sym *hdr = (Elf64_Sym*) bind (edta->d_off + ndx * sizeof (Elf64_Sym),
					  sizeof (Elf64_Sym));
      if (hdr == NULL)
	return NULL;
      dst->st_name = decode (hdr->st_name);
      dst->st_value = decode (hdr->st_value);
      dst->st_size = decode (hdr->st_size);
      dst->st_info = decode (hdr->st_info);
      dst->st_other = decode (hdr->st_other);
      dst->st_shndx = decode (hdr->st_shndx);
    }
  return dst;
}

Elf_Internal_Rela *
Elf::elf_getrel (Elf_Data *edta, unsigned int ndx, Elf_Internal_Rela *dst)
{
  if (dst == NULL || edta == NULL || edta->d_buf == NULL)
    return NULL;
  if (elf_getclass () == ELFCLASS32)
    {
      Elf32_Rel *rel = ((Elf32_Rel *) edta->d_buf) + ndx;
      dst->r_offset = decode (rel->r_offset);
      dst->r_info = ELF64_R_INFO (ELF32_R_SYM (decode (rel->r_info)),
				  ELF32_R_TYPE (decode (rel->r_info)));
    }
  else
    {
      Elf64_Rel *rel = ((Elf64_Rel *) edta->d_buf) + ndx;
      dst->r_offset = decode (rel->r_offset);
      dst->r_info = decode (rel->r_info);
    }
  return dst;
}

Elf_Internal_Rela *
Elf::elf_getrela (Elf_Data *edta, unsigned int ndx, Elf_Internal_Rela *dst)
{
  if (dst == NULL || edta == NULL || edta->d_buf == NULL)
    return NULL;
  if (elf_getclass () == ELFCLASS32)
    {
      Elf32_Rela *rela = ((Elf32_Rela *) edta->d_buf) + ndx;
      dst->r_offset = decode (rela->r_offset);
      dst->r_addend = decode (rela->r_addend);
      dst->r_info = ELF64_R_INFO (ELF32_R_SYM (decode (rela->r_info)),
				  ELF32_R_TYPE (decode (rela->r_info)));
    }
  else
    {
      Elf64_Rela *rela = ((Elf64_Rela *) edta->d_buf) + ndx;
      dst->r_offset = decode (rela->r_offset);
      dst->r_addend = decode (rela->r_addend);
      dst->r_info = decode (rela->r_info);
    }
  return dst;
}

Elf64_Ancillary *
Elf::elf_getancillary (Elf_Data *edta, unsigned int ndx, Elf64_Ancillary *dst)
{
  if (dst == NULL || edta == NULL || edta->d_buf == NULL)
    return NULL;
  if (elf_getclass () == ELFCLASS32)
    {
      Elf32_Ancillary *p = ((Elf32_Ancillary *) edta->d_buf) + ndx;
      dst->a_tag = decode (p->a_tag);
      dst->a_un.a_val = decode (p->a_un.a_val);
    }
  else
    {
      Elf64_Ancillary *p = ((Elf64_Ancillary *) edta->d_buf) + ndx;
      dst->a_tag = decode (p->a_tag);
      dst->a_un.a_val = decode (p->a_un.a_val);
    }
  return dst;
}

Elf *
Elf::get_related_file (const char *lo_name, const char *nm)
{
  DbeFile *df;
  if (*nm == '/')
    {
      df = new DbeFile (nm);
      df->filetype |= (DbeFile::F_FILE | DbeFile::F_DEBUG_FILE);
    }
  else
    {
      char *bname = get_basename (lo_name);
      char *fnm = dbe_sprintf ("%.*s/%s", (int) (bname - lo_name), lo_name, nm);
      df = new DbeFile (fnm);
      df->filetype |= (DbeFile::F_FILE | DbeFile::F_DEBUG_FILE);
      free (fnm);
    }
  Dprintf (DEBUG_STABS, "get_related_file: %s  -> '%s'\n", nm, df->get_name ());
  Elf_status st = ELF_ERR_CANT_OPEN_FILE;
  Elf *elf = elf_begin (df->get_location (), &st);
  if (elf)
    {
      elf->dbeFile = df;
      return elf;
    }
  switch (st)
    {
    case ELF_ERR_CANT_OPEN_FILE:
      append_msg (CMSG_ERROR, GTXT ("Cannot open file `%s'"), df->get_name ());
      break;
    case ELF_ERR_BAD_ELF_FORMAT:
    default:
      append_msg (CMSG_ERROR, GTXT ("Cannot read ELF header of `%s'"),
		  df->get_name ());
      break;
    }
  delete df;
  return NULL;
}

Elf *
Elf::find_ancillary_files (char *lo_name)
{
  // read the .gnu_debuglink and .SUNW_ancillary seections
  if (gnu_debug_file)
    return gnu_debug_file;
  unsigned int sec = elf_get_sec_num (NTXT (".gnu_debuglink"));
  if (sec > 0)
    {
      Elf_Data *dp = elf_getdata (sec);
      if (dp)
	{
	  gnu_debug_file = get_related_file (lo_name, (char *) (dp->d_buf));
	  if (gnu_debug_file)
	    return gnu_debug_file;
	}
    }

  sec = elf_get_sec_num (NTXT (".SUNW_ancillary"));
  if (sec > 0)
    {
      Elf_Internal_Shdr *shdr = get_shdr (sec);
      uint64_t check_sum = 0;
      char *ancName = NULL;
      if (shdr)
	{
	  Elf_Data *dp = elf_getdata (sec);
	  for (int i = 0, sz = (int) (shdr->sh_size / shdr->sh_entsize);
		  i < sz; i++)
	    {
	      Elf64_Ancillary anc;
	      if (elf_getancillary (dp, i, &anc) == NULL
		  || anc.a_tag == ANC_SUNW_NULL)
		break;
	      if (anc.a_tag == ANC_SUNW_MEMBER)
		ancName = elf_strptr (shdr->sh_link, anc.a_un.a_ptr);
	      else if (anc.a_tag == ANC_SUNW_CHECKSUM)
		{
		  if (i == 0)
		    {
		      check_sum = anc.a_un.a_val;
		      continue;
		    }
		  if (check_sum == anc.a_un.a_val)
		    ancName = NULL;
		  if (ancName)
		    {
		      Elf *ancElf = get_related_file (lo_name, ancName);
		      if (ancElf == NULL)
			continue;
		      int ancSec = ancElf->elf_get_sec_num (".SUNW_ancillary");
		      if (ancSec > 0)
			{
			  Elf_Internal_Shdr *ancHdr = ancElf->get_shdr (ancSec);
			  if (ancHdr)
			    {
			      Elf_Data *anc_dp = ancElf->elf_getdata (ancSec);
			      Elf64_Ancillary anc1;
			      if (ancElf->elf_getancillary (anc_dp, 0, &anc1)
				  && (anc1.a_tag == ANC_SUNW_CHECKSUM) &&
				  anc1.a_un.a_val == anc.a_un.a_val)
				{
				  if (ancillary_files == NULL)
				    ancillary_files = new Vector<Elf*>(2);
				  ancillary_files->append (ancElf);
				}
			      else
				append_msg (CMSG_WARN, GTXT ("Load Object: '%s' (checksum Ox%lld). The .anc file '%s' has checksum Ox%llx"),
					    STR (fname), (long long) check_sum,
					    STR (ancElf->dbeFile->get_location ()),
					    (long long) anc1.a_un.a_val);
			    }
			}
		      ancName = NULL;
		    }
		}
	    }
	}
    }
  return NULL;
}

char*
Elf::get_location ()
{
  return dbeFile ? dbeFile->get_location () : fname;
}

#define RET_S(x)   if (t == x) return (char *) #x

static char *
get_elf_class_name (int t)
{
  RET_S (ELFCLASSNONE);
  RET_S (ELFCLASS32);
  RET_S (ELFCLASS64);
  return NTXT ("ELFCLASS_UNKNOWN");
}

static char *
get_elf_data_name (int t)
{
  RET_S (ELFDATANONE);
  RET_S (ELFDATA2LSB);
  RET_S (ELFDATA2MSB);
  return NTXT ("ELFDATA_UNKNOWN");
}

static char *
get_elf_osabi_name (int t)
{
  RET_S (ELFOSABI_NONE);
  RET_S (ELFOSABI_HPUX);
  RET_S (ELFOSABI_NETBSD);
  RET_S (ELFOSABI_LINUX);
  RET_S (ELFOSABI_SOLARIS);
  RET_S (ELFOSABI_AIX);
  RET_S (ELFOSABI_IRIX);
  RET_S (ELFOSABI_FREEBSD);
  RET_S (ELFOSABI_TRU64);
  RET_S (ELFOSABI_MODESTO);
  RET_S (ELFOSABI_OPENBSD);
  return NTXT ("ELFOSABI_UNKNOWN");
}

static char *
get_elf_etype_name (int t)
{
  RET_S (ET_NONE);
  RET_S (ET_REL);
  RET_S (ET_EXEC);
  RET_S (ET_DYN);
  RET_S (ET_CORE);
  RET_S (ET_LOPROC);
  RET_S (ET_HIPROC);
  return NTXT ("ETYPE_UNKNOWN");
}

static char *
get_elf_ptype_name (int t)
{
  RET_S (PT_NULL);
  RET_S (PT_LOAD);
  RET_S (PT_DYNAMIC);
  RET_S (PT_INTERP);
  RET_S (PT_NOTE);
  RET_S (PT_SHLIB);
  RET_S (PT_PHDR);
  RET_S (PT_TLS);
  RET_S (PT_LOOS);
  RET_S (PT_GNU_EH_FRAME);
  RET_S (PT_GNU_EH_FRAME);
  RET_S (PT_HIOS);
  RET_S (PT_LOPROC);
  RET_S (PT_HIPROC);
  return NTXT ("PTYPE_UNKNOWN");
}

static char *
get_elf_shtype_name (unsigned int t)
{
  RET_S (SHT_NULL);
  RET_S (SHT_PROGBITS);
  RET_S (SHT_SYMTAB);
  RET_S (SHT_STRTAB);
  RET_S (SHT_RELA);
  RET_S (SHT_HASH);
  RET_S (SHT_DYNAMIC);
  RET_S (SHT_NOTE);
  RET_S (SHT_NOBITS);
  RET_S (SHT_REL);
  RET_S (SHT_SHLIB);
  RET_S (SHT_DYNSYM);
  RET_S (SHT_INIT_ARRAY);
  RET_S (SHT_FINI_ARRAY);
  RET_S (SHT_PREINIT_ARRAY);
  RET_S (SHT_GROUP);
  RET_S (SHT_SYMTAB_SHNDX);
  RET_S (SHT_LOOS);
  RET_S (SHT_SUNW_verdef);
  RET_S (SHT_SUNW_verneed);
  RET_S (SHT_HIOS);
  RET_S (SHT_LOPROC);
  RET_S (SHT_HIPROC);
  RET_S (SHT_LOUSER);
  RET_S (SHT_HIUSER);
  return NTXT ("SHTYPE_UNKNOWN");
}

static char *
get_elf_machine_name (int t)
{
  RET_S (EM_NONE);
  RET_S (EM_M32);
  RET_S (EM_SPARC);
  RET_S (EM_386);
  RET_S (EM_68K);
  RET_S (EM_88K);
  RET_S (EM_860);
  RET_S (EM_MIPS);
  RET_S (EM_S370);
  RET_S (EM_MIPS_RS3_LE);
  RET_S (EM_SPARC32PLUS);
  RET_S (EM_960);
  RET_S (EM_PPC);
  RET_S (EM_PPC64);
  RET_S (EM_V800);
  RET_S (EM_FR20);
  RET_S (EM_RH32);
  RET_S (EM_RCE);
  RET_S (EM_ARM);
  RET_S (EM_ALPHA);
  RET_S (EM_SH);
  RET_S (EM_SPARCV9);
  RET_S (EM_TRICORE);
  RET_S (EM_ARC);
  RET_S (EM_H8_300);
  RET_S (EM_H8_300H);
  RET_S (EM_H8S);
  RET_S (EM_H8_500);
  RET_S (EM_IA_64);
  RET_S (EM_MIPS_X);
  RET_S (EM_COLDFIRE);
  RET_S (EM_68HC12);
  RET_S (EM_MMA);
  RET_S (EM_PCP);
  RET_S (EM_NCPU);
  RET_S (EM_NDR1);
  RET_S (EM_STARCORE);
  RET_S (EM_ME16);
  RET_S (EM_ST100);
  RET_S (EM_TINYJ);
  RET_S (EM_X86_64);
  RET_S (EM_PDSP);
  RET_S (EM_FX66);
  RET_S (EM_ST9PLUS);
  RET_S (EM_ST7);
  RET_S (EM_68HC16);
  RET_S (EM_68HC11);
  RET_S (EM_68HC08);
  RET_S (EM_68HC05);
  RET_S (EM_SVX);
  RET_S (EM_ST19);
  RET_S (EM_VAX);
  RET_S (EM_CRIS);
  RET_S (EM_JAVELIN);
  RET_S (EM_FIREPATH);
  RET_S (EM_ZSP);
  RET_S (EM_MMIX);
  RET_S (EM_HUANY);
  RET_S (EM_PRISM);
  RET_S (EM_AVR);
  RET_S (EM_FR30);
  RET_S (EM_D10V);
  RET_S (EM_D30V);
  RET_S (EM_V850);
  RET_S (EM_M32R);
  RET_S (EM_MN10300);
  RET_S (EM_MN10200);
  RET_S (EM_PJ);
  RET_S (EM_OPENRISC);
  RET_S (EM_XTENSA);
  return NTXT ("ELFMACHINE_UNKNOWN");
}

static char *
get_elf_version_name (int t)
{
  RET_S (EV_NONE);
  RET_S (EV_CURRENT);
  return NTXT ("VERSION_UNKNOWN");
}

static char *
get_elf_ancillary_tag (int t)
{
  RET_S (ANC_SUNW_NULL);
  RET_S (ANC_SUNW_CHECKSUM);
  RET_S (ANC_SUNW_MEMBER);
  RET_S (ANC_SUNW_NUM);
  return NTXT ("ANCILLARY_TAG_UNKNOWN");
}

#define ADD_S(x)    if ((f & (x)) == (x)) { sb->append(' '); sb->append(#x); f &= ~(x); }

static void
dump_sh_flags (StringBuilder *sb, long long flags)
{
  long long f = flags;
  if (f != 0)
    {
      sb->append (NTXT (" ["));
      ADD_S (SHF_WRITE)
      ADD_S (SHF_ALLOC)
      ADD_S (SHF_EXECINSTR)
      ADD_S (SHF_MERGE)
      ADD_S (SHF_STRINGS)
      ADD_S (SHF_INFO_LINK)
      ADD_S (SHF_LINK_ORDER)
      ADD_S (SHF_OS_NONCONFORMING)
      ADD_S (SHF_GROUP)
      ADD_S (SHF_TLS)
      ADD_S (SHF_SUNW_ABSENT)
      ADD_S (SHF_EXCLUDE)
      if (f != 0 && f != flags)
	sb->appendf (NTXT (" 0x%llx"), (long long) f);
      sb->append (NTXT (" ]"));
    }
  sb->append (NTXT ("\n"));
}

static void
dump_p_flags (StringBuilder *sb, long long flags)
{
  long long f = flags;
  if (f != 0)
    {
      sb->append (NTXT (" ["));
      ADD_S (PF_X)
      ADD_S (PF_W)
      ADD_S (PF_R)
      ADD_S (PF_MASKPROC)
      if (f != 0 && f != flags)
	sb->appendf (NTXT (" 0x%llx"), (long long) f);
      sb->append (NTXT (" ]"));
    }
  sb->append (NTXT ("\n"));
}

char *
Elf::dump ()
{
  StringBuilder sb;
  sb.sprintf (NTXT ("ELF Header: %s\n"), fname ? fname : GTXT ("(unknown)"));
  if (ehdrp == NULL)
    {
      sb.appendf (GTXT ("\n\n Cannot read Elf header\n"));
      return sb.toString ();
    }
  sb.appendf (NTXT ("  %-15s "), NTXT ("e_ident"));
  for (int i = 0; i < EI_NIDENT; i++)
    sb.appendf (NTXT ("%x"), ehdrp->e_ident[i]);
  sb.append (NTXT ("\n"));
  char *fmt0 = NTXT ("  %-15s %10lld ( %s )\n");
  char *fmt1 = NTXT ("  %-15s 0x%08llx ( %lld )\n");
  char *fmt2 = NTXT ("  %-15s 0x%08llx");
  sb.appendf (fmt0, NTXT ("EI_CLASS"), (long long) ehdrp->e_ident[EI_CLASS],
	      get_elf_class_name (ehdrp->e_ident[EI_CLASS]));
  sb.appendf (fmt0, NTXT ("EI_DATA"), (long long) ehdrp->e_ident[EI_DATA],
	      get_elf_data_name (ehdrp->e_ident[EI_DATA]));
  sb.appendf (fmt0, NTXT ("EI_OSABI"), (long long) ehdrp->e_ident[EI_OSABI],
	      get_elf_osabi_name (ehdrp->e_ident[EI_OSABI]));
  sb.appendf (fmt0, NTXT ("e_type"), (long long) ehdrp->e_type,
	      get_elf_etype_name (ehdrp->e_type));
  sb.appendf (fmt0, NTXT ("e_machine"), (long long) ehdrp->e_machine,
	      get_elf_machine_name (ehdrp->e_machine));
  sb.appendf (fmt0, NTXT ("e_version"), (long long) ehdrp->e_version,
	      get_elf_version_name (ehdrp->e_version));
  sb.appendf (fmt1, NTXT ("e_entry"), (long long) ehdrp->e_entry,
	      (long long) ehdrp->e_entry);
  sb.appendf (fmt1, NTXT ("e_phoff"), (long long) ehdrp->e_phoff,
	      (long long) ehdrp->e_phoff);
  sb.appendf (fmt1, NTXT ("e_shoff"), (long long) ehdrp->e_shoff,
	      (long long) ehdrp->e_shoff);
  sb.appendf (fmt1, NTXT ("e_flags"), (long long) ehdrp->e_flags,
	      (long long) ehdrp->e_flags);
  sb.appendf (fmt1, NTXT ("e_ehsize"), (long long) ehdrp->e_ehsize,
	      (long long) ehdrp->e_ehsize);
  sb.appendf (fmt1, NTXT ("e_phentsize"), (long long) ehdrp->e_phentsize,
	      (long long) ehdrp->e_phentsize);
  sb.appendf (fmt1, NTXT ("e_phnum"), (long long) ehdrp->e_phnum,
	      (long long) ehdrp->e_phnum);
  sb.appendf (fmt1, NTXT ("e_shentsize"), (long long) ehdrp->e_shentsize,
	      (long long) ehdrp->e_shentsize);
  sb.appendf (fmt1, NTXT ("e_shnum"), (long long) ehdrp->e_shnum,
	      (long long) ehdrp->e_shnum);
  sb.appendf (fmt1, NTXT ("e_shstrndx"), (long long) ehdrp->e_shstrndx,
	      (long long) ehdrp->e_shstrndx);

  for (unsigned int i = 0; i < ehdrp->e_phnum; i++)
    {
      sb.appendf (NTXT ("\nProgram Header[%d]:\n"), i);
      Elf_Internal_Phdr *phdr = get_phdr (i);
      if (phdr == NULL)
	{
	  sb.appendf (NTXT ("      ERROR: get_phdr(%d) failed\n"), i);
	  continue;
	}
      sb.appendf (fmt0, "p_type", (long long) phdr->p_type,
		  get_elf_ptype_name (phdr->p_type));
      sb.appendf (fmt2, "p_flags", (long long) phdr->p_flags);
      dump_p_flags (&sb, phdr->p_flags);
      sb.appendf (fmt1, "p_offset", (long long) phdr->p_offset,
		  (long long) phdr->p_offset);
      sb.appendf (fmt1, "p_vaddr", (long long) phdr->p_vaddr,
		  (long long) phdr->p_vaddr);
      sb.appendf (fmt1, "p_paddr", (long long) phdr->p_paddr,
		  (long long) phdr->p_paddr);
      sb.appendf (fmt1, "p_filesz", (long long) phdr->p_filesz,
		  (long long) phdr->p_filesz);
      sb.appendf (fmt1, "p_memsz", (long long) phdr->p_memsz,
		  (long long) phdr->p_memsz);
      sb.appendf (fmt1, "p_align", (long long) phdr->p_align,
		  (long long) phdr->p_align);
    }

  for (unsigned int i = 1; i < ehdrp->e_shnum; i++)
    {
      sb.appendf (NTXT ("\nSection Header[%d]:\n"), i);
      Elf_Internal_Shdr *shdr = get_shdr (i);
      if (shdr == NULL)
	{
	  sb.appendf (NTXT ("      ERROR: get_shdr(%d) failed\n"), i);
	  continue;
	}
      char *s = get_sec_name (i);
      sb.appendf (fmt0, "sh_name", (long long) shdr->sh_name,
		  s ? s : NTXT ("NULL"));
      sb.appendf (fmt0, "sh_type", (long long) shdr->sh_type,
		  get_elf_shtype_name (shdr->sh_type));
      sb.appendf (fmt2, "sh_flags", (long long) shdr->sh_flags);
      dump_sh_flags (&sb, shdr->sh_flags);
      sb.appendf (fmt1, "sh_addr", (long long) shdr->sh_addr,
		  (long long) shdr->sh_addr);
      sb.appendf (fmt1, "sh_offset", (long long) shdr->sh_offset,
		  (long long) shdr->sh_offset);
      sb.appendf (fmt1, "sh_size", (long long) shdr->sh_size,
		  (long long) shdr->sh_size);
      sb.appendf (fmt1, "sh_link", (long long) shdr->sh_link,
		  (long long) shdr->sh_link);
      sb.appendf (fmt1, "sh_info", (long long) shdr->sh_info,
		  (long long) shdr->sh_info);
      sb.appendf (fmt1, "sh_addralign", (long long) shdr->sh_addralign,
		  (long long) shdr->sh_addralign);
      sb.appendf (fmt1, "sh_entsize", (long long) shdr->sh_entsize,
		  (long long) shdr->sh_entsize);
    }

  for (unsigned int i = 1; i < ehdrp->e_shnum; i++)
    {
      Elf_Internal_Shdr *shdr = get_shdr (i);
      if (shdr == NULL)
	continue;
      char *secName = get_sec_name (i);
      if (secName == NULL)
	continue;
      if (strcmp (NTXT (".SUNW_ancillary"), secName) == 0)
	{
	  sb.appendf (NTXT ("\nSection[%d]:  %s\n"), i, secName);
	  Elf_Data *dp = elf_getdata (i);
	  for (int j = 0, cnt = (int) (shdr->sh_size / shdr->sh_entsize);
		  j < cnt; j++)
	    {
	      Elf64_Ancillary anc;
	      if (elf_getancillary (dp, j, &anc) == NULL)
		break;
	      sb.appendf (NTXT ("%10d  %-20s 0x%08llx %6lld"), j,
			  get_elf_ancillary_tag ((int) anc.a_tag),
			  (long long) anc.a_un.a_ptr, (long long) anc.a_un.a_ptr);
	      if (anc.a_tag == ANC_SUNW_MEMBER)
		sb.appendf (NTXT ("  %s\n"), STR (elf_strptr (shdr->sh_link, anc.a_un.a_ptr)));
	      else
		sb.append (NTXT ("\n"));
	    }
	}
    }
  return sb.toString ();
}

void
Elf::dump_elf_sec ()
{
  if (!DUMP_ELF_SEC)
    return;
  if (ehdrp == NULL)
    return;
  Dprintf (DUMP_ELF_SEC, "======= DwarfLib::dump_elf_sec\n"
	   " N |type|flags|  sh_addr | sh_offset | sh_size | sh_link |"
	   " sh_info | sh_addralign | sh_entsize | sh_name | name\n");
  for (unsigned int sec = 1; sec < ehdrp->e_shnum; sec++)
    {
      Elf_Internal_Shdr *shdr = get_shdr (sec);
      if (shdr == NULL)
	continue;
      char *name = elf_strptr (ehdrp->e_shstrndx, shdr->sh_name);
      Dprintf (DUMP_ELF_SEC, "%3d:%3d |%4d |%9lld | %9lld |%8lld |%8lld |"
	       "%8lld |%14d |%11lld | %6lld %s\n",
	       sec, (int) shdr->sh_type, (int) shdr->sh_flags,
	       (long long) shdr->sh_addr, (long long) shdr->sh_offset,
	       (long long) shdr->sh_size, (long long) shdr->sh_link,
	       (long long) shdr->sh_info,
	       (int) shdr->sh_addralign, (long long) shdr->sh_entsize,
	       (long long) shdr->sh_name, name ? name : NTXT ("NULL"));
    }
  Dprintf (DUMP_ELF_SEC, NTXT ("\n"));
}
