#ifndef lint
static        char sccsid[] = "@(#)rtsetup.c 1.1 94/10/31"; /* from UCB 5.4 85/11/26 */
#endif not lint

#include <sys/types.h>
#include <a.out.h>
#include <link.h>
#include "dynamic.h"
#include "reloc_info.h"
#include <sys/param.h>
#include <stdio.h>
#include <sys/stat.h>
#include <sys/file.h>
#include <sys/mman.h>

char *mymalloc();
extern int picflag;

fs_init(rt, sb)
	register struct runtime *rt;
	register int sb;		/* no of symbols for runtime linking */
{
	register int i;
	long buckets;

	/* buckets = sb/4; */
	buckets = (sb < 4 ? sb : sb/4);

	rt->hp = (struct fshash *) mymalloc((buckets+sb)*sizeof(struct fshash));
	rt->sp = (struct nlist *) mymalloc(sb * sizeof (struct nlist));
	rt->hp_last = rt->hp + (buckets+sb);
	rt->sp_last = rt->sp + sb;
	rt->fsalloc = lalign(rt->fsalloc);
	rt->fsstr = (char *) calloc(rt->fsalloc, 1);
	if (rt->hp == 0 || rt->sp == 0 || rt->fsstr == 0)
		error(1, "out of memory in buildfs");
	rt->spp = rt->sp;
	for (i = 0; i < buckets; i++) {
		(rt->hp+i)->fssymbno = -1;	/* mark it empty with -1 */
		(rt->hp+i)->next = 0;		/* init with null index ptr */
	}
	rt->hp_ind = buckets;
	rt->buckets = buckets;
}

/* 
 * this routine does a bunch of initializations for runtime linking
 */
rt_init(rt)
	register struct runtime *rt;
{
	register int i;

	i = totalsymb();
	fs_init(rt, i);
	rt->rl = calcreloc();
	if ((rt->rp = (struct relocation_info *) calloc (rt->rl,
	    sizeof (struct relocation_info))) == 0)
		error(1, "out of memory for dynamic relocation");
	rt->rpp = rt->rp;
}

dp_init(rt)
	register struct runtime *rt;
{
	register struct dynamic *dp = rt->dp;
	register int i;

	i = totalsymb();
	dp->rs = rt->rl * sizeof (struct relocation_info);
	dp->ss = i * sizeof (struct nlist);
	dp->sts = rt->fsalloc;
	dp->hs = rt->hp_ind * sizeof (struct fshash);
	
	/*
	 * jump table entries needed to be on an even boundary
	 */
	dp->libstr = lalign(dp->libstr);
}

/*
 * initialize for data and jump linkage. 
 */
dj_init(rt, slp, flag)
	register struct runtime *rt;
	register struct dslot *slp;
	register int flag; /* if nonzero then set it up for runtime linking */
{
	register struct dynamic *dp = rt->dp;

	/*
	 * for runtime linking we need one extra slot in the data linkage
	 * table to store the address of symbol __DYNAMIC. This is done
	 * so that the runtime linker can find its own dynamic structure
	 * before it can relocate itself.
	 * We also need an extra slot in the jump linkage table which
	 * contained the direct jump to the runtime binder (all the
	 * jump entries do a pc relative jump to this slot).
	 */
	if (slp->ds || slp->ss || slp->js) {
		slp->ds += 1;
		slp->js += 1;
	}

	/*
	 * Calculate the offset into the GOT storage area for the
	 * GOT symbol itself.  In some implementations, the GOT may
	 * be accessed by a signed displacement, this fact is adjusted
	 * for here.
	 */
	dp->got_off = slp->ds + slp->ss;
	if ((slp->ds + slp->ss) > MAX_GOT_SIZE)  {
		if (picflag == 1)
			error(1, 
			    "GLOBAL_OFFSET_TABLE overflown: need to use -PIC");
		dp->got_off = 0;
	} else {
		if (dp->got_off > (MAX_GOT_SIZE / 2))
			dp->got_off = ((MAX_GOT_SIZE / 2) - dp->got_off) *
					sizeof (int);
		else
			dp->got_off = 0;
	}

	/* 
	 * be careful here: the number of bytes for jump slots 
	 * are machine dependent. +++++++
	 */
	dp->ds = (slp->ds + slp->ss) * sizeof(int);
	dp->js = slp->js * sizeof(struct jbind);		
	if ((rt->dt = (int *) calloc(dp->ds, sizeof(int))) == 0)
		error(1, "can't allocate space for data linkage table");
	rt->dtp = rt->dt;
	rt->dto = dp->got_off;
	if (dp->js != 0) {
		if ((rt->jt = (struct jbind  *) calloc(dp->js,
		    sizeof(struct jbind))) == 0)
			error(1, "can't allocate space for jump linkage table");
		if (flag) {
			rt->jtp = rt->jt + 1;
			rt->jto = sizeof(struct jbind);
#if	TARGET==SUN4
			setupjs(rt->jt, 0);
#endif
#if	TARGET==SUN3 || TARGET==SUN2
			rt->jt->code = JUMP;
#endif
	    	} else {
			rt->jtp = rt->jt;
			rt->jto = 0;
		}
	}
}

init_lkd(lkdp, rt, ts, db)
	register struct link_dynamic *lkdp;
	register struct runtime *rt;
	int ts;					/* text size */
	int db;					/* database */
{
	int off;
	int i;
	int j;
	int k = 0;
	char *cp = rt->libname;

	lkdp->ld_version = 2;
	lkdp->v2->ld_got = db + sizeof(struct link_dynamic) + 
	    sizeof(struct ld_debug) + sizeof(struct link_dynamic_2);
	lkdp->v2->ld_plt = lkdp->v2->ld_got + rt->dp->ds;
	lkdp->v2->ld_got += abs(rt->dp->got_off);
	lkdp->v2->ld_plt_sz = rt->dp->js;

	lkdp->v2->ld_rel = ts;
	lkdp->v2->ld_hash = lkdp->v2->ld_rel + rt->dp->rs;
	lkdp->v2->ld_stab = lkdp->v2->ld_hash + rt->dp->hs;
	lkdp->v2->ld_symbols = lkdp->v2->ld_stab + rt->dp->ss;
	if (rt->searchpath)
		lkdp->v2->ld_rules = lkdp->v2->ld_symbols + rt->fsalloc;
	lkdp->v2->ld_symb_size = rt->fsalloc;
	i = rt->dp->lib;
	if (i == 0) 
		lkdp->v2->ld_need = 0;
	else {
		if (lkdp->v2->ld_rules)
			lkdp->v2->ld_need = lkdp->v2->ld_rules +
			    lalign(rt->spthlen);
		else
			lkdp->v2->ld_need = lkdp->v2->ld_symbols + rt->fsalloc;
		off = lkdp->v2->ld_need + (i * sizeof(struct link_object));
		j = 0;
		while (i > 0) {
			rt->lko[j].lo_name = off + k;
			k += strlen(cp) + 1;
			cp = rt->libname + k;
			i--;
			if (i == 0)
				rt->lko[j].lo_next = 0;
			else
				rt->lko[j].lo_next = lkdp->v2->ld_need + 
				    ((j+1)*sizeof(struct link_object));
			j++;
		}
	}
}

#define skipblank(cp)	while ( *cp == ' '  || *cp == '\n' ) cp++;
#define GLOB_SYMB	"object global"

struct ssymbol *ssymbol_p;

/*
 * this routine read in symbols from a file of the following format:
 * 	object global s1, s2, s3, ...., sn;
 * any reference to theses symbols will then be reduced to a relative
 * relocation.
 */
int
getsymb(filename)
	char		*filename;
{
	int 		fd;
	struct stat 	sb;
	char		*buf;
	char		*cp;
	char		*cp1;
	struct ssymbol	*nsp;
	struct ssymbol	*csp;
	char		sc;


	/*
	 * open file
	 */
	if ((fd = open(filename, O_RDONLY)) == -1)
		error(1, "open symbol file %s failed", filename);
	if (fstat(fd, &sb) == -1)
		error(1, "fstat symbol file %s failed", filename);
	buf = mmap(0, sb.st_size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);

	/*
	 * look for key word "object global"
	 */
	cp = buf;
	while ( *cp == ' ' ) cp++;
	if (!strcmp(cp, GLOB_SYMB))
		error(1, "missing key word (object global) in symbol file");
	cp += strlen(GLOB_SYMB);
	skipblank(cp);

	/*
	 * loop until ";", the marker for the end of the list is found.
	 */
	csp = ssymbol_p;
	cp1 = cp;
	for (;;) {
		if (*cp != ',' && *cp != ';') {
			cp++;
			continue;
		}
			
		sc = *cp;
		*cp++ = '\0';
		nsp = (struct ssymbol *) malloc( sizeof(struct ssymbol));
		nsp->ssp = cp1;
		nsp->ss_next = 0;
		if (csp)
			csp->ss_next = nsp; 
		else
			ssymbol_p = nsp;
		csp = nsp;
		skipblank(cp);
		cp1 = cp;
		if (sc == ';')
			break;
	} 
}
