/*
Copyright William Schelter. All rights reserved.
Modified 1990 by Per Bothner (bothner@cs.wisc.edu) to
emit a more compact and fast format.


  Use this to build an executable rsym,
which will grab only the external symbols from an object file,
and put them in a simple format: (cf ext_sym.h) 

This information will be used for relocation. 

to compile use cc rsym.c -o rsym  -I../h
*/



#include <stdio.h>
#include "config.h"
/* #include "mdefs.h" */
#include "ext_sym.h"



#ifdef ATT
/* #include <ldfcn.h> */
#endif

#define IN_RSYM 1

struct filehdr my_header;
int nsyms;
struct syment *symbol_table;
char *my_string_table;
char *start_address;

#ifdef RSYM_AUX
#include RSYM_AUX
#endif

/* our defs */

#define TABLE_SIZE 3


#ifdef DEBUG
int debug =1;
#undef dprintf
#define dprintf(s,ar) if(debug) { printf(" ( s )",ar) ; fflush(stdout);}
#else
int debug =0;
#define dprintf(s,ar) 
#endif




/*  this program will get the external symbols from a file writing
them out to a file together with their addresses */


main(argc,argv)
int argc ;
char *argv[];
{
  if (argc!=3) {perror("bad arg count");
		fflush(stdout);
		exit(1);}
  get_myself(argv[1]);
  output_externals(argv[2]);
  }

get_myself(filename)
char *filename;
{
	unsigned int i;
	FILE *fp;
	int string_size=0;
	extern char *malloc();
        
	fp = fopen(filename, RDONLY);
	
	if (fp == NULL) {
		fprintf(stderr, "Can't open %s\n", filename);
		exit(1);
	}


	HEADER_SEEK(fp);
	fread((char *)&my_header,sizeof(struct filehdr),1,fp);
	if(N_BADMAG(my_header)){
	  fprintf(stderr,"Bad magic %s",filename);
	  exit(1);};

	nsyms=NSYMS(my_header);
	symbol_table
	= (struct syment *)malloc(sizeof(struct syment) * nsyms);
	/*
	sizeof(struct syment) and SYMESZ are not always the same.
	*/

	if(fseek(fp,(int)(N_SYMOFF(my_header)),0))
	  {fprintf(stderr,"seek error");
					       exit(1);}

#ifdef HPUX
	for (i = 0;  i < nsyms;  i++)
	{	
		fread((char *)&symbol_table[i], SYMESZ, 1, fp);
		dprintf(string_size %d,string_size);
		symbol_table[i].n_un.n_strx = string_size;
		string_size += symbol_table[i].n_length + 1;
		fseek(fp,symbol_table[i].n_length,1);
	      }
#else	
	fread(symbol_table, SYMESZ, nsyms, fp);
#endif
	/*
	If the string table is not empty,
	its length is stored after the symbol table,
	This is not described in the manual, and may change in the future.
	*/
	/* fseek(fp,N_STROFF(my_header),0);
	   strings follow symbol table! */
#ifndef HPUX
#ifdef N_STROFF
	fseek(fp,N_STROFF(my_header),0);
#endif	
	if (fread((char *)&string_size, 4, 1, fp) > 0)	{
		my_string_table = malloc(string_size);
                if(debug)
		  {printf(" string_size is %d fseek %d ",string_size,fseek(fp,string_size-1,1));
		   printf(" fseek back %d ",fseek(fp,1-string_size,1));};
		fseek(fp, -4, 1);
		if(string_size!=(fread(my_string_table, 1, string_size, fp)))
		  {dprintf( string_size was %d ,string_size);
		   perror("rsym could not read bad string table") ;
		   exit(1);}

	}
	else {fprintf(stderr,"Error: There is no string table \n");
		     exit(1);}
#else	
{
		char *p; 
		int slen;
		p = my_string_table=malloc((unsigned int)string_size);
		dprintf( string table leng = %d, string_size);
		fseek(fp,(int)( LESYM_OFFSET(my_header)),  0);
		for (i = 0;  i < nsyms; i++)
		{
			fseek(fp,SYMESZ, 1);
			slen = symbol_table[i].n_length;
			dprintf( slen = %d,slen);
			fread(p,slen,1,fp);
			*((p)+slen) = '\0';
			dprintf( p = %s,p );
			dprintf(symbol_table[i].n_type %d,
				symbol_table[i].n_type);
			p += symbol_table[i].n_length + 1;
		}
	}
#endif
	fclose(fp);
}

int hash_size = 1024;
struct sym_link {
    struct sym_link *next;
    struct syment *sym;
};

struct sym_link **hash_tab = NULL;

output_externals(outfile)
     char *outfile;
{
    FILE *symout;
    int i;
    struct lsymbol_table tab;
    char tem[SYMNMLEN+1];
    struct syment *p, *end;
    int *offsets;
    char *name;
    tem[SYMNMLEN]=0;
    tab.n_symbols=0;
    tab.tot_leng=0;
    tab.hash_size = hash_size;
    symout=fopen(outfile,"w");
    if (!symout)
	{perror(outfile); exit(1);};
    hash_tab = (struct sym_link **)malloc(sizeof(struct sym_link*)*hash_size);
    offsets = (int*)malloc(sizeof(int) * (hash_size+1));
    for (i = hash_size; --i >= 0; ) hash_tab[i] = NULL;
    
    fseek(symout,sizeof(struct lsymbol_table),0);
    end = symbol_table + nsyms;
    for (p = symbol_table; p < end; p++)	{
	/* Is the following check enough? */
	if (EXT_and_TEXT_BSS_DAT(p)) {
	    unsigned hash;
	    struct sym_link *link =
		(struct sym_link*)malloc(sizeof(struct sym_link));
	    name = SYM_NAME(p);
	    hash = (unsigned)hash_string(name, strlen(name)) % hash_size;
	    link->sym = p;
	    link->next = hash_tab[hash];
	    hash_tab[hash] = link;
	    tab.n_symbols++;
	    p = p + NUM_AUX(p);
	}
    }
    for (i = 0; i < hash_size; i++) {
	struct sym_link *link = hash_tab[i];
	offsets[i] = ftell(symout);
	for ( ; link != NULL; link = link->next) {
	    p = link->sym;
	    name = SYM_NAME(p);
	    fwrite((char *)&(p->n_value),sizeof(int),1,symout);
	    dprintf( p->n_value %d , p->n_value);
	    dprintf( name %s , name);
	    while(tab.tot_leng++,*name)
		putc(*name++,symout); 
	    putc(0,symout);
	    dprintf(  NUM_AUX(p) %d ,  NUM_AUX(p));
	    dprintf( index , (int) (p - symbol_table) / sizeof(struct syment));
	}
    }
    offsets[i] = i = ftell(symout);
    for (; i % sizeof(int); i++) fputc(0, symout); /* align */
    tab.hash_location = i;
    fwrite(offsets, sizeof(int), hash_size+1, symout);
    fseek(symout,0,0);
    fwrite(&tab,sizeof(tab),1,symout);
    fclose(symout);
    return 0;
}

