Xinu7/src/cmd/ld11.c.RAW

/*
 *  link editor - Source modified to be just a PDP 11 cross-loader 
 *                for the VAX using the portable archive format               
 *                October 1981          Steve Salisbury
 */

/*
 *  This file produces:
 *	ld11	for the vax
 *
 *  Modified to take advantage of library indices (see xlib.c).
 *
 *  Eric Cooper
 *  July, 1980
 */

#include <stdio.h>
#include <signal.h>
#include <ar.h>
#include <sys/types.h>
#include <sys/stat.h>


/* various machine dependent definitions */
#include "{Xinu-directory}/include/pdp11/pagsiz.h"
#include "{Xinu-directory}/include/pdp11/a.out.h"
#define LIBNAME  "{Xinu-directory}/lib/lib"


/* high and low words of longs are reversed between vax and pdp11 */
typedef struct	{
	short hiword;
	short loword;
} longword;

fixl(p)
register longword *p;
{
	register short t;

	t=p->hiword; p->hiword=p->loword; p->loword=t;
}

long atoi();

typedef	char *STRING;
typedef	short BOOL;
#define TRUE	1
#define FALSE	0

#define	COMM	05	/* internal use only */

/*
 * for rounding to word boundaries
 */
#define	HW	0
#define	FW	01
#define	DW	03

#define TMASK	(~0)
#define	RELFLG	01

#define NOVLY	16

#define	NROUT	500
#define	NSYM	1103
#define	NSYMPR	1000

char	premeof[] = "Premature EOF";

typedef struct {
	long	loc;
} LIBLIST;

/* overlay management */
int	vindex;
typedef struct {
	int	argsav;
	int	symsav;
	LIBLIST	*libsav;
	STRING	vname;
	long	ctsav, cdsav, cbsav;
	long	offt, offd, offb, offtr, offdr, offs;
} OVERLAY;
OVERLAY	vnodes[NOVLY];

/* input management */
typedef struct {
	short	*fakeptr;
	int	bno;
	int	nibuf;
	int	nuser;
	char	buff[BSIZE];
} PAGE;

PAGE	page[2];

struct {
	short	*fakeptr;
	int	bno;
	int	nibuf;
	int	nuser;
} fpage;

typedef struct {
	char	*ptr;
	int	bno;
	int	nibuf;
	long	size;
	long	pos;
	PAGE	*pno;
} STREAM;

STREAM text;
STREAM reloc;

struct	ar_hdr archdr;
long           archdr_size;
long           archdr_date;

struct	exec filhdr;

/*
 * one entry for each archive member referenced;
 * set in first pass; needs restoring for overlays
 */

LIBLIST	liblist[NROUT];
LIBLIST	*libp = liblist;

/* for fast loading of libraries */
#define ARXFILNAME      "__.SYMDEF     "  

typedef struct {
	char x_name[8];
	long x_loc;	/* byte addr of module within library */
} XDEF;
XDEF	*xdefs;		/* sorted table of defined symbols in library */
int	nxdefs;

/* symbol management */
typedef struct nlist SYMBOL;

typedef struct {
	int locindex;		/* index to symbol in file */
	SYMBOL *locsymbol;	/* ptr to symbol table */
} LOCAL;

SYMBOL	cursym;			/* current symbol */
SYMBOL	*symtab;		/* actual symbols */
SYMBOL	*lastsym;		/* last symbol entered */
SYMBOL	*nextsym;		/* next available symbol table entry */
int	nsym;
SYMBOL	*hshtab[NSYM+2];	/* hash table for symbols */
LOCAL	*local;
short	indxtab[NSYM];
#define symhash(p)	(indxtab[(p)-symtab])

/* internal symbols */
SYMBOL	*p_etext;
SYMBOL	*p_edata;
SYMBOL	*p_end;
SYMBOL	*entrypt;

/* flags */
int	xflag;		/* discard local symbols */
int	Xflag;		/* discard locals starting with 'L' */
int	Sflag;		/* discard all except locals and globals */
int	rflag;		/* preserve relocation bits, don't define common */
int	arflag;		/* original copy of rflag */
int	sflag;		/* discard all symbols */
int	dflag;		/* define common even with rflag */
int	iflag;		/* I/D space separated */
int	nflag = 0;	/* pure procedure */
BOOL	vflag;		/* overlays used */
int	Oflag;		/* set magic # to 405 (overlay) */

int	ofilfnd;
char	*ofilename	= "l.out";
int	infil;
char	*filname;

long	textbase;
/* cumulative sizes set in pass 1 */
long	tsize;
long	dsize;
long	bsize;
long	trsize;
long	drsize;
long	ssize;

/* symbol relocation; both passes */
long	ctrel;
long	cdrel;
long	cbrel;
long	ctorel;
long	cdorel;
long	cborel;

int	errlev;
int	delarg	= 4;


FILE	*tout;
FILE	*dout;
char	*doutn	= "";
FILE	*trout;
char	*troutn	= "";
FILE	*drout;
char	*droutn	= "";
FILE	*sout;
char	*soutn	= "";

char	*mktemp();
char 	get();
char	getb();
long	get3();
long	getl();
SYMBOL	**lookup();
FILE	*tcreat();
long	round();
SYMBOL	**slookup();
SYMBOL	*lookloc();
long	step();
XDEF	*bsearch();

delexit()
{
	unlink("l.out");
	if (*doutn)
		unlink(doutn);
	if (*troutn)
		unlink(troutn);
	if (*droutn)
		unlink(droutn);
	if (*soutn)
		unlink(soutn);
	if (delarg==0)
		chmod(ofilename, 0777 &~ umask(0));
	exit(delarg);
}

main(argc, argv)
char **argv;
{
	register int c, i; 
	int num;
	register char *ap, **p;
	BOOL found; 
	int vscan;
	char save;

	if (signal(SIGINT, SIG_IGN) != SIG_IGN)
		signal(SIGINT, delexit);
	if (argc == 1)
		exit(4);
	p = argv+1;

	nextsym = symtab = (SYMBOL *)calloc(NSYM, sizeof(SYMBOL));
	if (symtab == NULL)
		error(1, "Memory overflow");

	/* scan files once to find symdefs */
	for (c=1; c<argc; c++) {
		filname = 0;
		ap = *p++;

		if (*ap == '-') {
			for (i=1; ap[i]; i++) {
			switch (ap[i]) {
			case 'o':
				if (++c >= argc)
					error(1, "Bad output file");
				ofilename = *p++;
				ofilfnd++;
				continue;

			case 'u':
			case 'e':
				if (++c >= argc)
					error(1, "Bad 'use' or 'entry'");
				enter(slookup(*p++));
				if (ap[i]=='e')
					entrypt = lastsym;
				continue;

			case 'D':
				if (++c >= argc)
					error(1, "-D: arg missing");
				num = atoi(*p++);
				if (dsize>num)
					error(1, "-D: too small");
				dsize = num;
				continue;

			case 'T':
				if (++c >= argc)
					error(1, "-T: arg missing");
				if (tsize!=0)
					error(1, "-T: too late, some text already loaded");
				textbase = atoi(*p++);
				continue;

			case 'l':
				save = ap[--i]; 
				ap[i]='-';
				load1arg(&ap[i]); 
				ap[i]=save;
				break;

			case 'x':
				xflag++;
				continue;

			case 'X':
				Xflag++;
				continue;

			case 'S':
				Sflag++; 
				continue;

			case 'r':
				rflag++;
				arflag++;
				continue;

			case 's':
				sflag++;
				xflag++;
				continue;

			case 'n':
				nflag++;
				continue;

			case 'N':
				nflag = 0;
				continue;

			case 'd':
				dflag++;
				continue;

			case 'i':
				iflag++;
				continue;

			case 'v':
				if (++c >= argc)
					error(1, "-v: arg missing");
				vflag = TRUE;
				vscan = vindex; 
				found = FALSE;
				while (--vscan>=0)
					if (strcmp(vnodes[vscan].vname, *p) == 0) {
						found = TRUE;
						break;
					}
				if (found) {
					endload(c, argv);
					restore(vscan);
				} else
					record(c, *p);
				p++;
				continue;

			case 'O':
				Oflag++;
				continue;
			default:
				error(1, "bad flag");
			} /*endsw*/
			break;
			} /*endfor*/
		} else
			load1arg(ap);
	}
	endload(argc, argv);
	exit(0);
}

/* used after pass 1 */
long	torigin;
long	dorigin;
long	borigin;
long	database;

endload(argc, argv)
int argc; 
char **argv;
{
	register int c, i; 
	long dnum;
	register char *ap, **p;

/*
	realloc(symtab, nextsym-symtab);
*/
	filname = 0;
	middle();
	setupout();
	local = (LOCAL *)malloc(NSYMPR*sizeof(*local));
	if (local == NULL)
		error(1,"Memory overflow");
	p = argv+1;
	libp = liblist;
	for (c=1; c<argc; c++) {
		ap = *p++;
		if (*ap == '-') {
			for (i=1; ap[i]; i++) {
			switch (ap[i]) {
			case 'D':
				for (dnum = atoi(*p); dorigin<dnum; dorigin++) {
					putc(0, dout);
					if (rflag)
						putc(0, drout);
				}
			case 'T':
			case 'u':
			case 'e':
			case 'o':
			case 'v':
				++c; 
				++p;

			default:
				continue;

			case 'l':
				ap[--i]='-'; 
				load2arg(&ap[i]);
				break;
			} /*endsw*/
			break;
			} /*endfor*/
		} else
			load2arg(ap);
	}
	finishout();
}

record(c, nam)
int c; 
STRING nam;
{
	register OVERLAY *v;

	v = &vnodes[vindex++];
	v->argsav = c;
	v->symsav = nextsym-symtab;
	v->libsav = libp;
	v->vname = nam;
	v->offt = tsize; 
	v->offd = dsize; 
	v->offb = bsize; 
	v->offtr = trsize;
	v->offdr = drsize;
	v->offs = ssize;
	v->ctsav = ctrel; 
	v->cdsav = cdrel; 
	v->cbsav = cbrel;
}

restore(vscan)
int vscan;
{
	register OVERLAY *v;
	register SYMBOL *saved,*sp;

	v = &vnodes[vscan];
	vindex = vscan+1;
	libp = v->libsav;
	ctrel = v->ctsav; 
	cdrel = v->cdsav; 
	cbrel = v->cbsav;
	tsize = v->offt; 
	dsize = v->offd; 
	bsize = v->offb; 
	trsize = v->offtr;
	drsize = v->offdr;
	ssize = v->offs;
	saved = symtab + v->symsav;
	sp = nextsym;
	while (sp>saved)
		hshtab[symhash(--sp)]=0;
	nextsym = saved;
}

/* scan file to find defined symbols */
load1arg(cp)
register char *cp;
{
	long loc;
	int olderrlev;

	switch (getfile(cp)) {

	case 0:			/* regular object file */
		load1(0, 0L);
		break;

	case 1:			/* regular archive */
                loc = SARMAG;     
	regular:
		while (loc = step(loc))
			;
		libp++->loc = -1;
		break;

	case 2:			/* archive with index */
		ldxar(symtab);
		libp++->loc = -1;
		break;

	case 3:			/* out of date index */
		olderrlev = errlev;
		error(0, "index out of date (warning)");
		errlev = olderrlev;
                loc = sizeof(int) + sizeof(archdr) + round(archdr_size, 1);
		goto regular;
	}
	close(infil);
}

long
step(loc)
long loc;
{
	dseek(&text, loc, (long)sizeof(archdr));
	if (text.size <= 0)
		return (0);
	mget(&archdr, sizeof(archdr), &text);
        archdr_size = atoi(archdr.ar_size);
        archdr_date = atoi(archdr.ar_date);
#ifdef DEBUG
	printf("%.14s, %d bytes, %s", 
		archdr.ar_name,
                archdr_size,
                ctime(&archdr_date));
#endif
	if (load1(1, loc+sizeof(archdr))) {
		libp++->loc = loc;
		if(libp >= liblist + NROUT)
			error(1,"liblist overflow");
	}
        return (loc + round(archdr_size, 1) + sizeof(archdr));
}

/*
 * Load an indexed archive.
 * Try to resolve all undefined externals above symptr.
 */

ldxar(symptr)
SYMBOL *symptr;
{
	register SYMBOL *sp;
	register XDEF *xp;
	SYMBOL *before;

	for (sp = symptr; sp < nextsym; sp++)
		if (sp->n_type == N_EXT+N_UNDF) {
			xp = bsearch(sp->n_name);
			if (xp == (XDEF *)0)
				continue;
			before = nextsym;
			step(xp->x_loc);
			if (nextsym != before)
				ldxar(before);
		}
}

XDEF *
bsearch(name)
STRING name;
{
	register bottom = 1;
	register top = nxdefs;
	register mid;
	register XDEF *p;
	short n;

#ifdef DEBUG
	printf("%.8s ", name);
#endif
	while (top >= bottom) {
		mid = (bottom + top) / 2;
		p = &xdefs[mid-1];
		n = strcmpn(name, p->x_name, 8);
		if (n > 0)
			bottom = mid + 1;
		else if (n < 0)
			top = mid - 1;
		else
			return (p);
	}
#ifdef DEBUG
	printf("not found\n");
#endif
	return ((XDEF *) 0);
}

/* single file or archive member */
load1(libflg, loc)
long loc;
{
	register SYMBOL *sp;
	SYMBOL *savnext;
	int ndef, nlocal, type, mtype;

	readhdr(loc);
	ctrel = tsize;
	cdrel += dsize;
	cbrel += bsize;
	ndef = 0;
	nlocal = sizeof(cursym);
	savnext = nextsym;
	if ((filhdr.a_flag&RELFLG) == 1) {
		error(0, "No relocation bits");
		return(0);
	}
	loc += sizeof(filhdr) + 2*(filhdr.a_text + filhdr.a_data);
	dseek(&text, loc, (long)filhdr.a_syms);
	while (text.size > 0) {
		symget(&cursym, &text);
		type = cursym.n_type;
		if (Sflag) {
			mtype = type&N_TYPE;
			if (mtype == 1 || mtype > 4)
				continue;
		}
		if ((type&N_EXT)==0) {
			if (Xflag==0 || cursym.n_name[0]!='L' || type&STABTYPES)
				nlocal += sizeof cursym;
			continue;
		}
		symreloc();
		if (enter(lookup()))
			continue;
		if ((sp = lastsym)->n_type != N_EXT+N_UNDF)
			continue;
		if (cursym.n_type == N_EXT+N_UNDF) {
			if (cursym.n_value > sp->n_value)
				sp->n_value = cursym.n_value;
			continue;
		}
		if (sp->n_value != 0 && cursym.n_type == N_EXT+N_TEXT)
			continue;
		ndef++;
		sp->n_type = cursym.n_type;
		sp->n_value = cursym.n_value;
	}
	if (libflg==0 || ndef) {
		tsize += filhdr.a_text;
		dsize += round(filhdr.a_data, FW);
		bsize += round(filhdr.a_bss, FW);
		ssize += nlocal;
		return(1);
	}
	/*
	 * No symbols defined by this library member.
	 * Rip out the hash table entries and reset the symbol table.
	 */
	while (nextsym>savnext)
		hshtab[symhash(--nextsym)]=0;
	return(0);
}

middle()
{
	register SYMBOL *sp, *symp;
	long csize, t, corigin, ocsize;
	int nund, rnd;
	char s;

	torigin = 0; 
	dorigin = 0; 
	borigin = 0;

	p_etext = *slookup("_etext");
	p_edata = *slookup("_edata");
	p_end = *slookup("_end");
	/*
	 * If there are any undefined symbols, save the relocation bits.
	 */
	symp = nextsym;
	if (rflag==0) {
		for (sp = symtab; sp<symp; sp++)
			if (sp->n_type==N_EXT+N_UNDF && sp->n_value==0
				&& sp!=p_end && sp!=p_edata && sp!=p_etext
			) {
				rflag++;
				dflag = 0;
				break;
			}
	}
	if (rflag) 
		sflag = iflag = 0;
	if (rflag) 
		nflag = Oflag = 0;
	/*
	 * Assign common locations.
	 */
	csize = 0;
	if (dflag || rflag==0) {
		ldrsym(p_etext, tsize, N_EXT+N_TEXT);
		ldrsym(p_edata, dsize, N_EXT+N_DATA);
		ldrsym(p_end, bsize, N_EXT+N_BSS);
		for (sp = symtab; sp<symp; sp++) {
			if ((s=sp->n_type)==N_EXT+N_UNDF && (t = sp->n_value)!=0) {
				rnd = HW;
				csize = round(csize, rnd);
				sp->n_value = csize;
				sp->n_type = N_EXT+COMM;
				ocsize = csize;	
				csize += t;
			}
			if (((s&TMASK) == N_EXT+N_UNDF) && (s & STABTYPES)) {
				sp->n_value = ocsize;
				sp->n_type = (s & STABTYPES) | (N_EXT+COMM);
			}
		}
	}
	/*
	 * Now set symbols to their final value
	 */
	csize = round(csize, FW);
	torigin = textbase;
	t = (nflag || iflag) ? round(tsize, 077) : tsize;
	database = (iflag ? 0 : (nflag ? round(t, PAGRND) : t));
	dorigin = database;
	corigin = dorigin + dsize;
	borigin = corigin + csize;
/*
*/
	cdorel = 0;
	cborel = dsize+csize;
	nund = 0;
	for (sp = symtab; sp<symp; sp++) switch (sp->n_type & TMASK) {
	case N_EXT+N_UNDF:
		if (!arflag)
			errlev |= 01;
		if ((arflag==0 || dflag) && sp->n_value==0) {
			if (nund==0)
				printf("Undefined:\n");
			nund++;
			printf("%.8s\n", sp->n_name);
		}
		continue;

	case N_EXT+N_ABS:
	default:
		continue;

	case N_EXT+N_TEXT:
		sp->n_value += torigin;
		continue;

	case N_EXT+N_DATA:
		sp->n_value += dorigin;
		continue;

	case N_EXT+N_BSS:
		sp->n_value += borigin;
		continue;

	case N_EXT+COMM:
		sp->n_type = (sp->n_type & STABTYPES) | (N_EXT+N_BSS);
		sp->n_value += corigin;
		continue;
	}
	if (!arflag && nund)	/* don't bother with second pass */
		delexit();
	if (sflag || xflag)
		ssize = 0;
	bsize += csize;
	nsym = ssize / sizeof(SYMBOL);
}

ldrsym(asp, val, type)
long val;
SYMBOL *asp;
{
	register SYMBOL *sp;

	if ((sp = asp) == 0)
		return;
	if (sp->n_type != N_EXT+N_UNDF || sp->n_value) {
		printf("%.8s: ", sp->n_name);
		error(0, "Multiply defined (internal)");
		return;
	}
	sp->n_type = type;
	sp->n_value = val;
}

extern char _sibuf[BUFSIZ]; /* the space is forced upon us; might as well use it */

setupout()
{
	int bss;

	tout = fopen(ofilename, "w");
	if (tout==NULL)
		error(1, "cannot create output");
	setbuf(tout,_sibuf);
	dout = tcreat(&doutn, "/tmp/ldaaXXXXX");
	if (sflag==0 || xflag==0)
		sout = tcreat(&soutn, "/tmp/ldbaXXXXX");
	if (rflag) {
		trout = tcreat(&troutn, "/tmp/ldcaXXXXX");
		drout = tcreat(&droutn, "/tmp/lddaXXXXX");
	}
	filhdr.a_magic = (Oflag ? A_MAGIC4 :
				(iflag ? A_MAGIC3 :
					(nflag ? A_MAGIC2 : A_MAGIC1)));
	filhdr.a_text = (nflag || iflag ? round(tsize, 077) : tsize);
	filhdr.a_data = dsize;
	filhdr.a_bss = bsize;
	filhdr.a_unused = 0;
	filhdr.a_flag = (rflag==0);
	filhdr.a_syms = sflag? 0: (ssize + (sizeof cursym)*(nextsym-symtab));
	if (entrypt) {
		if (entrypt->n_type!=N_EXT+N_TEXT)
			error(0, "Entry point not in text");
		else
			filhdr.a_entry = entrypt->n_value | 01;
	} else
		filhdr.a_entry=0;
	fwrite(&filhdr, sizeof(filhdr), 1, tout);
}

FILE *
tcreat(namep, name)
char **namep, *name;
{
	register FILE *fp;
	register char *tnm;

	tnm = mktemp(name);
	if ((fp = fopen(tnm, "w")) == NULL)
		error(1, "Cannot create temp file");
	chmod(tnm, 0600);
	*namep = tnm;
	return(fp);
}

load2arg(acp)
char *acp;
{
	register char *cp;
	register LIBLIST *lp;

	cp = acp;
	if (getfile(cp) == 0) {
		while (*cp)
			cp++;
		while (cp >= acp && *--cp != '/');
		mkfsym(++cp);
		load2(0L);
	} else {	/* scan archive members referenced */
		for (lp = libp; lp->loc != -1; lp++) {
			dseek(&text, lp->loc, (long)sizeof(archdr));
			mget(&archdr, sizeof(archdr), &text);
			mkfsym(archdr.ar_name);
			load2(lp->loc + (long)sizeof(archdr));
		}
		libp = ++lp;
	}
	close(infil);
}

load2(loc)
long loc;
{
	register SYMBOL *sp;
	register LOCAL *lp;
	register int symno;
	int type, mtype;

	readhdr(loc);
	ctrel = torigin;
	cdrel += dorigin;
	cbrel += borigin;
	/*
	 * Reread the symbol table, recording the numbering
	 * of symbols for fixing external references.
	 */
	lp = local;
	symno = -1;
	loc += sizeof(filhdr);
	dseek(&text, loc+2*(filhdr.a_text+filhdr.a_data), (long)filhdr.a_syms);
	while (text.size > 0) {
		symno++;
		symget(&cursym, &text);
		symreloc();
		type = cursym.n_type;
		if (Sflag) {
			mtype = type&N_TYPE;
			if (mtype == 1 || mtype > 4)
				continue;
		}
		if ((type&N_EXT) == 0) {
			if (!sflag&&!xflag&&
				(!Xflag||cursym.n_name[0]!='L'||type&STABTYPES))
				symwrite(&cursym, 1, sout);
			continue;
		}
		if ((sp = *lookup()) == 0)
			error(1, "internal error: symbol not found");
		if (cursym.n_type == N_EXT+N_UNDF) {
			if (lp >= local+NSYMPR)
				error(1, "Local symbol overflow");
			lp->locindex = symno;
			lp++->locsymbol = sp;
			continue;
		}
		if (cursym.n_type & STABTYPES) {
			continue;
		}
		if (cursym.n_type!=sp->n_type || cursym.n_value!=sp->n_value) {
			printf("%.8s: ", cursym.n_name);
			error(0, "Multiply defined");
		}
	}
	dseek(&text, loc, (long)filhdr.a_text);
	dseek(&reloc, loc+filhdr.a_text+filhdr.a_data, (long)filhdr.a_text);
	load2td(lp, ctrel, tout, trout);
	dseek(&text, loc+filhdr.a_text, (long)filhdr.a_data);
	dseek(&reloc, loc+2*filhdr.a_text+filhdr.a_data, (long)filhdr.a_data);
	load2td(lp, cdrel, dout, drout);
	while (filhdr.a_data&FW) {
		putc(0, dout); filhdr.a_data++;
	}
	torigin += filhdr.a_text;
	dorigin += filhdr.a_data;
	borigin += filhdr.a_bss;
	cdorel += filhdr.a_data;
	cborel += filhdr.a_bss;
}

load2td(lp, creloc, b1, b2)
LOCAL *lp;
long creloc;
FILE *b1, *b2;
{
	register short r;
	register short t;
	register SYMBOL *sp;
	register char *cp;
	short rr, tt;

	for (;;) {
		/*
		 * The pickup code is copied from "get" for speed.
		 */

		/* next text or data word */
		cp = (char *)&tt;
		/* first byte */
		if (--text.size <= 0) {
			if (text.size < 0)
				break;
			text.size++;
			*cp++ = get(&text);
		} else if (--text.nibuf < 0) {
			text.nibuf++;
			text.size++;
			*cp++ = get(&text);
		} else
			*cp++ = *text.ptr++;
		/* second byte */
		if (--text.size <= 0) {
			if (text.size < 0)
				break;
			text.size++;
			*cp++ = get(&text);
		} else if (--text.nibuf < 0) {
			text.nibuf++;
			text.size++;
			*cp++ = get(&text);
		} else
			*cp++ = *text.ptr++;
		t = tt;

		/* next relocation word */
		cp = (char *)&rr;
		/* first byte */
		if (--reloc.size <= 0) {
			if (reloc.size < 0)
				error(1, "Relocation error");
			reloc.size++;
			*cp++ = get(&reloc);
		} else if (--reloc.nibuf < 0) {
			reloc.nibuf++;
			reloc.size++;
			*cp++ = get(&reloc);
		} else
			*cp++ = *reloc.ptr++;
		/* second byte */
		if (--reloc.size <= 0) {
			if (reloc.size < 0)
				error(1, "Relocation error");
			reloc.size++;
			*cp++ = get(&reloc);
		} else if (--reloc.nibuf < 0) {
			reloc.nibuf++;
			reloc.size++;
			*cp++ = get(&reloc);
		} else
			*cp++ = *reloc.ptr++;
		r = rr;

		switch (r&016) {

		case R_TEXT:
			t += ctrel;
			break;

		case R_DATA:
			t += cdrel;
			break;

		case R_BSS:
			t += cbrel;
			break;

		case R_UNDF:
			sp = lookloc(lp, (int)r);
			if (sp->n_type==N_EXT+N_UNDF) {
				r = (r&01) + ((nsym+(sp-symtab))<<4) + R_UNDF;
				break;
			}
			t += sp->n_value;
			r = (r&01) + ((sp->n_type-(N_EXT+N_ABS))<<1);
			break;
		}
		if (r&01)
			t -= creloc;
		puts(t, b1);
		if (rflag)
			puts(r, b2);
	}
}

finishout()
{
	if (nflag || iflag)
		while (tsize&077) {
			putc(0, tout); tsize++;
			if (rflag)
				putc(0, trout);
		}
	fclose(dout);
	copy(doutn);
	if (rflag) {
		fclose(trout);
		copy(troutn);
		fclose(drout);
		copy(droutn);
	}
	if (sflag==0) {
		if (xflag==0) {
			fclose(sout);
			copy(soutn);
		}
		symwrite(symtab, nextsym-symtab, tout);
	}
	fclose(tout);
	if (!ofilfnd) {
		unlink("a.out");
		link("l.out", "a.out");
		ofilename = "a.out";
	}
	delarg = errlev;
	delexit();
}

copy(np)
char *np;
{
	register c;
	register FILE *fp;

	if ((fp = fopen(np, "r")) == NULL)
		error(1, "cannot recopy output");
	while ((c = getc(fp)) != EOF)
		putc(c, tout);
	fclose(fp);
}

mkfsym(s)
char *s;
{

	if (sflag || xflag)
		return;
	strcpyn(cursym.n_name, s, 8);
	cursym.n_type = N_TYPE;
	cursym.n_value = torigin;
	symwrite(&cursym, 1, sout);
}

mget(loc, n, sp)
register STREAM *sp;
register char *loc;
{
	register char *p;

	if ((sp->nibuf -= n) >= 0) {
		if ((sp->size -= n) > 0) {
			p = sp->ptr;
			sp->pos += n;
			do
				*loc++ = *p++;
			while (--n);
			sp->ptr = p;
			return;
		} else
			sp->size += n;
	}
	sp->nibuf += n;
	do {
		*loc++ = get(sp);
	} while (--n);
}

symwrite(sp, n, f)
SYMBOL *sp;
FILE *f;
{
	fwrite(sp, sizeof(*symtab), n, f);
}

symget(sp,f) 
SYMBOL *sp; 
STREAM *f; 
{
	mget(sp,sizeof(*sp),f);
}

dseek(sp, loc, s)
register STREAM *sp;
long loc, s;
{
	register PAGE *p;
	register b, o;
	int n;

	b = loc>>BSHIFT;
	o = loc&BMASK;
	if (o&01)
		error(1, "loader error; odd offset");
	--sp->pno->nuser;
	if ((p = &page[0])->bno!=b && (p = &page[1])->bno!=b)
		if (p->nuser==0 || (p = &page[0])->nuser==0) {
			if (page[0].nuser==0 && page[1].nuser==0)
				if (page[0].bno < page[1].bno)
					p = &page[0];
			p->bno = b;
			lseek(infil, loc & ~(long)BMASK, 0);
			if ((n = read(infil, p->buff, sizeof(p->buff))) < 0)
				n = 0;
			p->nibuf = n;
	} else
		error(1, "No pages");
	++p->nuser;
	sp->bno = b;
	sp->pno = p;
	if (s != -1) {sp->size = s; sp->pos = 0;}
	sp->ptr = p->buff + o;
	if ((sp->nibuf = p->nibuf-o) <= 0)
		sp->size = 0;
}

char
get(asp)
STREAM *asp;
{
	register STREAM *sp;

	sp = asp;
	if ((sp->nibuf -= sizeof(char)) < 0) {
		dseek(sp, ((long)(sp->bno+1)<<BSHIFT), (long)-1);
		sp->nibuf -= sizeof(char);
	}
	if ((sp->size -= sizeof(char)) <= 0) {
		if (sp->size < 0)
			error(1, premeof);
		++fpage.nuser;
		--sp->pno->nuser;
		sp->pno = (PAGE *)&fpage;
	}
	sp->pos += sizeof(char);
	return (*sp->ptr++);
}

getfile(acp)
register STRING acp;
{
        char arcmag[SARMAG];
	struct stat st;
	static char buf[256];

	infil = -1;
	archdr.ar_name[0] = '\0';
	if (acp[0] == '-' && acp[1] == 'l') {
		if(acp[2] == '\0')
			acp = "-la";
                sprintf(buf, "%s%s.a", LIBNAME, &acp[2]);
		filname = buf;
/****************************************************************/
/* This trick checks for "/lib/lib..." before "/v1/lib/lib..." */
/* but we aren't using those libraries so we don't do this.     */ 
/****************************************************************/
/*              if ((infil = open(filname+4, 0)) != -1)         */
/*                      filname += 4;                           */
/****************************************************************/
	}
	else
		filname = acp;
	if (infil == -1 && (infil = open(filname, 0)) < 0)
		error(1, "cannot open");
#ifdef DEBUG
	printf("getfile(%s)\n", filname);
#endif
	page[0].bno = page[1].bno = -1;
	page[0].nuser = page[1].nuser = 0;
	text.pno = reloc.pno = (PAGE *)&fpage;
	fpage.nuser = 2;
        dseek(&text, 0L, (long)SARMAG);
	if (text.size <= 0)
		error(1, premeof);
        mget(arcmag, SARMAG, &text);
        if (strcmpn(arcmag,ARMAG,SARMAG))
		return (0);
        dseek(&text, (long)SARMAG, (long)sizeof(archdr));
	if (text.size <= 0)
		error(1, premeof);
	mget(&archdr, sizeof(archdr), &text);
        archdr_size = atoi(archdr.ar_size);
        archdr_date = atoi(archdr.ar_date);
        if (strcmpn(archdr.ar_name, ARXFILNAME, 14) != 0)
		return (1);
	fstat(infil, &st);
        if (st.st_mtime > archdr_date)
		return (3);
        xdefs = (XDEF *)malloc(archdr_size);
	if (xdefs == NULL)
		error(1, "Memory overflow");
        dseek(&text, (long)SARMAG+sizeof(archdr), archdr_size);
	if (text.size <= 0)
		error(1, premeof);
        mget(xdefs, archdr_size, &text);
        nxdefs = archdr_size / sizeof(XDEF);
	return (2);
}

SYMBOL **lookup()
{
	register int i; 
	register SYMBOL **hp;
	register char *cp;

	i = 0;
	
	for (cp = cursym.n_name; cp < &cursym.n_name[8];)
		i = (i<<1) + *cp++;
	hp = &hshtab[(i&077777)%NSYM+2];
	while (*hp != 0)
		if (strcmpn((*hp)->n_name, cursym.n_name, 8) != 0) {
			if (++hp >= &hshtab[NSYM+2])
				hp = hshtab;
		}
		else
			break;
	return(hp);
}

SYMBOL **slookup(s)
char *s;
{
	strcpyn(cursym.n_name, s, 8);
	cursym.n_type = N_EXT+N_UNDF;
	cursym.n_value = 0;
	return(lookup());
}

enter(hp)
register SYMBOL **hp;
{
	register SYMBOL *sp;

	if (*hp==0) {
		if ((nextsym-symtab)>=NSYM)
			error(1, "Symbol table overflow");
		*hp = lastsym = sp = nextsym++;
		strcpyn(sp->n_name, cursym.n_name, 8);
		sp->n_type = cursym.n_type;
		symhash(sp) = hp-hshtab;
		sp->n_value = cursym.n_value;
		return(1);
	} else {
		lastsym = *hp;
		return(0);
	}
}

symreloc()
{
	switch (cursym.n_type) {

	case N_TEXT:
	case N_EXT+N_TEXT:
		cursym.n_value += ctrel;
		return;

	case N_DATA:
	case N_EXT+N_DATA:
		cursym.n_value += cdrel;
		return;

	case N_BSS:
	case N_EXT+N_BSS:
		cursym.n_value += cbrel;
		return;

	case N_EXT+N_UNDF:
		return;
	}
	if (cursym.n_type&N_EXT)
		cursym.n_type = N_EXT+N_ABS;
}

error(n, s)
char *s;
{
	if (errlev==0)
		printf("ld:");
	if (filname) {
		printf("%s", filname);
                if (archdr.ar_name[0])
			printf("(%.14s)", archdr.ar_name);
		printf(": ");
	}
	printf("%s\n", s);
	if (n)
		delexit();
	errlev = 2;
}

SYMBOL *
lookloc(lp, r)
register LOCAL *lp;
{
	register LOCAL *clp;
	register sn;

	sn = (r>>4) & 07777;
	for (clp = local; clp<lp; clp++)
		if (clp->locindex == sn)
			return(clp->locsymbol);
	error(1, "Local symbol botch");
}

readhdr(loc)
long loc;
{
	long *p; int i;

	dseek(&text, loc, (long)sizeof(filhdr));
	mget(&filhdr, sizeof(filhdr), &text);
	if (filhdr.a_magic!=A_MAGIC1 && filhdr.a_magic!=A_MAGIC2 &&
		filhdr.a_magic!=A_MAGIC3 && filhdr.a_magic!=A_MAGIC4)
			error(1,"Bad magic number");
	if (filhdr.a_text&01 || filhdr.a_data&01) {
		printf("tsize=%X  dsize=%X\n",filhdr.a_text,filhdr.a_data);
		error(1, "Text/data size odd");
	}
	if (filhdr.a_magic == A_MAGIC2) {
		cdrel = -round(filhdr.a_text, PAGRND);
		cbrel = cdrel - filhdr.a_data;
	} else if (filhdr.a_magic == A_MAGIC1) {
		cdrel = -filhdr.a_text;
		cbrel = cdrel - filhdr.a_data;
	} else
		error(1, "Bad format");
}

long
round(v, r)
long v;
unsigned r;
{
	v += r;
	v &= ~(long)r;
	return(v);
}

writel(p, n, f)
long *p;
FILE *f;
{
	while (n--)
		putl(*p++, f);
}

putl(w, f)
FILE *f;
long w; 
{
	fixl(&w);
	fwrite(&w,sizeof(long),1,f);
}

long
getl(sp) 
STREAM *sp; 
{
	long t; 
	
	mget(&t,4,sp);
	fixl(&t);
	return(t);
}

puts(w, f)
FILE *f; 
short w; 
{
	fwrite(&w,sizeof(short),1,f);
}

char
*gets(sp)
STREAM *sp; 
{
	short t; mget(&t,2,sp); return((char *)t);
}

#ifdef DEBUG
prsymtab()
{
	register SYMBOL *sp;

	for (sp = symtab; sp < nextsym; sp++)
		printf("%.8s\n", sp->n_name);
}
#endif