/* gnu.a.out.c Functions to handle the symbol table of a a.out exec file.
   from nlist.c and objdump.c Modifications by T.Gingold */

/* Copyright (C) 1991 Free Software Foundation, Inc.
This file is part of the GNU C Library.

The GNU C Library 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, or (at your option)
any later version.

The GNU C Library 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 the GNU C Library; see the file COPYING.  If not, write to
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */

/* show_stack "dump" the stack using the debug informations.
   So, it must know the name of the programm (argv[0]), as soon as possible 
   because it can be changed.
   From the name, the programm searchs the complete path. This is made by
   find_exec.c, taken from dld 2.3.2 (thanks)
   This two operations are made the first time the programm call malloc.
   An other (and better) way is changing crt0.s. But this is not always 
   possible: we must have the sources or guess how crt0.s works.
   From these information, show_stack load the symbols informations only when
   necessary. They are stored in the stack: some space is allocated by alloca.
   I don't really want to use malloc, because show_stack could be used when
   the state of malloc is strange. I don'y want store this information at the
   beginn of the programm, because it use a lot of spaces.
   chkr_use_symtab is called each time an function want to use the symbols.
    Its mere argument is another function(...) which can access to the loaded
    symbol table. Yes, alloca make the system a little tortuous. For memory,
    alloca allocates mem in the stack, so because of the frame pointor, the 
    memory is automatically free when the function return. See auto variable.
 */   
    
#include <nlist.h>
#include <fcntl.h>
#include <a.out.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <strings.h>
#define obstack_chunk_free free
#define obstack_chunk_alloc xmalloc
#include "obstack.h"
#include "insertor.h"
#include "utils.h"
#include "stab.h"

#define PAGESIZE 4096

extern int end, etext, edata;

/* variable to access to the symbol table */
size_t nsymbols;			/* number of symbols */
struct nlist *symbols;		/* symbol table */
char *string_table;		/* string table */
unsigned long string_table_size;	/* size of the string table */
struct exec header;		/* header structure */

u_char *text_seg;
unsigned int text_len;
struct TSym *text_sym;
unsigned int nbr_text_sym = 0;

unsigned int treloc_len;
struct relocation_info *treloc_table;

/* return true if the symbol is useful
   usefule symbols are:
   0x04 N_TEXT		
   0x05 N_TEXT | N_EXT	symbol defined in text segment
   0x44 N_SLINE		line number in text segment
   0x64 N_SO		name of main source file
   Not really portable ...
 */
#define GOODSYM(x) ( ((x)& ~N_EXT) == N_TEXT || ((x)& ~0x20) == 0x44 )

#if 0
/* Compare two symbols
   It is necessary to sort. */
static int symcomp(const __ptr_t ptr1, const __ptr_t ptr2)
{
  return ( (struct nlist*)ptr1 )->n_value -
         ( (struct nlist*)ptr2 )->n_value;
}
#endif

static int reloccomp(const __ptr_t ptr1, const __ptr_t ptr2)
{
  return ( (struct relocation_info*)ptr1 )->r_address -
         ( (struct relocation_info*)ptr2 )->r_address;
}

static int Tsymcomp(const __ptr_t ptr1, const __ptr_t ptr2)
{
  register int res = ( (struct TSym*)ptr1 )->addr 
                   - ( (struct TSym*)ptr2 )->addr;
  if (res == 0)
    return ((struct TSym*)ptr1)->gcc - ((struct TSym*)ptr2)->gcc;
  else
    return res;
}

/* load the symbol table and call func to use the symbol table
   At the end of chkr_use_symtab, the symbol table is unusuable.
   The symbol table in memory keep only 'useful' symbols, and it is sorted.
   So, to be faster, the symtab is written to a temp file, and all next 
   calls to this function will only load the temp file. Really faster */
int
load_symbol_table(char *prog_path)
{
 int fd;		/* file decriptor */
 int status = 0;	/* bad status */
 int  str_index=0;

 fd = open(prog_path, O_RDONLY);		/* open the binary file */
 while (fd != -1)		/* used only to use break */
   {
     /* Load the header of the file */
     if (read(fd, (__ptr_t)&header, sizeof(header)) != sizeof(header))
       break;	/* in case of error */

     /* check that this is really an executable:
        The magic number must be valid */
     if( N_BADMAG(header) /* || !header.a_trsize || header.a_drsize */ )
       break;
     
#define PAGE_ROUND(a) (((unsigned)(a) + PAGESIZE -1) & ~(PAGE_SIZE - 1))     

     /* check that this is not a bad file */
     if (N_MAGIC(header) != OMAGIC)
       fatal(1,"Can't find good information in the binary file.\n");
       
     /* jump to the symbol table */
     if (lseek(fd, N_SYMOFF(header), 0) < 0)
       break;
     
     /* allocate (stack)mem for loading the symbols */
     symbols = (struct nlist *) xmalloc(header.a_syms);
     nsymbols = header.a_syms / sizeof(symbols[0]);
   
     /* load the symbols table */
     if (read(fd, (__ptr_t) symbols, sizeof(symbols[0]) * nsymbols) != 
 		sizeof(symbols[0])*nsymbols)
       break;
  
     /* load the size of all the strings */
     if (read(fd, (__ptr_t) &string_table_size, sizeof(string_table_size)) 
   				!= sizeof(string_table_size))
       break;
     
     /* allocate space for the strings */
     string_table_size -= sizeof(string_table_size);
     string_table = (char *) xmalloc(string_table_size);
   
     /* load the strings */ 
     if (read(fd, (__ptr_t) string_table, string_table_size) != 
     		string_table_size)
       break;
     
     /* sort the symbols table, according to the value */
/*     qsort(symbols, nsymbols, sizeof(symbols[0]), symcomp); */

     /* jump to the text segment */
     if (lseek(fd, N_TXTOFF(header), 0) < 0)
       break;
     
     /* allocate (stack)mem for loading the segment */
     text_seg = (char *) xmalloc(header.a_text);
     text_len = header.a_text;
   
     /* load the text segment */
     if (read(fd, text_seg, text_len) != text_len)
       break;
       
     /* jump to the text reloc*/
     if (lseek(fd, N_TRELOFF(header), 0) < 0)
       break;
     
     /* allocate (stack)mem for loading the segment */
     treloc_table = (struct relocation_info *) xmalloc(header.a_trsize);
     treloc_len = header.a_trsize;
   
     /* load the text segment */
     if (read(fd, treloc_table, treloc_len) != treloc_len)
       break;       
     
     treloc_len /= sizeof(struct relocation_info);
     
     /* sort this table */
     qsort(treloc_table, treloc_len, sizeof(treloc_table[0]), reloccomp);
       
     status = 1; /* all is right */
     break;
     /* NOTREACHED */
   }
   if (fd != -1)
     close(fd);		/* all is right */
 
 /* normalize the symbol table: Convert index to C-string (relocation) */
 if (status)
   {
     for(str_index=0; str_index < nsymbols; str_index++)
       {
         if (symbols[str_index].n_type == 0x00)
           printf("Found n_type == 0\n");
         if (symbols[str_index].n_un.n_strx)
           symbols[str_index].n_un.n_name = 
     		&string_table[symbols[str_index].n_un.n_strx - sizeof(int)];
         else
           symbols[str_index].n_un.n_name = "";
       }
     return 1;
   }
 else
   return 0;
}

void
first_process()
{
 int i;
 int j;
 int len;
 int gcc_compiled = 0;
 int sub_offset = -1;
 struct nlist *sp;
 struct obstack o_text_sym;
 struct obstack o_text_sym1;
 
 obstack_begin(&o_text_sym, 4096);
 obstack_begin(&o_text_sym1, 4096);
 /* At first, we collect all symbols for the .text section.
  * Then we sort them
  * And finally, we eliminate unuseful symbol and we mark the other symbols
  *  to know if they are produced by GCC.
  */
 
 for (i = 0, sp = symbols; i < nsymbols; i++, sp++)
   switch(sp->n_type & (~N_EXT))
     {
       case N_TEXT:
         len = strlen(sp->n_un.n_name);
         j = 4;
         if (strcmp(sp->n_un.n_name + len - 2, ".o") == 0)
             j = 0;
         if (strcmp(sp->n_un.n_name, "gcc2_compiled.") == 0)
             j = 1;
         if (strncmp(sp->n_un.n_name, "___gnu_compiled_", 16) == 0)
             j = 2;
         obstack_ptr_grow(&o_text_sym, sp->n_value);
         obstack_int_grow(&o_text_sym, j);
         obstack_int_grow(&o_text_sym, i);
         nbr_text_sym++;
         break; /* OK */
       case 0:
       case N_DATA:
       case N_BSS:
       
       case N_GSYM:
       case N_FUN:
       case N_RSYM:
       case N_SSYM:
       case N_PSYM:
       case N_LSYM:
       case N_SO:
       case N_SOL:
       case N_SLINE:
       case N_DSLINE:
       case N_BSLINE:
       case N_LBRAC:
       case N_RBRAC:
       case N_BCOMM:
       case N_ECOMM:
       case N_ECOML:
       case N_LENG:
       case N_LCSYM:
       case N_STSYM:
       	 break;	/* OK */
       case N_INDR:
         break;
       default:
       case N_SETA:
       case N_SETT:
       case N_SETD:
       case N_SETB:
       case N_SETV:
       case N_ABS:
       case N_FNAME:
       case N_MAIN:
       case N_ENTRY:
       case N_BINCL:
       case N_EINCL:
       case N_EXCL:
       case N_PC:
       case N_M2C:
       case N_SCOPE:
       	 fatal(1, "Unknown symbols '%s' (type=0x%x)\n", sp->n_un.n_name, sp->n_type);
     }
   text_sym = obstack_finish(&o_text_sym);
   qsort(text_sym, nbr_text_sym, sizeof(struct TSym), Tsymcomp);
   /* Now, we will really process the text symbols */
   sub_offset = -1;
   j = 0;
   gcc_compiled = 0;
   for (i = 0; i < nbr_text_sym ; i++)
   {
      if (text_sym[i].gcc == 0)
        {
          /* avoid the first, and double */
          if (sub_offset != -1 && gcc_compiled != 2 && sub_offset != text_sym[i].addr)
            printf("But it is not compiled by GCC!\n");
          printf("Found a sub object file: %s\n", symbols[text_sym[i].sym].n_un.n_name);
          sub_offset = text_sym[i].addr;
          gcc_compiled = 0;
          continue;
        }
      if (text_sym[i].gcc == 1)
        {
          if (sub_offset != text_sym[i].addr && sub_offset != -1)
            printf("gcc2_compiled. has a bad offset!\n");
          gcc_compiled++;
          continue;
        }
      if (text_sym[i].gcc == 2)
        {
          if (sub_offset != text_sym[i].addr && sub_offset != -1)
            printf("___gnu_compiled has a bad offset!\n");           
          gcc_compiled++;
          continue;
        }
      obstack_ptr_grow(&o_text_sym1, text_sym[i].addr);
      obstack_int_grow(&o_text_sym1, gcc_compiled > 0);
      obstack_int_grow(&o_text_sym1, text_sym[i].sym);
      text_bm[text_sym[i].addr] |= TBM_SYM;
      j++;
   }
   if (gcc_compiled == 0)
     printf("But it is not compiled by GCC!\n");
   obstack_free(&o_text_sym, (void*)0);
   text_sym = obstack_finish(&o_text_sym1);
   nbr_text_sym = j;
   
   /* finally, mark all text reloc as unused */
   for (i = 0; i < treloc_len; i++)
     treloc_table[i].r_pad = 0;
}            
     
/* show all the symbol table. */
void
dump_symtab()
{
  int i;
  struct nlist *sp;

  /* From objdump.c (binutils 1.9) */
  printf("%3s: %4s %5s %4s %8s\n",
	  "#", "type", "other", "desc", "val");
  for (i = 0, sp = symbols; i < nsymbols; i++, sp++)
    {
      printf("%3d: %4x %5x %4x %8x %s\n",
	      i,
	      sp->n_type & 0xff,
	      sp->n_other & 0xff,
	      sp->n_desc & 0xffff,
	      sp->n_value,
	      sp->n_un.n_name);
    }
}

void
dump_treloc()
{
  int i;
  struct relocation_info *ri;
  for(i = 0, ri = treloc_table; i < treloc_len; i++, ri++)
    {
      printf("%3d: %4x %1d %s",
              i,
              ri->r_address,
              1 << ri->r_length,
              ri->r_pcrel ? "PCREL" : "     ");
      if (ri->r_extern)
        printf(" %s\n", symbols[ri->r_symbolnum].n_un.n_name);
      else
        switch(ri->r_symbolnum)
          {
            case N_TEXT:
            case N_TEXT | N_EXT:
              printf(" .text\n");
              break;
            case N_DATA:
            case N_DATA | N_EXT:
              printf(" .data\n");
              break;
            case N_BSS:
            case N_BSS | N_EXT:
              printf(" .bss\n");
              break;
            default:
              printf(" .????\n");
/*	      printf(" %x\n", ri->r_symbolnum);*/
              break;
          }
    }
}

void
dump_tsym()
{
 int i;
 struct nlist *sp;
 for(i = 0; i < nbr_text_sym; i++)
   {
     sp = &symbols[text_sym[i].sym];
     printf("%3d: %4x %5x %4x %8x %s",
	      i,
	      sp->n_type & 0xff,
	      sp->n_other & 0xff,
	      sp->n_desc & 0xffff,
	      sp->n_value,
	      sp->n_un.n_name);
     if (text_sym[i].gcc)
       printf(" [compiled by GCC]\n");
     else
       printf("\n");
     if (sp->n_value != text_sym[i].addr)
       fatal(1, "Bad addr\n");
   }
}

#ifdef TEST
void
main(int argc, char *argv[])
{
  if(argc != 2)
    {
      fatal(1,"usage: %s prog\n",argv[0]);
      exit(1);
    }
  if (load_symbol_table(argv[1]))
  {
    first_process();
/*    dump_symtab();
    dump_treloc(); */
    dump_tsym();
  }
} 
#endif