/* Copyright Per Bothner 1987. Read the file Q-INFO */
#include <stdio.h>
#include <b.out.h>
#include "b.structs.h"

typedef char *Pointer;
struct BinDescrptor
  {
    Pointer base;
    Pointer torigin, dorigin; /* start of text and data segments */
    Pointer (*symLookup)();
    struct ExtSymbol symList;
  };

#define xread(ptr, n, file) \
  { int _n=n; if (fread(ptr, _n, 1, file) != _n) return END_OF_FILE; }
#define Error(x) return x;

base

SystemCode
LinkLoad(bfile, desc)
    FILE *bfile;
    struct BinDescriptor *desc;
  { struct bhdr filhdr;	/* .b file header */
    int i, pos, c, segment;
    xread(&filhdr, sizeof filhdr, bfile);
    ByteSwapLongInPlace(&filhdr, sizeof(struct bhdr));
    if (filhdr.fmagic != FMAGIC)  return BAD_STATE;

    /* allocate and read the text and data segments *.
    i = filhdr.tsize + filhdr.dsize;
    desc->base = (char *) malloc(i);
    if (desc->base == NULL) return NO_MEMORY;
    desc->torigin = (int) desc->base;
    desc->dorigin = desc->torigin + filhdr.tsize;
    xread(desc->base, i, bfile);

 /* read symbol table */
    for (pos=0; pos<filhdr.ssize; )
      {
	struct vaxsym insym;
	struct ExtSymbol *sym;

	xread(&insym, sizeof(insym), bfile);
	ByteSwapLongInPlace(&insym.svalue, sizeof(long));	
	sym = (struct ExtSymbol*)
	    malloc(sizeof(struct ExtSymbol) + insym.slength);
	sym->s.stype = insym.stype;
	sym->s.slength = insym.slength;
	sym->s.svalue = insym.svalue;
	sym->next = NULL;
	xread((char*)(sym+1), insym.slength, bfile);
	pos += sizeof(struct vaxsym) + insym.slength;

	switch (sym->s.stype & ~EXTERN)
	  {
	    case TEXT:
	    case DATA:
	    case BSS: /* ??? */
		sym->s.svalue += desc->torigin;
		break;
	    case UNDEF:
		if (p->s.svalue != 0)  /* actually a BSS symbol */
		  {
		    p->s.stype = BSS;
		    value = blocation;
		    blocation += p->s.svalue;
		    p->s.svalue = value;
		  }
		else		/* a true external reference */
		  {
		    value = LinkFind(sym);
		    if (value) p->s.svalue = value;
		    else /* record the unsatisfied symbol in newtable */
		      {
			if (unsat < maxsymbols)
			  {
			    uname = (char *) malloc(p->s.slength + 1);
			    strcpy(uname, p->name);
			    newtable[unsat].name = uname;
			    newtable[unsat].type = LINKUNSAT;
			  }
			unsat++;
		      }
		  }
		break;
	  }  /* end switch */
	p++;
      }

  /* relocation */
    for (segment = 0; segment <= 1; segment ++)
      {
	long rcount;			/* number of relocation commands */
	long txtsize;
	char *segstart = desc->torigin;
        switch (segment)
	  {
	    case 0:
		txtsize = filhdr->tsize;
		rcount = filhdr->rtsize/sizeof rel;
		break;
	    case 1:
		segstart += filhdr->tsize;
		txtsize = filhdr->dsize;
		rcount = filhdr->rdsize/sizeof rel;
	  }   
	while(rcount--)			/* for each relocation command */
	{
		xread(&rel, sizeof rel, bfile);
		rel.rsymbol = shortreverse(rel.rsymbol);
		rel.rpos = reverse(rel.rpos);
		switch(rel.rsegment)
		{
		case REXT:
			offs = (*desc->symLookup)(desc, rel.rsymbol);
			break;
		case RTEXT:
		case RDATA:
		case RBSS:
			offs = desc->torigin;
			break;
		}
		if (rel.rdisp)
			offs -= rel.rpos + (int) segstart;
		if (rel.rpos > txtsize) return BAD_ARGS;

		switch(rel.rsize)
		  {
		    case RBYTE:
			*(segstart + rel.rpos) += offs;
			break;
		    case RWORD:
			* (short *)(segstart + rel.rpos) += offs;
			break;
		    case RLONG:
			* (long *)(segstart + rel.rpos) += offs;
			break;
		    default:
			return BAD_ARGS;
		  }
	}
      }
    return OK;
  }

Pointer
StdSymLookup(desc, i)
    struct BinDescriptor *desc;
  { register struct ExtSymbol *sym = desc->symList;
    for (;; sym = sym->next)
      {
	if (sym == NULL || sym->symNo > i)
	    return 0;
	if (sym->symNo == i)
	  {
	    if ((sym->s.stype & ~EXTERN) == UNDEF)
	      {
		fprintf(stderr, "linkload: Undefined symbol: %s\n", sym+1);
		fflush(stderr);
	      }
	    return sym->s.svalue;;
	  }
      }
  }

InitBinDescriptor(desc)
    struct BinDescriptor *desc;
  {
    desc->symLookup = StdSymLookup;
    desc->symList = NULL;
  }
