/*!================================================================

  module:       vpi01GetModuleName.h

 -------------------------------------------------------------------

  responsible:  ThomasS

  special area: Gets module name of executable

  description:                 
                Gets the module name without use of argc, argv
		on Win and UNIX (stolen from veo670.c).


  see also:

  -------------------------------------------------------------------------





    ========== licence begin  GPL
    Copyright (c) 1998-2005 SAP AG

    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 2
    of the License, 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, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
    ========== licence end




 ===================================================================*/

/*==================================================================*
 *  INCLUDES                                                        *
 *==================================================================*/

#undef _FILE_OFFSET_BITS

#include <stdio.h>

#include "vpi01GetModuleName.h"
#if defined(_WIN32)
#include <windows.h>

#else

#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <signal.h>

#ifdef LINUX
#include <sys/types.h>
#include <fcntl.h>
#include <elf.h>
#include <sys/file.h>
#include <sys/procfs.h>
#undef __USE_FILE_OFFSET64
#define __USE_LARGEFILE64
#include <sys/mman.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <linux/version.h>
#endif

#ifdef SUN
#include <sys/types.h>
#include <fcntl.h>
#include <libelf.h>
#include <sys/frame.h>
#include <ucontext.h>
#ifdef BIT64
#include <sys/stack.h>
#endif /* BIT64 */
#include <sys/file.h>
#include <procfs.h> /* this is the new procfs interface, not based on ioctl!!!! */
#include <sys/mman.h>
#include <sys/param.h>
#include <sys/stat.h>
#endif /* SUN */

#ifdef HPUX
#include <dl.h>
#include <fcntl.h>
#ifdef BIT64
#include <elf.h>
#else
#include <a.out.h>
#endif
#endif /* HPUX */

#if defined(OSF1)
#include <loader.h>
#include <excpt.h>
#include <ldfcn.h>
#endif /* OSF1 */

#ifdef AIX
#include <sys/debug.h>
#include <sys/ldr.h>
#endif /* AIX */

#endif

/*==================================================================*
 *  DEFINES                                                         *
 *==================================================================*/

#if defined(_WIN32) || defined(_WIN64)
#   if defined (_INTEGRAL_MAX_BITS) && _INTEGRAL_MAX_BITS >= 64
        typedef unsigned __int64    SAPDB_UInt8;     /* integer (8 byte, unsigned) */
        typedef signed __int64      SAPDB_Int8;      /* integer (8 byte, signed)   */
#   else
#       error __int64 type not supported
#   endif
#else
#   if defined (BIT64)
        typedef unsigned long       SAPDB_UInt8;     /* integer (8 byte, unsigned) */
        typedef signed long         SAPDB_Int8;      /* integer (8 byte, signed)   */
#   else
        typedef unsigned long long  SAPDB_UInt8;     /* integer (8 byte, unsigned) */
        typedef signed long long    SAPDB_Int8;      /* integer (8 byte, signed)   */
#   endif
#endif /* UNIX(tm) tested on AIX,DEC,LINUX,HP,SNI and SUN ;-) */

#  if defined(OSF1)
struct modul_chain_elem
{
    ldr_module_info_t	      moduleInfo;
    ldr_region_info_t	      textInfo;
    elf32_LDFILE	         *ldptr;
    int			opened;		/* opened or at at least tried to open */
    int			symtab_ok;
};
typedef struct modul_chain_elem mce;
typedef	struct modul_chain_elem	*mce_p;
#endif

#define SIZE_OF_MESSAGE_BUFFER_VPI01 (4096)
#define MAX_SYMBOL_LENGTH 4096

#if defined(SUN) || defined(LINUX) || (defined(HPUX) && defined(BIT64))
#  define HAS_ELF_SUPPORT
#else
#  undef  HAS_ELF_SUPPORT
#endif

#ifdef HAS_ELF_SUPPORT
#ifdef BIT64
typedef Elf64_Sym  Elf_Symbol;
typedef Elf64_Ehdr Elf_ElfHeader;
typedef Elf64_Shdr Elf_SectionHeader;
typedef Elf64_Word Elf_Word;
typedef Elf64_Addr Elf_Addr;
#define ELF_ST_TYPE(_i) ELF64_ST_TYPE(_i)
#else
typedef Elf32_Sym  Elf_Symbol;
typedef Elf32_Ehdr Elf_ElfHeader;
typedef Elf32_Shdr Elf_SectionHeader;
typedef Elf32_Word Elf_Word;
typedef Elf32_Addr Elf_Addr;
#define ELF_ST_TYPE(_i) ELF32_ST_TYPE(_i)
#endif /* BIT64 */
#endif /* HAS_ELF_SUPPORT */

/*==================================================================*
 *  DECLARATIONS                                                    *
 *==================================================================*/

static char vpi01MessageBuffer[SIZE_OF_MESSAGE_BUFFER_VPI01];

/*==================================================================*
 *  CODE                                                            *
 *==================================================================*/

#ifdef HAS_ELF_SUPPORT

/*
  Function: vpi01ReadElfSymbol

  Helper routine, that reads a symbol. Symbol is overwritten with each call
*/
static int vpi01ReadElfSymbol (int fd, Elf_Word stringTabOffset, Elf_Word entry, char **pSymbol,
			       FILE *pi01TraceFile)
{
  static char lastSymbolBuffer[MAX_SYMBOL_LENGTH];

  *pSymbol = (char *) "";
  if (lseek (fd, stringTabOffset + entry, SEEK_SET) != stringTabOffset + entry) {
    fprintf (pi01TraceFile, "Failed to seek to symbol string offset\n");
    return 0;
  }
  if (read (fd, lastSymbolBuffer, sizeof(lastSymbolBuffer)-1) <= 0) {
    fprintf (pi01TraceFile, "Failed to read symbol from file\n");
    return 0;
  }
  *pSymbol = lastSymbolBuffer;
  return 1;
}

#endif

#ifdef WIN32

char *vpi01ModuleName (FILE *pi01TraceFile)
{
  char *szCmdLine;
  char *p = NULL;

  szCmdLine = GetCommandLine ();
  if ((p = strchr (&szCmdLine[1], '"')) != NULL) {
    *p = '\0'; 
    p = &szCmdLine[1];
  } else if ((p = strchr (szCmdLine, ' ')) != NULL) {
    *p = '\0';
    p = szCmdLine;
  } else {
    p = szCmdLine;
  }

  return p;
}

#endif

#if defined(SUN) || defined(LINUX)

#ifndef HPUX
#    define NOHEAP_MALLOC noheap_malloc
#    define NOHEAP_CALLOC noheap_calloc
#    define NOHEAP_FREE noheap_free
#    define USRTEXT  0x2000
#endif /* HPUX */

static void *
noheap_malloc(size_t size)
{
  static int fd_dev_zero;
  static caddr_t ra;

    if ((fd_dev_zero = open("/dev/zero", O_RDWR, 0)) == -1) {
        perror("opening /dev/zero");
        return (NULL);
    }

    if (size == 0) size = sizeof (int);

    if ((ra = (caddr_t)mmap((caddr_t) 0, size + sizeof(caddr_t), 
                   (PROT_READ | PROT_WRITE), MAP_PRIVATE,
                   fd_dev_zero, 0)) == (caddr_t)-1) {
        perror("mmap'ing /dev/zero");
        ra = NULL;
    } else {
        *((size_t *)ra) = size;
        ra += sizeof(size_t);
    }

    close(fd_dev_zero);
    return((void *)ra);
}

static void *
noheap_calloc(size_t nelem, size_t elsize)
{
    /* While mmap of /dev/zero is being used, the block               */
    /* should come back already zero filled.                          */
    return(noheap_malloc((nelem * elsize)));
}

/*---------------------------------------------------------------------------*/

static void
noheap_free(void *ptr)
{
  static size_t len;
  static caddr_t real_ptr;

    if (ptr == NULL) return;

    real_ptr = (caddr_t)(((size_t *)ptr) - 1);
    len = *((size_t *)real_ptr) + sizeof(caddr_t);
    if (munmap(real_ptr, len))
        perror("munmap");
}

#endif

#ifdef LINUX

#define PROC_MMAP_FILE_OFFSET     49
#define PROC_MMAP_ACCESS_OFFSET   18
#define PROC_MMAP_VADDR_OFFSET    0
#define PROC_MMAP_VENDADDR_OFFSET 9

static void PrepareProcMap(char *procMap, char *endProcMap)
{
  static char *p;
  
  p = procMap;
  while (p < endProcMap) {
    if (*p == '\n') *p = 0;
    ++p;
  }
}

static char *NextProcMap (char *procMap, size_t *pBufferedChars, int proc_fd, 
			  FILE *pi01TraceFile)
{
  static char *p;
  static size_t bytesStored;
  static int    bytesRead;
  
  bytesStored = *pBufferedChars;
  
  p = procMap + strlen(procMap);
  while ( *p == 0 && p < (procMap + bytesStored) ) {
    ++p;
  }
  bytesStored -= (p-procMap);
  
  memcpy (procMap, p, bytesStored);
  bytesRead = read (proc_fd, procMap + bytesStored, 1024 - bytesStored );
  if (bytesRead < 0) {
    fprintf (pi01TraceFile, "cant read from /proc file\n");
    return ((char *) "");
  }
  PrepareProcMap (procMap + bytesStored, procMap + bytesStored + bytesRead);
  
  *pBufferedChars = bytesStored + bytesRead;
  
  return (*pBufferedChars > 0 ? procMap : NULL);
}

char *vpi01ModuleName (FILE *pi01TraceFile)
{
  char *p = NULL;
  static int       fd_proc;	/* /proc fd                           */
  static char      procMapBuffer[1024];
  static char     *procMap;
  static size_t    bufferedChars; /* Number of currently buffered character */
  static caddr_t   vaddr;
  static caddr_t   vendaddr;

  sprintf (vpi01MessageBuffer, "/proc/%d/maps", getpid ());

  if ((fd_proc = open (vpi01MessageBuffer, O_RDONLY)) == -1) {
    sprintf (vpi01MessageBuffer,
	    "Cant open /proc/%d/maps : %d\n", getpid (), errno);
    fprintf (pi01TraceFile, vpi01MessageBuffer);
    return ((char *) "");
  }

  if ((bufferedChars = read (fd_proc, procMapBuffer, sizeof(procMapBuffer))) <= 0 ) {
    sprintf(vpi01MessageBuffer,
	    "Cant read from /proc/%d/maps : %d\n", getpid(), errno);
    fprintf (pi01TraceFile, vpi01MessageBuffer);
    return ((char *) "");
  }

  procMap = &procMapBuffer[0];
  /* Split lines into zero terminated strings */
  PrepareProcMap (procMap, procMap + bufferedChars);

  do {
    if (strlen (procMap) < PROC_MMAP_FILE_OFFSET)
      continue;
    
    if (strncmp (procMap+PROC_MMAP_ACCESS_OFFSET, "r-xp", 4) != 0)
      continue;
    
    vaddr = (caddr_t)
      strtoul (procMap+PROC_MMAP_VADDR_OFFSET, NULL, 16);
    
    vendaddr = (caddr_t)
      strtoul (procMap+PROC_MMAP_VENDADDR_OFFSET, NULL, 16);
    
    p = (char *) procMap + PROC_MMAP_FILE_OFFSET;
    break;
  } while (0 != NextProcMap (procMap, &bufferedChars, fd_proc, pi01TraceFile));
  
  (void) close (fd_proc);
  return p;
}
#endif

#ifdef SUN

static void vpi01SunGetModuleFileName(char *mapName, char **pModuleFileName,
				      FILE *pi01TraceFile)
{
  static  int               fd;
  static  Elf_ElfHeader     elfhdr;           /* Elf header                (read once) */
  static  Elf_SectionHeader symbolSectionHdr; /* Elf symbol section header (read once) */
  static  Elf_SectionHeader elfSectionHdr;    /* temp Elf section header */
  static  Elf_Word          stringSection;
  static  Elf_Word          stringTabOffset;
  static  int               i;
  static  size_t            syms;
  static  Elf_Symbol        symtabEntry;
  static  SAPDB_Int8        systemRc;

  *pModuleFileName = (char *) "????";

  sprintf (vpi01MessageBuffer,
	   "/proc/%d/object/%s", getpid (), mapName);
  fd = open (vpi01MessageBuffer, O_RDONLY);
  if (fd < 0) {
    sprintf(vpi01MessageBuffer,
	    "Cannot open mapfile /proc/%d/object/%s :%d\n", getpid (), mapName, errno);
    fprintf (pi01TraceFile, vpi01MessageBuffer);
    return;
  }
  /* Get the ELF header                                             */

  if ((systemRc = read (fd, &elfhdr, sizeof (elfhdr))) != sizeof (elfhdr)) {
    fprintf (pi01TraceFile, "Unable to read ELF header \n");
    goto done;
  }

  if (elfhdr.e_shnum == 0) {
    fprintf (pi01TraceFile, "No section header entries.\n");
    goto done;
  }

  if (elfhdr.e_shentsize > sizeof (elfSectionHdr)) {
    fprintf (pi01TraceFile, "Elf section header entries larger than expected.\n");
    goto done;
  }

  /* Seek to the section header table                                   */

  if ((systemRc = lseek (fd, (long) elfhdr.e_shoff, SEEK_SET)) != elfhdr.e_shoff) {
    fprintf (pi01TraceFile, "Unable to seek to section header.\n");
    goto done;
  }

  stringSection = 0;
  
  for (i = 0; i < (int) elfhdr.e_shnum; i++) {
    if ((systemRc = read (fd, &elfSectionHdr, elfhdr.e_shentsize)) !=  elfhdr.e_shentsize) {
      fprintf (pi01TraceFile, "Unable to read ELF header section.\n");
      goto done;
    }
    if (elfSectionHdr.sh_type ==  SHT_SYMTAB) {
      memcpy (&symbolSectionHdr, &elfSectionHdr, sizeof (elfSectionHdr));
      stringSection = elfSectionHdr.sh_link;
    }
  }

  if ((0 == stringSection) ||
      (stringSection >= elfhdr.e_shnum)) {
    fprintf (pi01TraceFile, "Warning - no ELF string section defined.\n");
    goto done;
  }
  
  /*Skip section up to wanted string section */
  if ((systemRc = lseek(fd, (long) elfhdr.e_shoff, SEEK_SET)) != elfhdr.e_shoff) {
    fprintf (pi01TraceFile, "Unable to do second seek to ELF section headers.\n");
    goto done;
  }

  for (i = 0; i < stringSection; i++) {
    if ((systemRc = read (fd, &elfSectionHdr, elfhdr.e_shentsize)) !=  elfhdr.e_shentsize) {
      fprintf (pi01TraceFile, "Unable to read ELF header section.\n");
      goto done;
    }
  }

  if ((systemRc = read(fd, &elfSectionHdr, elfhdr.e_shentsize)) !=  elfhdr.e_shentsize) {
    fprintf (pi01TraceFile, "Unable to read ELF string section header.\n");
    goto done;
  }

  if (elfSectionHdr.sh_size == 0) {
    fprintf (pi01TraceFile, "Warning - empty ELF string table; no symbols.\n");
    goto done;
  }

  stringTabOffset = elfSectionHdr.sh_offset;

  /* calculate the number of symbols found */
  syms = symbolSectionHdr.sh_size/symbolSectionHdr.sh_entsize;
  
  /*Seek to symbol table section */
  if ((systemRc = lseek (fd, (long) symbolSectionHdr.sh_offset, SEEK_SET)) != symbolSectionHdr.sh_offset) {
    fprintf (pi01TraceFile, "Unable to do seek to ELF symbol table\n");
    goto done;
  }
  
  for (i = 0; i < syms; i++) {
    if (( systemRc = read (fd, &symtabEntry, sizeof (Elf_Symbol))) != sizeof(Elf_Symbol)) {
      sprintf (vpi01MessageBuffer,
	       "Warning - reading ELF symbol table failed; got %d expected %d error %d\n",
	       systemRc, sizeof (Elf_Symbol), errno);           
      fprintf (pi01TraceFile, vpi01MessageBuffer);
      goto done;
    }
    if (symtabEntry.st_name != (Elf_Word) 0) {
      (void) vpi01ReadElfSymbol (fd, stringTabOffset, symtabEntry.st_name, pModuleFileName,
				 pi01TraceFile);
      goto done;
    }
  }
 done:
  close (fd);
}

char *vpi01ModuleName (FILE *pi01TraceFile)
{
  char *p = NULL;
  static int       fd_proc;	/* /proc fd                           */
  static prmap_t  *prmaps;   /* temporary allocated process maps   */
  static char     *mapname;  /* allocated process mapname          */
  static int       i;
  static int bytesRead;
  static struct stat mapStat;
  static char     *moduleFileName; /* SUN hides module file name in symbol table... */

  prmaps = 0;
  sprintf (vpi01MessageBuffer, "/proc/%d/map", getpid ());
  
  if ((fd_proc = open(vpi01MessageBuffer, O_RDONLY)) == -1) {
    sprintf (vpi01MessageBuffer,
	     "Cant open /proc/%d/map : %d\n", getpid (), errno);
    fprintf (pi01TraceFile, vpi01MessageBuffer);
    return ((char *) "");
  }
  
  /* Reading individual entries does not work. (SUN feature....) */
  if (-1 == fstat (fd_proc, &mapStat)) {
    sprintf (vpi01MessageBuffer,
	     "Cant stat /proc/%d/map : %d\n", getpid (), errno);
    fprintf (pi01TraceFile, vpi01MessageBuffer);
    (void) close (fd_proc);
    return ((char *) "");
  }
  if (mapStat.st_size <= sizeof(prmap_t) ||
      (mapStat.st_size % sizeof(prmap_t)) != 0) {
    sprintf (vpi01MessageBuffer,
	     "Cant stat /proc/%d/map : %d\n", getpid (), errno);
    fprintf (pi01TraceFile, vpi01MessageBuffer);
    (void) close (fd_proc);
    return ((char *) "");
  }

  prmaps = NOHEAP_CALLOC (mapStat.st_size, 1);
  if ( !prmaps ) {
    sprintf (vpi01MessageBuffer,
	     "Out of memory: Wanted %d bytes for copy of map file\n", 
	     (int) mapStat.st_size);
    fprintf (pi01TraceFile, vpi01MessageBuffer);
    (void) close (fd_proc);
    return ((char *) "");
  }
  
  bytesRead = 0; /* Read the complete file in */
  do { /* Reading individual entries does not work. (SUN feature....) */
    i = read (fd_proc, ((char *) prmaps) + bytesRead, mapStat.st_size - bytesRead);
    if (i < 0) {
      sprintf (vpi01MessageBuffer,
	       "Failed to read complete map file: %d\n", errno);
      fprintf (pi01TraceFile, vpi01MessageBuffer);
      (void) close (fd_proc);
      return ((char *) "");
    }
    bytesRead += i;
  } while (bytesRead < mapStat.st_size);

  close (fd_proc);

  /* Get address space (shared library) mappings                    */
  for (i = 0; (i * sizeof(prmap_t)) < mapStat.st_size; ++i) {
    static prmap_t *pm;
    static int      fd_obj;
    
    pm = prmaps + i;
    if (pm->pr_mflags != (MA_READ | MA_EXEC))
      continue;
    
    sprintf (vpi01MessageBuffer,
	     "/proc/%d/object/%s", getpid (), pm->pr_mapname);
    fd_obj = open (vpi01MessageBuffer, O_RDONLY);
    if(fd_obj == -1) {
      continue;
    }
    moduleFileName = (char *) "????";
    vpi01SunGetModuleFileName (pm->pr_mapname, &moduleFileName, pi01TraceFile);
    close (fd_obj);
    
    mapname = NOHEAP_CALLOC(strlen(pm->pr_mapname)+1, 1);
    strcpy (mapname, pm->pr_mapname);
    p = (char *) moduleFileName;
    break;
  }
  if (mapname) NOHEAP_FREE(mapname);
  if (prmaps) NOHEAP_FREE(prmaps);

  return p;
}

#endif

#ifdef HPUX

char *vpi01ModuleName (FILE *pi01TraceFile)
{
  char *p = NULL;
  static int dlIndex;
  static int retCode;
  static struct shl_descriptor *dlDesc;

  for (dlIndex = 0; 0 == (retCode = shl_get (dlIndex, &dlDesc)); ++dlIndex ) {
    p = (char *) dlDesc->filename;
    break;
  }
  return p;
}

#endif

#ifdef OSF1

char *vpi01ModuleName (FILE *pi01TraceFile)
{
  char *p = NULL;
  static ldr_process_t my_proc;
  static ldr_module_t	 module_id;
  static mce           moduleChainElement;
  static size_t		 ret_size;
  static int	         regionIndex;

  my_proc = ldr_my_process ();
  module_id = LDR_NULL_MODULE;

  for (;;) {
    static int	 rc;
    static mce_p me_p; /* ModuleChainElement pointer */
    static ldr_module_info_t * mInfo;
    
    me_p = &moduleChainElement;
    mInfo = &me_p->moduleInfo;
    rc = ldr_next_module (my_proc, &module_id);
    if (rc) {
      sprintf (vpi01MessageBuffer,
	       "ldr_next_module failed:%d\n", errno);
      fprintf (pi01TraceFile, vpi01MessageBuffer);
      break;
    }

    /* end of module list found */
    if (module_id == LDR_NULL_MODULE) {
      break;
    }

    /* inquire module information */
    rc = ldr_inq_module (my_proc,
			 module_id,
			 mInfo,
			 sizeof (ldr_module_info_t),
			 &ret_size );
    if (rc) {
      sprintf (vpi01MessageBuffer,
	       "ldr_inq_module failed:%d\n", errno);
      fprintf (pi01TraceFile, vpi01MessageBuffer);
      continue;
    }

    if (mInfo->lmi_nregion > 0) {
      static ldr_region_info_t *rInfo;
      rInfo = &me_p->textInfo;
      
      for (regionIndex = 0;
	   regionIndex < mInfo->lmi_nregion;
	   regionIndex++) {
	rc = ldr_inq_region (my_proc, module_id, regionIndex,
			     rInfo, sizeof (ldr_region_info_t),
			     &ret_size );
	if (rc) {
	  sprintf (vpi01MessageBuffer,
		   "ldr_inq_region failed for %s failed:%d\n",
		   mInfo->lmi_name, 
		   errno);
	  fprintf (pi01TraceFile, vpi01MessageBuffer);
	  break;
	}
	if (0 == memcmp (rInfo->lri_name, ".text", strlen(".text")+1)) {
	  regionIndex = mInfo->lmi_nregion;
	}
      }

      if (regionIndex < mInfo->lmi_nregion) {
	break; /* no text region found */
      }

      /* Copy of entry needed */
      me_p = (mce_p) calloc (1, sizeof(mce));
      if (0 == me_p) {
	fprintf (pi01TraceFile, "Out of memory for module list entry copy\n");
      } else {
	memcpy (me_p, &moduleChainElement, sizeof (moduleChainElement));
      }

      p = (char *) mInfo->lmi_name;
      if (me_p) free (me_p);
      me_p = 0;
      break;
    }
  } /* for(;;) break at end of module list */

  return p;
}

#endif

#ifdef AIX

char *vpi01ModuleName (FILE *pi01TraceFile)
{
  char *p = NULL;
  static char moduleInfo[4096];
  static struct ld_info *pInfo;
  static int rc;
  
  rc = loadquery (L_GETINFO, moduleInfo, sizeof (moduleInfo));
  if (rc < 0) {
    sprintf (vpi01MessageBuffer,
	     "No module list since loadquery failed: %d\n", errno);
    fprintf (pi01TraceFile, vpi01MessageBuffer);
    return ((char *) "");
  }

  for (pInfo = (struct ld_info *) moduleInfo;
       pInfo;
       pInfo = (struct ld_info *) (((char *) pInfo) + pInfo->ldinfo_next)) {

    p = (char *) pInfo->ldinfo_filename;
    break;

    if (!pInfo->ldinfo_next) {
	break;
    }
  }

  return p;
}

#endif

#ifdef NMP

char *vpi01ModuleName (FILE *pi01TraceFile)
{
  return "";
}

#endif
