/* 
  Copyright William Schelter. All rights reserved.
  There is a companion file rsym.c which is used to build
  a list of the external symbols in a COFF or A.OUT object file, for
  example saved_kcl.  These are loaded into kcl, and the
  linking is done directly inside kcl.  This saves a good 
  deal of time.   For example a tiny file foo.o with one definition
  can be loaded in .04 seconds.  This is much faster than
  previously possible in kcl.
  The function fasload from unixfasl.c is replaced by the fasload
  in this file.
  Modified by Per Bothner, 1990.
  */
#include <stdarg.h>


/* for testing in standalone manner define STAND
   You may then compile this file cc -g -DSTAND -DDEBUG -I../hn
   a.out /tmp/foo.o /public/akcl/unixport/saved_kcl /public/akcl/unixport/
   will write a /tmp/sfasltest file
   which you can use comp to compare with one produced by ld.
   */


#include "config.h"
#include <stdio.h>
#ifdef STAND
#include "mdefs.h"

#else
/*#include "include.h"*/
#undef S_DATA
#endif

#include "ext_sym.h"

int node_compare();
Pointer the_start;
Pointer init_address;
Pointer bsearch();

struct reloc relocation_info;
/* next 5 static after debug */

int debug;

#ifdef DEBUG
#define dprintf(s,ar) if(debug) { printf(" ( s )",ar) ; fflush(stdout);}
#define STAT

#else /* end debug */
#define dprintf(s,ar) 
#define STAT static
#endif


#define MAXPATHLEN 200
#define PTABLE_EXTRA 20

STAT struct syment *symbol_table;
STAT char *start_address;
STAT char *my_string_table;
STAT int extra_bss;

STAT char command[200];
STAT char tmpfile1 [50]; 

#ifndef describe_sym
#define describe_sym(a)
#endif

#ifdef STAND
#include "rel_stand.c"
#endif


char *bil;

handle_missing_symbol(name, where, kind)
     char *name;
     Pointer where;
{
    fprintf(stderr, "[Unknown label %s referenced at 0x%x]\n",
	    name, where);
}

struct pending_relocation {
    struct pending_relocation *next;
    Pointer where;
    int kind;
    /* followed by copy of name */
};

struct pending_relocation *pending_relocations = NULL;

do_pending_relocations()
{
    struct pending_relocation **ptr = &pending_relocations;
    while ( *ptr != NULL ) {
	register struct pending_relocation *preloc = *ptr;
	int found;
	Pointer addr = lookup_global_label((char*)(preloc + 1), &found);
	if (found) {
#ifdef BSD
	    switch(preloc->kind) {
	      case 0: *( char *)preloc->where += (int)addr; break;
	      case 1: *( short *)preloc->where += (int)addr; break;
	      case 2: *( long *)preloc->where += (long)addr; break;
	    }
#else
	    *(int*)preloc->where += addr; break;
#endif
	    *ptr = preloc->next;
	    free (preloc);
	}
	else
	    ptr = &(*ptr)->next;
    }
}

#if 0
handle_missing_symbol(name, where, kind)
     Pointer where;
     char *name;
{
    int nlen = strlen(name);
    struct pending_relocation *preloc = (struct pending_relation*)
	malloc(sizeof(struct pending_relocation) + nlen + 1);
    preloc->kind = kind;
    preloc->where = where;
    strcpy((char*)(preloc + 1), name);
    preloc->next = pending_relocations;
    pending_rleocations = preloc;     
}
#endif

#include RELOC_FILE

#ifdef AKCL
int fasload(faslfile)
     object faslfile;
{
    long fasl_vector_start;
    
    object memory, data;
    FILE *fp;
    char filename[MAXPATHLEN];
#ifndef STAND	
    object *old_vs_base = vs_base;
    object *old_vs_top = vs_top;
#endif
    bil=tempspace; /* reset tmp malloc */
#ifdef STAND
    strcpy(filename,faslfile);
    fp=fopen(filename,RDONLY);
#else
    coerce_to_filename(faslfile, filename);
    faslfile = open_stream(faslfile, smm_input, Cnil, Kerror);
    vs_push(faslfile);
    fp = faslfile->sm.sm_fp;
#endif
    linkload(fp);
#ifndef AKCL
    fclose(fp);
#endif
}
#else
static FEerror(format, arg1, arg2)
    char *format;
{
    fprintf(stderr, format, arg1, arg2);
}
#endif AKCL

linkload(fp)
     FILE *fp;
{
    struct filehdr fileheader;
#ifdef COFF
    struct scnhdr sectionheader;
    struct scnhdr section[4];
    int j;
#endif
    int string_size=0;
    int textsize, datasize, bsssize,nsyms;
    int i, nrel;

    init_address=0;
    
    HEADER_SEEK(fp);
    if(!fread((char *)&fileheader, sizeof(struct filehdr), 1, fp))
	FEerror("Could not get the header",0,0);
    nsyms = NSYMS(fileheader);
#ifdef COFF
    fseek(fp,fileheader.f_opthdr,1);
    fread(&section[1], sizeof(sectionheader), 1, fp);
    textsize = section[1].s_size;
    fread((char *)&section[2], sizeof(sectionheader), 1, fp);
    datasize = section[2].s_size; 
    fread(&sectionheader, sizeof(sectionheader), 1, fp);
    if (strcmp(section[3].s_name, ".bss") == 0)
	bsssize=section[3].s_size; 
    else     bsssize=section[3].s_size = 0;
#endif
    
#ifdef BSD
    textsize=fileheader.a_text;
    datasize=fileheader.a_data;
    bsssize=fileheader.a_bss;
#endif
    symbol_table =
	(struct syment *) malloc(sizeof(struct syment)*
				 (unsigned int)nsyms);
    fseek(fp,(int)( N_SYMOFF(fileheader)),  0);
    {
	for (i = 0;  i < nsyms;  i++)
	    {fread((char *)&symbol_table[i], SYMESZ, 1, fp);
	     dprintf( symbol table %d , i);
	     if (debug) describe_sym(i);
	     dprintf( at %d , &symbol_table[i]);
#ifdef HPUX
	     symbol_table[i].n_un.n_strx = string_size;
	     dprintf(string_size %d, string_size);
	     string_size += symbol_table[i].n_length + 1;
	     fseek(fp,(int)symbol_table[i].n_length,1);
#endif
	 }
    }
    /*	
      on MP386
      The sizeof(struct syment) = 20, while only SYMESZ =18. So we had to read
      one at a time.
      fread((char *)symbol_table, SYMESZ*fileheader.f_nsyms,1,fp);
      */
    
#ifdef READ_IN_STRING_TABLE
    
    my_string_table=READ_IN_STRING_TABLE(fp,string_size);
    
#else  
#ifdef MUST_SEEK_TO_STROFF
    fseek(fp,N_STROFF(fileheader),0);
#endif	
    {int ii=0;
     if (!fread((char *)&ii,sizeof(int),1,fp))
	 {FEerror("The string table of this file did not have any length",0,
		  0);}
     fseek(fp,-4,1);
     /* at present the string table is located just after the symbols */
     my_string_table=(char*)malloc((unsigned int)ii);
     dprintf( string table leng = %d, ii);
     
     if(ii!=fread(my_string_table,1,ii,fp))
	 FEerror("Could not read whole string table",0,0) ;
 }
#endif	
#ifdef SEEK_TO_END_OFILE
    SEEK_TO_END_OFILE(fp);	
#else	
    while ((i = getc(fp)) == 0)
	;
    ungetc(i, fp);
#endif
    
#ifdef AKCL
    fasl_vector_start=ftell(fp);
#endif
    
#if 0
    if (!((c_table.ptable) && *(c_table.ptable)))
	build_symbol_table();
#endif
    
    /* figure out if there is more bss space needed */
    extra_bss=get_extra_bss(symbol_table,nsyms,datasize+textsize+bsssize);

    /* allocate some memory */
#ifndef STAND
#ifdef AKCL
    memory = alloc_object(t_cfdata);
    memory->cfd.cfd_self = 0;
    memory->cfd.cfd_start = 0;
    memory->cfd.cfd_size = datasize+textsize+bsssize + extra_bss;
    vs_push(memory);
    the_start=start_address=        
	memory->cfd.cfd_start =	
	    alloc_contblock(memory->cfd.cfd_size);
#else /* !AKCL */
    the_start=start_address =
	(Pointer)malloc(datasize+textsize+bsssize + extra_bss);
#endif /* !AKCL */
#else
    the_start=start_address
	= malloc(datasize+textsize+bsssize + extra_bss + 0x80000);
    the_start=start_address= (char *)(
				      0x1000* ((((int)the_start + 0x70000) + 0x1000)/0x1000));
    
#endif	  
    dprintf( code size %d , datasize+textsize+bsssize + extra_bss);
    if (fseek(fp,N_TXTOFF(fileheader) ,0) < 0)
	FEerror("file seek error",0,0);
    fread(the_start, textsize + datasize, 1, fp);
    dprintf(read into memory text +data %d bytes, textsize + datasize);
    /* relocate the actual loaded text  */
    
    dprintf( the_start %x, the_start);
    
    /* this looks up symbols in c.ptable and also adds new externals to
       that c.table */
    relocate_symbols(NSYMS(fileheader));  
    
#ifdef COFF
    for(j=1; j<3 ; j++) {
	dprintf( relocating section %d \n,j);
	fseek(fp,section[j].s_relptr,0);
	for(i=0; i < section[j].s_nreloc; i++) {
	    fread(&relocation_info, 10, 1, fp);
	    dprintf(relocating %d,i);
	    relocate();};
    }
#endif
#ifdef BSD
    fseek(fp,N_RELOFF(fileheader),0);
    nrel = (fileheader.a_trsize/sizeof(struct reloc));
    for (i=0; i < nrel; i++) {
	fread((char *)&relocation_info, sizeof(struct reloc), 1, fp);
	dprintf(relocating %d,i);
	relocate();
    }
#ifdef N_DRELOFF
    fseek (fp, N_DRELOFF(fileheader), 0);
#endif
    nrel = (fileheader.a_drsize/sizeof(struct reloc));
    the_start += fileheader.a_text;
    for (i=0; i < nrel; i++) {
	fread((char *)&relocation_info, sizeof(struct reloc), 1, fp);
	dprintf(relocating %d,i);
	relocate();
    }
#endif
    
    /* end of relocation */
    dprintf( END OF RELOCATION \n,0);
    dprintf( invoking init function at %x, start_address)
	dprintf( textsize is %x,textsize);
    dprintf( datasize is %x,datasize);
    
#ifdef AKCL
    /* read in the fasl vector */
    fseek(fp,fasl_vector_start,0);
    if (feof(fp))
	data=0;
    else {
	data = read_fasl_vector(faslfile);
	vs_push(data);
#ifdef COFF
	dprintf( read fasl now symbols %d , fileheader.f_nsyms);
#endif
    }
    close_stream(faslfile, 1);	
#endif /* AKCL */
    
#ifdef STAND
    {FILE *out;
     out=fopen("/tmp/sfasltest","w");
     fwrite((char *)&fileheader, sizeof(struct filehdr), 1, out);
     fwrite(start_address,sizeof(char),datasize+textsize,out);
     fclose(out);}
    printf("\n(start %x)\n",start_address);
#else /* !STAND */
    free(my_string_table);
    free(symbol_table);
    
#ifdef AKCL 
    call_init(init_address,memory,data);
    
    vs_base = old_vs_base;
    vs_top = old_vs_top;
    if(symbol_value(Vload_verbose)!=Cnil)
        printf("start address -T %x ",memory->cfd.cfd_start);
    return(memory->cfd.cfd_size);
#endif /* AKCL */
#endif /* !STAND */
}

get_extra_bss(symbol_table, length, start)
     int length;
     struct syment *symbol_table;
{
 int result = start;
 struct syment *end,*sym;
 char tem[SYMNMLEN +1];
 end =symbol_table + length;
 for(sym=symbol_table; sym < end; sym++) {
#ifdef BSD
     if ((sym->n_type & N_EXT) && N_SECTION(sym) == N_UNDEF)
#endif
#ifdef COFF
     if(0)
		 /* what we really want is
		    if (sym->n_scnum==0 && sym->n_sclass == C_EXT
		    && !(bsearch(..in ptable for this symbol)))
		    Since this won't allow loading in of a new external array
		    char foo[10]  not ok
		    static foo[10] ok.
		    for the moment we give undefined symbol warning..
		    Should really go through the symbols, recording the external addr
		    for ones found in ptable, and for the ones not in ptable
		    set some flag, and add up the extra_bss required.  Then
		    when you have the new memory chunk in hand,
		    you could make the pass setting the relative addresses.
		    for the ones you flagged last time.
		    */
#endif
	 /* external bss so not included in size of bss for file */
	 {int val=sym->n_value;
	  if (val) {
	      int found;
	      Pointer addr = lookup_global_label(SYM_NAME(sym), &found);
	      if (found) {
		  sym->n_value = (int)addr;
		  sym->n_type = N_ABS;
	      }
	      else {
		  sym->n_value=result;
		  sym->n_type = N_BSS|N_EXT;
		  result += val;}}}
     sym += NUM_AUX(sym); 
     
 }
 return (result-start);
}


/* go through the symbol table changing the addresses of the symbols
   to reflect the current cfd_start */

relocate_symbols(length)
     unsigned int length;
{
    struct syment *end,*sym;
    unsigned int typ;
    char tem[SYMNMLEN +1];
    int found;
    Pointer addr;
    tem[SYMNMLEN]=0;
    end =symbol_table + length;
    for(sym=symbol_table; sym < end; sym++) {
	char *name = SYM_NAME(sym);
	typ=NTYPE(sym);
#ifdef N_STAB    
	if (N_STAB & sym->n_type) {
	    continue;/* skip: It  is for dbx only */
	}
#endif    
#ifdef BSD
#ifdef N_SETT
	if (sym->n_type == N_SETT) {
	    init_address = start_address + sym->n_value;
#ifdef DEBUG
	    fprintf(stderr, "[Init: %s = 0x%x]\n", name, init_address);
#endif
	}
#endif
	typ=N_SECTION(sym);
	if ((sym->n_type & N_EXT) && N_SECTION(sym) != N_UNDEF) {
	    char *new_name=(char*)malloc(1+strlen(name));
	    strcpy(new_name, name);
	    add_global_label(new_name, start_address + sym->n_value);
	}
#endif
	switch (typ)	{
#ifdef BSD
	  case N_ABS : case N_TEXT: case N_DATA: case N_BSS:
#endif
#ifdef COFF
	  case 1: case 2: case 3:
#endif
	    dprintf( for sym %s ,name)
		dprintf( new value will be start %x, start_address);
	    sym->n_value += (int)start_address;
	    break;
	  case  N_UNDEF:
	    addr = lookup_global_label(name, &found);
	    if (found) {
#ifdef COFF
		sym->n_value = addr - sym->n_value;
#endif
#ifdef BSD
		/* the old value of sym->n_value is the length of the
		   common area starting at this address */
		sym->n_value = (int)addr;
		sym->n_type = N_ABS;
#endif
	    }      
	    else {
		fprintf(stderr,"undefined %s symbol", name);
		fflush(stderr);
	    }
	    break;
	  default:
#ifdef COFF
	    dprintf(am ignoring a scnum %d,(sym->n_scnum));
#endif
	    break;
	}
	sym += NUM_AUX(sym);
    }
}

#if 0
#define system_directory ""
build_symbol_table()
{  printf("Building symbol table for %s ..\n",kcl_self);fflush(stdout);
   sprintf(tmpfile1,"/tmp/rsym%d",getpid());
   coerce_to_filename(symbol_value(siVsystem_directory),
		      system_directory);
   sprintf(command,"%srsym %s %s",system_directory,kcl_self,tmpfile1);
   if (system(command) != 0)
       FEerror("The rsym command ~a failed .",1,make_simple_string(command)
	       );
   read_symbol_tables(tmpfile1);
   unlink(tmpfile1);
}
#endif

struct new_global_label {
    char *name;
    Pointer value;
    struct new_global_label *next;
};

static struct new_global_label **new_global_labels = NULL;
static int new_global_size = 512;

add_global_label(name, value)
     char *name;
     Pointer value;
{
    struct new_global_label *new_label;
    int hash;
    if (new_global_labels == NULL) {
	int i;
	new_global_labels = (struct new_global_label**)
	    malloc(sizeof(struct global_label*) * new_global_size);
	for (i = new_global_size; --i >= 0; ) new_global_labels[i] = NULL;
    }
    hash = hash_string(name, strlen(name)) % new_global_size;
    new_label =
	(struct new_global_label*)malloc(sizeof(struct new_global_label));
    new_label->name = name;
    new_label->value = value;
    new_label->next = new_global_labels[hash];
    new_global_labels[hash] = new_label;
}

#ifdef DEBUG
print_name(p)
     struct syment *p;
{char tem[10],*name;
 name=SYM_NAME(p);
 name=   (((p)->_n._n_n._n_zeroes == 0) ? 
	  &my_string_table[(p)->_n._n_n._n_offset] :
	  ((p)->_n._n_name[SYMNMLEN -1] ? 
	   (strncpy(tem,(p)->_n._n_name,  
		    SYMNMLEN), 
	    (char *)tem) : 
	   (p)->_n._n_name ));
 
 printf("(name:|%s|)",name);
 printf("(sclass 0x%x)",p->n_sclass);
 printf("(external_p 0x%x)",SYM_EXTERNAL_P(p));
 printf("(n_type 0x%x)",p->n_type);
 printf("(n_value 0x%x)",p->n_value);
 printf("(numaux 0x%x)\n",NUM_AUX(p));
 fflush(stdout);
}
#endif
/* Copyright (C) 1990 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 1, 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.  */

/* Perform a binary search for KEY in BASE which has NMEMB elements
   of SIZE bytes each.  The comparisons are done by (*COMPAR)().  */
Pointer bsearch(key, base, nmemb, size, compar)
register char * key ;
register char * base;
int nmemb;
register int size;
register int (*compar)();
{
    register int l, u, idx;
    register char * p;
    register int comparison;
    
    l = 0;
    u = nmemb - 1;
    while (l <= u)
	{
	    idx = (l + u) / 2;
	    p = (char*) ((( char *) base) + (idx * size));
	    comparison = (*compar)(key, p);
	    /* Don't make U negative because it will wrap around.  */
	    if (comparison < 0)
		{
		    if (idx == 0)
			break;
		    u = idx - 1;
		}
	    else if (comparison > 0)
		l = idx + 1;
	    else
		return (Pointer) p;
	}
    
    return NULL;
}

int *global_label_hashtab = NULL;
struct lsymbol_table *GlobalLabels = NULL;

void read_symbol_table(char *fileName)
{
    struct lsymbol_table head;
    FILE *sym_file;
    int tot_size;
    if (GlobalLabels != NULL) return;
    if (fileName == NULL) fileName = "Q.syms";
    sym_file = fopen(fileName, "r");
    if (sym_file == NULL) {
	fprintf(stderr, "Cannot open symbol file: %s\n", fileName);
	exit(-1);
    }
    fread(&head, sizeof(head), 1, sym_file);
    tot_size = SYMTAB_TOTAL_SIZE(&head);
    GlobalLabels = (struct lsymbol_table*)malloc(tot_size);
    fseek(sym_file, 0, 0);
    fread(GlobalLabels, tot_size, 1, sym_file);
    global_label_hashtab = (int*)((char*)GlobalLabels + head.hash_location);
    fclose(sym_file);
}

Pointer lookup_global_label(char *name, int *found)
{
    void *object;
    char *ptr, *lim;
    char *nptr;
    int *val;
    int hash;
    int hash0 = hash_string(name, strlen(name));

    /* First search the "new" labels, if any */
    if (new_global_labels) {
	struct new_global_label *new_label =
	    new_global_labels[hash0 % new_global_size];
	for ( ; new_label != NULL; new_label = new_label->next)
	    if (strcmp(new_label->name, name) == 0)
		return new_label->value;
    }

    /* Now search the "global" table (i.e. original symbol table */
    if (GlobalLabels == NULL) read_symbol_table(NULL);
    hash = hash0 % GlobalLabels->hash_size;
    ptr = (char*)GlobalLabels + global_label_hashtab[hash];
    lim = (char*)GlobalLabels + global_label_hashtab[hash+1];
    for (;;) {
	if (ptr == lim) {
	    if (found) *found = 0;
	    return NULL;
	}
	val = (int*)ptr;
	ptr += sizeof(int);
	nptr = name;
	for (;;) {
	    int ch = *ptr++;
	    if (*nptr++ != ch) break;
	    if (ch == 0) {
		if (found) *found = 1;
#ifdef STRICT_ALIGNMENT
		{union { void *addr; char c[sizeof(void*)]; } u;
		 int i;
		 for (i = sizeof(void*); --i>= 0; )
		     u.c[i] = ((char*)val)[i];
		 return u.addr;}
#else
		return (void*)*val;
#endif
	    }
	}
	while ( *ptr++ != 0) ;
    }
}
