#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <sys/stat.h>

#include "disasm.h"
#include "bool.h"

#define CODE_BUF_LEN	20
#define CLLD_MAGIC	0x434c4c44
#define GCCLDD_MAGIC	0x47430


#define TREAD(fp, ptr, type) \
	(read ((fp), &(ptr), sizeof (type)) != sizeof (type))

#define DOCOL		0x02d9d
#define DOEXT0		0x02b88
#define DOIDNT		0x02e48
#define SEMI		0x0312b

static char		*progname;

static unsigned char	*code;

static void		 dump_file (int fp);
static void		 dump_code (unsigned long codestart,
				    unsigned long codeend);
static char		*hexstr (unsigned long addr, int len);
static unsigned long	 read_nibbles (unsigned long addr, int len);

struct reloc
{
  unsigned long	 offset;
  struct reloc	*next;
};

struct import
{
  unsigned long	 offset;
  int		 libnum;
  unsigned long	 liboff;
  struct import	*next;
};

struct shlib
{
  char		*name;
  struct import *imports;
  struct shlib	*next;
};

static char		*loader = 0;
static char	       **libnames;

static struct shlib	*libs = 0;
static unsigned int	 nshlibs = 0;

static struct reloc	*relocs = 0;
static unsigned int	 nrelocs = 0;

static struct import	*imports = 0;
static unsigned int	 nimports = 0;

int
main (int argc, char **argv)
{
  char		*objname;
  char		 hpstr[8];
  unsigned long	 magic;
  unsigned long	 pc;
  unsigned long	 base;
  unsigned long	 offset;
  unsigned long	 idlen;
  unsigned long	 headlen;
  unsigned long	 headoff;
  unsigned long	 datalen;
  unsigned long	 codestart;
  unsigned long	 codeend;
  int		 of;
  unsigned int	 i, j;
  struct stat	 st;
  struct shlib	*sh;
  struct reloc	*rel;
  struct import	*imp;

  progname = strrchr (argv[0], '/');
  if (progname)
    progname++;
  else
    progname = argv[0];

  if (argc < 2)
    {
      fprintf (stderr, "usage: %s object-file\n", progname);
      exit (1);
    }

  objname = argv[1];
  of = open (objname, O_RDONLY);
  if (of < 0)
    {
      fprintf (stderr, "%s: can't open `%s'\n", progname, objname);
      exit (1);
    }

  if (TREAD (of, magic, unsigned long))
    {
      fprintf (stderr, "%s: can't read magic number\n", progname);
      exit (1);
    }

  if (magic == CLLD_MAGIC)
    {
      dump_file (of);
    }
  else
    {
      lseek (of, 0, SEEK_SET);
      if (read (of, hpstr, 8) != 8)
        {
          fprintf (stderr, "%s: can't read HPHP48-X header\n", progname);
          exit (1);
        }
      if (!strncmp (hpstr, "HPHP48-", 7))
        {
          fprintf (stdout, "HP48 binary Rev %c\n", hpstr[7]);
	  fstat (of, &st);
	  code = (unsigned char *) malloc (st.st_size - 8);
	  if (!code)
	    {
	      fprintf (stdout, "%s: can't malloc buffer\n", progname);
	      exit (1);
            }
          if (read (of, code, st.st_size - 8) != st.st_size - 8)
	    {
	      fprintf (stdout, "%s: can't read file\n", progname);
	      exit (1);
	    }
          pc = 0;
	  if (DOCOL != read_nibbles (pc, 5))
	    {
	      fprintf (stdout, "%s: no Composite object\n", progname);
	      exit (1);
	    }
          pc += 5;
          while (DOIDNT == read_nibbles (pc, 5))
            {
              pc += 5;
	      idlen = read_nibbles (pc, 2);
	      pc += 2;
	      sh = (struct shlib *) malloc (sizeof (struct shlib));
              sh->name = (char *) malloc (idlen + 1);
	      for (i = 0; i < idlen; i++)
		{
		  sh->name[i] = (char)read_nibbles (pc, 2);
		  pc += 2;
		}
	      sh->name[i] = '\0';
	      sh->imports = 0;
	      sh->next = libs;
	      libs = sh;
              nshlibs++;
	    }

	  if (DOEXT0 == read_nibbles (pc, 5))
	    {
	      pc += 5;
	      datalen = read_nibbles (pc, 5);
	      codeend = pc + datalen;
	      pc += 5;
	      magic = read_nibbles (pc, 5);
	      pc += 5;
              if (magic != GCCLDD_MAGIC)
	        {
                  fprintf (stdout, "%s: wrong magic number\n", progname);
	          exit (1);
	        }

	      libnames = (char **) malloc ((nshlibs + 1) * sizeof(char *));
	      if (!libnames)
		{
		  fprintf (stderr, "%s: can't malloc libnames\n", progname);
		  exit (1);
		}
              libnames[0] = "main";

              sh = libs;
              i = 0;
	      while (sh)
		{
		  fprintf (stdout, "Need shared library %d: `%s'\n", i, sh->name);
                  i++;
		  libnames[i] = sh->name;
		  sh = sh->next;
		}

	      headlen = read_nibbles (pc, 5);
	      fprintf (stdout, "header length 0x%05lX\n", headlen);

	      codestart = headlen + pc;
	      headoff = pc + 5;

	      pc = codeend;
	      if (DOIDNT == read_nibbles (pc, 5))
		{
		  pc += 5;
		  idlen = read_nibbles (pc, 2);
		  pc += 2;
		  loader = (char *)malloc (idlen + 1);
		  if (!loader)
		    {
		      fprintf (stderr, "%s: can't malloc loader name\n",
			       progname);
		      exit (1);
		    }
		  for (i = 0; i < idlen; i++)
		    {
		      loader[i] = read_nibbles (pc, 2);
		      pc += 2;
		    }
		  loader[i] = '\0';
		}

	      if (loader)
		fprintf (stdout, "Loader: `%s'\n", loader);
	      pc = headoff;

	      base = read_nibbles (pc, 5);
	      pc += 5;
	      fprintf (stdout, "start address 0x%05lX\n", base);

	      while ((offset = read_nibbles (pc, 5)) != 0x00000)
		{
                  pc += 5;
		  rel = (struct reloc *) malloc (sizeof (struct reloc));
		  rel->offset = offset;
		  rel->next = relocs;
		  relocs = rel;
		  nrelocs++;
		}
              pc += 5;
              rel = relocs;
	      i = 0;
	      while (rel)
		{
		  fprintf (stdout, "Reloc %d: at 0x%05lX\n",
			   i, rel->offset);
                  i++;
		  rel = rel->next;
		}

	      if (loader)
		{
		  /* executable */
		  pc += 5;		/* skip start address rep	*/
		  pc += 5 * nshlibs;	/* skip shlib start addresses	*/
		  pc += 5;		/* skip 0			*/

		  while (read_nibbles (pc, 2) != 0x00)
		    {
		      imp = (struct import *) malloc (sizeof (struct import));
		      if (!imp)
			{
			  fprintf (stderr, "%s: can't malloc import\n",
				   progname);
			  exit (1);
			}
		      imp->libnum = (read_nibbles (pc, 2) - 1) / 5;
		      pc += 2;
		      imp->liboff = read_nibbles (pc, 5);
		      pc += 5;
		      imp->offset = read_nibbles (pc, 5);
		      pc += 5;
		      imp->next = imports;
		      imports = imp;
		    }
		  pc += 2;

		  sh = libs;
		  for (i = 0; i < nshlibs; i++)
		    {
		      while (read_nibbles (pc, 2) != 0x00)
		        {
		          imp = (struct import *)malloc(sizeof (struct import));
		          if (!imp)
			    {
			      fprintf (stderr, "%s: can't malloc import\n",
				       progname);
			      exit (1);
			    }
		          imp->libnum = (read_nibbles (pc, 2) - 1) / 5;
		          pc += 2;
		          imp->liboff = read_nibbles (pc, 5);
		          pc += 5;
		          imp->next = sh->imports;
		          sh->imports = imp;
		    	}
		      pc += 2;
		      sh = sh->next;
		    }

		  imp = imports;
		  i = 0;
		  if (imp)
		    fprintf (stdout, "`%s' imports:\n", libnames[i]);
		  while (imp)
		    {
		      fprintf (stdout,
			  "Import %d: at 0x%05lX offset 0x%05lX into `%s'\n",
			  i, imp->offset, imp->liboff, libnames[imp->libnum]);
		      i++;
		      imp = imp->next;
		    }

		  sh = libs;
		  for (i = 0; i < nshlibs; i++)
		    {
		      fprintf (stdout, "`%s' imports:\n", sh->name);
		      imp = sh->imports;
		      j = 0;
		      while (imp)
		        {
		          fprintf (stdout,
			    "Import %d: offset 0x%05lX into `%s'\n",
			    j, imp->liboff, libnames[imp->libnum]);
		          j++;
		          imp = imp->next;
		        }
		      sh = sh->next;
		    }
		}
	      else
		{
		  /* shared lib */
	          while (pc < codestart)
		    {
                      offset = read_nibbles (pc, 5);
                      pc += 5;
		      imp = (struct import *) malloc (sizeof (struct import));
		      imp->offset = offset;
		      imp->liboff = 0;
		      imp->next = imports;
		      imports = imp;
		      nimports++;
		    }
                  imp = imports;
	          i = 0;
	          while (imp)
		    {
		      fprintf (stdout, "Import %d: at 0x%05lX\n",
			       i, imp->offset);
                      i++;
		      imp = imp->next;
		    }
		}

              fprintf (stdout, "Code len: 0x%05lX\n", codeend - codestart);
	      dump_code (codestart, codeend);
	    }
	  else
	    {
              fprintf (stdout, "%s: no LibraryData found\n", progname);
	      exit (1);
	    }
        }
      else
        {
          fprintf (stdout, "%s: unknown object format\n", progname);
	}
    }

  close (of);

  return 0;
}

typedef union
{
  unsigned long long l;
  double d;
} l_or_d;

static void
dump_file (int fp)
{
  long		namlen;
  long		n;
  int		i, codlen;
  unsigned long reloc;
  unsigned long offset;
  unsigned long size;
  bool		reloc_demand;
  bool		isfloat;
  bool		isnum;
  bool		isshared;
  bool		even;
  l_or_d	value;
  char		name[200];

  if (TREAD (fp, namlen, long))
    {
      fprintf (stderr, "%s: can't read name length\n", progname);
      exit (1);
    }

  if (read (fp, name, namlen) != namlen)
    {
      fprintf (stderr, "%s: can't read name\n", progname);
      exit (1);
    }
  name [namlen] = '\0';

  fprintf (stdout, "Name: `%s'\n", name);

  
  if (TREAD (fp, n, long))
    {
      fprintf (stderr, "%s: can't read number of relocs\n", progname);
      exit (1);
    }
  
  fprintf (stdout, "Relocs: %ld\n", n);

  for (i = 0; i < n; i++)
    {
      if (TREAD (fp, reloc, unsigned long))
        {
          fprintf (stderr, "%s: can't read reloc %d\n", progname, i);
          exit (1);
        }
  
      fprintf (stdout, "Reloc %d: at 0x%05lX\n", i, reloc);
    }

  if (TREAD (fp, n, long))
    {
      fprintf (stderr, "%s: can't read number of imports\n", progname);
      exit (1);
    }
  
  fprintf (stdout, "Imports: %ld\n", n);

  for (i = 0; i < n; i++)
    {
      if (TREAD (fp, offset, unsigned long))
        {
          fprintf (stderr, "%s: can't read offset %d\n", progname, i);
          exit (1);
        }

      if (TREAD (fp, size, unsigned long))
        {
          fprintf (stderr, "%s: can't read size %d\n", progname, i);
          exit (1);
        }

      if (TREAD (fp, namlen, long))
        {
          fprintf (stderr, "%s: can't read name length\n", progname);
          exit (1);
        }

      if (read (fp, name, namlen) != namlen)
        {
          fprintf (stderr, "%s: can't read import %d\n", progname, i);
          exit (1);
        }
      name [namlen] = '\0';

      if (TREAD (fp, reloc_demand, bool))
        {
          fprintf (stderr, "%s: can't read reloc_demand %d\n", progname, i);
          exit (1);
        }

      fprintf (stdout, "Import %d: `%s' offset 0x%05lX len 0x%05lX %s\n",
		i, name, offset, size, reloc_demand ? "relocate" : "");
    }

  if (TREAD (fp, n, long))
    {
      fprintf (stderr, "%s: can't read number of symbols\n", progname);
      exit (1);
    }
  
  fprintf (stdout, "Symbols: %ld\n", n);

  for (i = 0; i < n; i++)
    {
      if (TREAD (fp, isfloat, bool))
        {
          fprintf (stderr, "%s: can't read float flag %d\n", progname, i);
          exit (1);
        }

      if (TREAD (fp, value.l, unsigned long long))
        {
          fprintf (stderr, "%s: can't read symbol value %d\n", progname, i);
          exit (1);
        }

      if (TREAD (fp, namlen, long))
        {
          fprintf (stderr, "%s: can't read name length\n", progname);
          exit (1);
        }

      if (read (fp, name, namlen) != namlen)
        {
          fprintf (stderr, "%s: can't read import %d\n", progname, i);
          exit (1);
        }
      name [namlen] = '\0';

      if (TREAD (fp, isnum, bool))
        {
          fprintf (stderr, "%s: can't read float flag %d\n", progname, i);
          exit (1);
        }

      fprintf (stdout, "Symbol %d: %s `%s'",
		i, isnum ? (isfloat ? "float" : "int") : "function", name);
      if (!isnum)
        fprintf (stdout, " offset 0x%05lX\n", (unsigned long)value.l);
      else
        {
          if (isfloat)
            fprintf (stdout, " value %0.11E\n", value.d);
          else
            fprintf (stdout, " value %016llX\n", value.l);
        }
    }

  if (TREAD (fp, isshared, bool))
    {
       fprintf (stderr, "%s: can't read shared flag\n", progname);
       exit (1);
    }

  fprintf (stdout, "Shared: %s\n", isshared ? "yes" : "no");

  if (TREAD (fp, even, bool))
    {
       fprintf (stderr, "%s: can't read even flag\n", progname);
       exit (1);
    }

  if (TREAD (fp, n, unsigned long))
    {
       fprintf (stderr, "%s: can't read length of code\n", progname);
       exit (1);
    }

  codlen = 2 * n;
  if (!even)
    codlen--;
  fprintf (stdout, "Code len: 0x%05X\n", codlen);

  code = (unsigned char *) malloc (n);
  if (!code)
    {
       fprintf (stderr, "%s: can't malloc storage for code\n", progname);
       exit (1);
    }

  if (read (fp, code, n) != n)
    {
       fprintf (stderr, "%s: can't read code\n", progname);
       exit (1);
    }

  dump_code (0, codlen);
  free (code);
}

static void
dump_code (unsigned long codestart, unsigned long codeend)
{
  unsigned long addr = codestart;
  unsigned long next;
  char		inst[200];

  while (addr < codeend)
    {
      next = disassemble (addr, inst);
      fprintf (stdout, "0x%05lX: %s %s\n", addr - codestart,
	       hexstr (addr, next - addr), inst);
      addr = next;
    }
}

unsigned int
read_nibble (unsigned long addr)
{
  unsigned int nib;

  nib = code [addr >> 1];
  if (addr & 1)
    nib = (nib >> 4) & 0x0f;
  else
    nib &= 0x0f;
  return nib;
}

static unsigned long
read_nibbles (unsigned long addr, int len)
{
  unsigned long res = 0;

  addr += len;
  while (len-- > 0)
    {
      res = (res << 4) | read_nibble (--addr);
    }
  return res;
}

static char *
hexstr (unsigned long addr, int len)
{
  static char buf[200];
  int  i;

  for (i = 0; i < len; i++)
    {
       buf[i] = read_nibble (addr + i);
       if (buf[i] < 0x0a)
         buf[i] += '0';
       else
         buf[i] += 'A' - (char)10;
    }
  for ( ; i < CODE_BUF_LEN; i++)
    buf[i] = ' ';

  buf[i] = '\0';
  return buf;
}



syntax highlighted by Code2HTML, v. 0.9.1