h57330
s 01424/00000/00000
d D 1.1 82/08/02 15:37:19 cecily 1 0
e
u
U
t
T
I 1
/*
 *  link editor
 */


#include <signal.h>
#include "sys/types.h"
#include "sys/stat.h"
#include "a.out.h"
#include "ar.h"

/*	Layout of a.out file :
 *
 *	header of 8 words	magic number 405, 407, 410, 411
 *				text size	)
 *				data size	) in bytes but even
 *				bss size	)
 *				symbol table size
 *				entry point
 *				stack size
 *				flag set if no relocation
 *
 *
 *	header:		0
 *	text:		16
 *	data:		16+textsize
 *	relocation:	16+textsize+datasize
 *	symbol table:	16+2*(textsize+datasize) or 16+textsize+datasize
 *
#ifdef	interdata
 *	Alter 16 to 32 above for interdata
#endif
 */
#define TRUE	1
#define FALSE	0

#ifdef	interdata

#define	add(a, b, s)	((unsigned)a + (((unsigned)b + 3) & ~3))
#define half(a)		((unsigned)(a) >> 1)

#endif


#define	OMAGIC	A_MAGIC4
#define	FMAGIC	A_MAGIC1
#define	NMAGIC	A_MAGIC2
#define	IMAGIC	A_MAGIC3

#define	N_COMM	05	/* internal use only */

		/* relocation bit values, should be in a.out.h */
#define	RN_ABS	00
#define	RTEXT	02
#define	RDATA	04
#define	RBSS	06
#define	REXT	010
#define	RHI	01	/*** high order half-word of 32 bit word ***/

#define NOVLY	16
#define	RELFLG	01
#define	NROUT	400		/*** changed from 256 for ingres ***/
#define	NSYM	1103
#define	NSYMPR	1000

char	premeof[] = "Premature EOF";
char	goodnm[] = "__.SYMDEF";

/* table of contents stuff */
#define TABSZ	700
struct tab
{	char cname[8];
	long cloc;
} tab[TABSZ];
int tnum;


/* overlay management */
int	vindex;
struct overlay {
	int	argsav;
	int	symsav;
	struct liblist	*libsav;
	char	*vname;
	int	ctsav, cdsav, cbsav;
	int	offt, offd, offb, offs;
} vnodes[NOVLY];

/* input management */
struct page {
	int	nuser;
	int	bno;
	int	nibuf;
	unsigned short	buff[256];		/***/
} page[2];

struct {
	int	nuser;
	int	bno;
} fpage;

struct stream {
	unsigned short	*ptr;		/***/
	int	bno;
	int	nibuf;
	int	size;
	struct page	*pno;
};

struct stream text;
struct stream reloc;

struct ar_hdr archdr;

struct exec filhdr;


/* one entry for each archive member referenced;
 * set in first pass; needs restoring for overlays
 */
struct liblist {
	long	loc;
};

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


/* symbol management */
struct local {
	int locindex;		/* index to symbol in file */
	struct nlist *locsymbol;	/* ptr to symbol table */
};

struct nlist	cursym;			/* current symbol */
struct nlist	symtab[NSYM];		/* actual symbols */
struct nlist	**symhash[NSYM];	/* ptr to hash table entry */
struct nlist	*lastsym;		/* last symbol entered */
int	symindex;		/* next available symbol table entry */
struct nlist	*hshtab[NSYM+2];	/* hash table for symbols */
struct local	local[NSYMPR];

/* internal symbols */
struct nlist	*p_etext;
struct nlist	*p_edata;
struct nlist	*p_end;
struct nlist	*entrypt;

int	trace;
/* flags */
int	aflag;		/*** load all an archive ***/
int	kflag;		/*** stack size set by user ***/
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	nflag;		/* pure procedure */
int	Oflag;		/* set magic # to 0405 (overlay) */
int	dflag;		/* define common even with rflag */
int	iflag;		/* I/D space separated */
int	vflag;		/* overlays used */

char	*ofilename = "a.out";		/***/
int	infil;
char	*filname;

/* cumulative sizes set in pass 1 */
int	tsize;
int	dsize;
int	bsize;
int	ssize;
int	ksize;		/*** stack size ***/

/* symbol relocation; both passes */
int	ctrel;
int	cdrel;
int	cbrel;

int	errlev;
int	delarg	= 4;
char	tfname[] = "/tmp/ldaXXXXX";


/* output management */
struct buf {
	int	fildes;
	int	nleft;
	unsigned short	*xnext;
	unsigned short	iobuf[256];
};
struct buf	toutb;
struct buf	doutb;
struct buf	troutb;
struct buf	droutb;
struct buf	soutb;

struct nlist	**lookup();
struct nlist	**slookup();
struct nlist	*lookloc();

delexit()
{
	unlink("l.out");
	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;
	int found; 
	int vscan; 
	char save;

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

	/* scan files once to find symdefs */
	for (c=1; c<argc; c++) {
		if (trace) printf("%s:\n", *p);
		filname = 0;
		ap = *p++;

		if (*ap == '-') {
			for (i=1; ap[i]; i++) {
			switch (ap[i]) {
			case 'a':		/*** load all an archive ***/
				aflag = TRUE;
				continue;

			case 'o':
				if (++c >= argc)
					error(2, "Bad output file");
				ofilename = *p++;
				continue;

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

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

/***/			case 'k':
/***/				kflag = TRUE;
/***/				if (++c >= argc)
/***/					error(2, "-k: arg missing");
/***/				ksize = atoi(*p++);
/***/				continue;

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

			case 'l':
				aflag = 0;		/*** no -a of a sys lib ***/
				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 'd':
				dflag++;
				continue;

			case 'i':
#ifdef	pdp11
				iflag++;
#else
				nflag++;
#endif
				continue;

			case 'O':
				Oflag++;
				continue;

			case 't':
				trace++;
				continue;

			default:  {
/***/				register char *badflag = "? is a bad flag";
/***/
/***/				*badflag = ap[i];
/***/				error(2, badflag);
/***/				}
			} /*endsw*/
			break;
			} /*endfor*/
		} else {
			load1arg(ap);
			aflag = 0;	/*** -a applies to next load only ***/
		}
	}
	endload(argc, argv);
}

/* used after pass 1 */
int	nsym;
int	torigin;
int	dorigin;
int	borigin;

endload(argc, argv)
int argc; 
char **argv;
{
	register int c, i; 
	int dnum;
	register char *ap, **p;
	filname = 0;
	middle();
	setupout();
	p = argv+1;
	libp = liblist;
	for (c=1; c<argc; c++) {
		ap = *p++;
		if (trace) printf("%s:\n", ap);
		if (*ap == '-') {
			for (i=1; ap[i]; i++) {
			switch (ap[i]) {
			case 'D':
				for (dnum = atoi(*p); dorigin<dnum; dorigin += 2) {
					putw(0, &doutb);
					if (rflag)
						putw(0, &droutb);
				}
			case 'u':
			case 'e':
			case 'o':
			case 'v':
			case 'k':			/***/
				++c; 
				++p;

			default:
				continue;

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

record(c, nam)
int c; 
char *nam;
{
	register struct overlay *v;

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

restore(vscan)
int vscan;
{
	register struct overlay *v;
	register int saved;

	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; 
	ssize = v->offs;
	saved = v->symsav;
	while (symindex>saved)
		*symhash[--symindex]=0;
}

/* scan file to find defined symbols */
load1arg(acp)
char *acp;
{
	register char *cp;
	long nloc;

	cp = acp;
	switch ( getfile(cp)) {
	case 0:
		load1(0, 0L);
		break;

	/* regular archive */
	case 1:
		nloc = sizeof(int) >> 1;		/***/
		while ( step(nloc) )
			nloc += (archdr.ar_size + sizeof(archdr) + 1) >> 1;
		break;

	/* table of contents */
	case 2:
		tnum = archdr.ar_size / sizeof(struct tab);
		if (tnum >= TABSZ) {
			error(2, "fast load buffer too small");
		}
		lseek(infil, (long)(sizeof(filhdr.a_magic)+sizeof(archdr)), 0);
		read(infil, (char *)tab, tnum * sizeof(struct tab));
		while (ldrand());
		libp->loc = -1;
		libp++;
		break;
	/* out of date table of contents */
	case 3:
		error(0, "out of date (warning)");
/***/		for(nloc = (sizeof(int)>>1)+((archdr.ar_size+sizeof(archdr)+1) >> 1); step(nloc);
			nloc += (archdr.ar_size + sizeof(archdr) + 1) >> 1);
		break;
	}
	close(infil);
}

step(nloc)
long nloc;
{
	dseek(&text, nloc, sizeof archdr);
	if (text.size <= 0) {
		libp->loc = -1;
		libp++;
		return(0);
	}
	mget((int *)&archdr, sizeof archdr);
	if (load1(1, nloc + (sizeof archdr) / 2)) {
		libp->loc = nloc;
		libp++;
	}
	return(1);
}

ldrand()
{
	int i;
	struct nlist *sp, **pp;
	struct liblist *oldp = libp;
	for(i = 0; i<tnum; i++) {
		if ((pp = slookup(tab[i].cname)) == 0)
			continue;
		sp = *pp;
		if (sp->n_type != N_EXT+N_UNDF)
			continue;
		step(tab[i].cloc >> 1);
	}
	return(oldp != libp);
}

#ifdef	pdp11
add(a,b,s)
int a, b;
char *s;
{
	long r;

	r = (long)(unsigned)a + (unsigned)b;
	if (r >= 0200000)
		error(1,s);
	return(r);
}
#endif


/* single file or archive member */
load1(libflg, loc)
long loc;
{
	register struct nlist *sp;
	int savindex;
	int ndef, nloc, type, mtype;

	readhdr(loc);
	ctrel = tsize;
	cdrel += dsize;
	cbrel += bsize;
	ndef = 0;
	nloc = sizeof cursym;
	savindex = symindex;
	if ((filhdr.a_flag&RELFLG)==1) {
		error(1, "No relocation bits");
		return(0);
	}
	loc += (sizeof filhdr)/2 + filhdr.a_text + filhdr.a_data;
	dseek(&text, loc, filhdr.a_syms);
	while (text.size > 0) {
		mget((int *)&cursym, sizeof cursym);
		type = cursym.n_type;
		if (Sflag) {
			mtype = type&037;
			if (mtype==1 || mtype>4) {
				continue;
			}
		}
		if ((type&N_EXT)==0) {
			if (Xflag==0 || cursym.n_name[0]!='L')
				nloc += 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 || aflag) {		/***/
		tsize = add(tsize,filhdr.a_text,"text overflow");
		dsize = add(dsize,filhdr.a_data,"data overflow");
		bsize = add(bsize,filhdr.a_bss,"bss overflow");
		ssize = add(ssize,nloc,"symbol table overflow");
/***/		if (!kflag && filhdr.a_stack > ksize)
/***/			ksize = filhdr.a_stack;
		return(1);
	}
	/*
	 * No symbols defined by this library member.
	 * Rip out the hash table entries and reset the symbol table.
	 */
	while (symindex>savindex)
		*symhash[--symindex]=0;
	return(0);
}

middle()
{
	register struct nlist *sp, *symp;
	register t, csize;
	int nund, corigin;

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

#ifdef	pdp11
	p_etext = *slookup("_etext");
	p_edata = *slookup("_edata");
	p_end = *slookup("_end");
#endif
#ifdef	interdata
	p_etext = *slookup("etext");
	p_edata = *slookup("edata");
	p_end = *slookup("end");
#endif
	/*
	 * If there are any undefined symbols, save the relocation bits.
	 */
	symp = &symtab[symindex];
	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)
		nflag = sflag = iflag = 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 (sp->n_type==N_EXT+N_UNDF && (t = sp->n_value)!=0) {
				t = (t+1) & ~01;
				sp->n_value = csize;
				sp->n_type = N_EXT+N_COMM;
				csize = add(csize, t, "bss overflow");
			}
	}
	/*
	 * Now set symbols to their final value
	 */
#ifdef	pdp11
	if (nflag || iflag)
		tsize = (tsize + 077) & ~077;
	dorigin = tsize;
	if (nflag)
		dorigin = (tsize+017777) & ~017777;
	if (iflag)
		dorigin = 0;
#endif
#ifdef	interdata
	if (nflag) {
		tsize = (tsize + 0xff) & ~0xff;
		dorigin = (tsize + 0xffff) & ~0xffff;
	} else
		dorigin = tsize;
#endif
	corigin = dorigin + dsize;
	borigin = corigin + csize;
	nund = 0;
	for (sp = symtab; sp<symp; sp++) switch (sp->n_type) {
	case N_EXT+N_UNDF:
		errlev |= 01;
		if (arflag==0 && 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+N_COMM:
		sp->n_type = N_EXT+N_BSS;
		sp->n_value += corigin;
		continue;
	}
	if (sflag || xflag)
		ssize = 0;
	bsize = add(bsize, csize, "bss overflow");
	nsym = ssize / (sizeof cursym);
}

ldrsym(asp, val, type)
struct nlist *asp;
{
	register struct nlist *sp;

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

setupout()
{
	tcreat(&toutb, 0);
	mktemp(tfname);
	tcreat(&doutb, 1);
	if (sflag==0 || xflag==0)
		tcreat(&soutb, 1);
	if (rflag) {
		tcreat(&troutb, 1);
		tcreat(&droutb, 1);
	}
	filhdr.a_magic = (Oflag ? OMAGIC :( iflag ? IMAGIC : ( nflag ? NMAGIC : FMAGIC )));
	filhdr.a_text = tsize;
	filhdr.a_data = dsize;
	filhdr.a_bss = bsize;
	filhdr.a_syms = sflag? 0: (ssize + (sizeof cursym)*symindex);
	if (entrypt) {
		if (entrypt->n_type!=N_EXT+N_TEXT)
			error(1, "Entry point not in text");
		else
			filhdr.a_entry = entrypt->n_value | 01;
	} else
		filhdr.a_entry=0;
#ifdef	pdp11
	filhdr.pad = 0;
#endif
#ifdef	interdata
	filhdr.a_stack = ksize;
#endif
	filhdr.a_flag = (rflag==0);
	mput(&toutb, (int *)&filhdr, sizeof filhdr);
}

tcreat(buf, tempflg)
struct buf *buf;
{
	register int ufd; 
	char *nam;
	nam = (tempflg ? tfname : "l.out");		/***/
	if ((ufd = creat(nam, 0666)) < 0)
							/***
		error(2, tempflg?"cannot create temp":"cannot create output");
							***/
		error(2, tempflg?"cannot create temp":"cannot create l.out");
	close(ufd); 
	buf->fildes = open(nam, 2);
	if (tempflg)
		unlink(tfname);
	buf->nleft = sizeof(buf->iobuf)/sizeof(int);
	buf->xnext = buf->iobuf;
}

load2arg(acp)
char *acp;
{
	register char *cp;
	register struct 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, sizeof archdr);
			mget((int *)&archdr, sizeof archdr);
			mkfsym(archdr.ar_name);
			load2(lp->loc + (sizeof archdr) / 2);
		}
		libp = ++lp;
	}
	close(infil);
}

load2(loc)
long loc;
{
	register struct nlist *sp;
	register struct 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)/2;
	dseek(&text, loc + filhdr.a_text + filhdr.a_data, filhdr.a_syms);
	while (text.size > 0) {
		symno++;
		mget((int *)&cursym, sizeof cursym);
		symreloc();
		type = cursym.n_type;
		if (Sflag) {
			mtype = type&037;
			if (mtype==1 || mtype>4) continue;
		}
		if ((type&N_EXT) == 0) {
			if (!sflag&&!xflag&&(!Xflag||cursym.n_name[0]!='L'))
				mput(&soutb, (int *)&cursym, sizeof cursym);
			continue;
		}
		if ((sp = *lookup()) == 0)
			error(2, "internal error: symbol not found");
		if (cursym.n_type == N_EXT+N_UNDF) {
			if (lp >= &local[NSYMPR])
				error(2, "Local symbol overflow");
			lp->locindex = symno;
			lp++->locsymbol = sp;
			continue;
		}
		if (cursym.n_type!=sp->n_type || cursym.n_value!=sp->n_value) {
			printf("%.8s: ", cursym.n_name);
			error(1, "Multiply defined");
		}
	}
	dseek(&text, loc, filhdr.a_text);
	dseek(&reloc, loc + half(filhdr.a_text + filhdr.a_data), filhdr.a_text);
	load2td(lp, ctrel, &toutb, &troutb);
#ifdef	interdata
	if (filhdr.a_text & 03) {
		putw(0, &toutb);
		if (rflag)
			putw(0, &troutb);
	}
#endif
	dseek(&text, loc+half(filhdr.a_text), filhdr.a_data);
	dseek(&reloc, loc+filhdr.a_text+half(filhdr.a_data), filhdr.a_data);
	load2td(lp, cdrel, &doutb, &droutb);
#ifdef	interdata
	if (filhdr.a_data&03) {
		putw(0, &doutb);
		if (rflag)
			putw(0, &droutb);
	}
	torigin = add(torigin, filhdr.a_text, "");
	dorigin = add(dorigin, filhdr.a_data, "");
	borigin = add(borigin, filhdr.a_bss,  "");
#endif
#ifdef	pdp11
	torigin += filhdr;
	dorigin += filhdr.a_data;
	borigin += filhdr.a_bss;
#endif
}

load2td(lp, creloc, b1, b2)
struct local *lp;
struct buf *b1, *b2;
{
	register r, t;
	register struct nlist *sp;
#ifdef	interdata
	register r1, h;
#endif

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

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

		/* next relocation word */
		if (--reloc.size <= 0) {
			if (reloc.size < 0)
				error(2, "Relocation error");
			reloc.size++;
			r = get(&reloc);
		} else if (--reloc.nibuf < 0) {
			reloc.nibuf++;
			reloc.size++;
			r = get(&reloc);
		} else
			r = *reloc.ptr++;

#ifdef	interdata
		if (r & RHI) {
			/*
			 * a fullword address value, the second relocation
			 * halfword means a 24 bit address if zero, or a
			 * 32 bit address if non-zero
			 */
			if ((r1 = get(&reloc)) == 0)
				h = t & 0xff00;
			t = (t << 16) | get(&text);
		}
#endif

		switch (r&016) {

		case RTEXT:
			t += ctrel;
			break;

		case RDATA:
			t += cdrel;
			break;

		case RBSS:
			t += cbrel;
			break;

		case REXT:
			sp = lookloc(lp, r);
			if (sp->n_type==N_EXT+N_UNDF) {
				r = (r&01) + ((nsym+(sp-symtab))<<4) + REXT;
				break;
			}
			t += sp->n_value;
			r = (r&01) + ((sp->n_type-(N_EXT+N_ABS))<<1);
			break;
		}
#ifdef	pdp11
		if (r&01)
			t -= creloc;
#endif
#ifdef	interdata
		if (r&01) {
			if (r1)
				putw(t>>16, b1);
			else
				putw(((t>>16)&0xff)|h, b1);
			if (rflag)
				putw(r, b2);
			r = r1;
			t &= 0xffff;
		}
#endif
		putw(t, b1);
		if (rflag)
			putw(r, b2);
	}
}

finishout()
{
#ifdef	pdp11
	register n, *p;
#endif
#ifdef	interdata
	register n;
	register unsigned short *p;
#endif

	if (nflag||iflag) {
		n = torigin;
#ifdef	pdp11
		while (n&077) {
#endif
#ifdef	interdata
		while (n & 0377) {
#endif
			n += sizeof(short);
			putw(0, &toutb);
			if (rflag)
				putw(0, &troutb);
		}
	}
	copy(&doutb);
	if (rflag) {
		copy(&troutb);
		copy(&droutb);
	}
	if (sflag==0) {
		if (xflag==0)
			copy(&soutb);
		for (p = (unsigned short *)symtab; p < (unsigned short *)&symtab[symindex];)
			putw(*p++, &toutb);
	}
	flush(&toutb);
	close(toutb.fildes);
/***/	unlink(ofilename);
/***/	if (link("l.out", ofilename) < 0)
/***/		error(2, "cannot create output");
	delarg = errlev;
	delexit();
}

copy(buf)
struct buf *buf;
{
#ifdef	pdp11
	register f, *p, n;
#endif
#ifdef	interdata
	register f, n;
	register unsigned short *p;
#endif

	flush(buf);
	lseek(f = buf->fildes, (long)0, 0);
	while ((n = read(f, (char *)doutb.iobuf, sizeof(doutb.iobuf))) > 1) {
		n >>= 1;
		p = (unsigned short *)doutb.iobuf;
		do
			putw(*p++, &toutb);
		while (--n);
	}
	close(f);
}

mkfsym(s)
char *s;
{

	if (sflag || xflag)
		return;
	cp8c(s, cursym.n_name);
	cursym.n_type = 037;
	cursym.n_value = torigin;
	mput(&soutb, (unsigned short *)&cursym, sizeof cursym);
}

mget(loc, n)
register unsigned short *loc;
register n;
{
	register unsigned short *p;

	n >>= 1;
	if ((text.nibuf -= n) >= 0) {
		if ((text.size -= n) > 0) {
			p = text.ptr;
			do
				*loc++ = *p++;
			while (--n);
			text.ptr = p;
			return;
		} else
			text.size += n;
	}
	text.nibuf += n;
	do {
		*loc++ = get(&text);
	} 
	while (--n);
}

mput(buf, loc, n)
struct buf *buf; 
register unsigned short *loc;
register n;
{

	n >>= 1;
	do {
		putw(*loc++, buf);
	} 
	while (--n);
}

dseek(sp, aloc, s)
long aloc;
register struct stream *sp;
{
	register struct page *p;
	register long b, o;
	register n;

	b = aloc >> 8;
	o = aloc & 0377;
	--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, (aloc & ~0377L) << 1, 0);
#ifdef	pdp11
			if ((n = read(infil, (char *)p->buff, 512)>>1) < 0)
#endif
#ifdef	interdata
			n = read(infil, (char *)p->buff, 512);
			if (n > 0)
				n >>= 1;
			else
#endif
				n = 0;
			p->nibuf = n;
	} else
		error(2, "No pages");
	++p->nuser;
	sp->bno = b;
	sp->pno = p;
	sp->ptr = p->buff + o;
	if (s != -1)
		sp->size = half(s);
	if ((sp->nibuf = p->nibuf-o) <= 0)
		sp->size = 0;
}

#ifdef	pdp11
half(i)
{
	return((i>>1)&077777);
}
#endif

get(sp)
register struct stream *sp;
{
	if (--sp->nibuf < 0) {
		dseek(sp, (long)(sp->bno + 1) << 8, -1);
		--sp->nibuf;
	}
	if (--sp->size <= 0) {
		if (sp->size < 0)
			error(2, premeof);
		++fpage.nuser;
		--sp->pno->nuser;
		sp->pno = (struct page *)&fpage;
	}
	return(*sp->ptr++);
}

getfile(cp)
register char *cp;
{
	register int c;
	struct stat x;

	infil = -1;
	archdr.ar_name[0] = '\0';
	filname = cp;
	if (cp[0]=='-' && cp[1]=='l') {
		if(cp[2] == '\0')
			cp = "-la";
		filname = "/usr/lib/libxxxxxxxxxxxxxxx";
		for(c=0; cp[c+2]; c++)
			filname[c+12] = cp[c+2];
		filname[c+12] = '.';
		filname[c+13] = 'a';
		filname[c+14] = '\0';
		if ((infil = open(filname+4, 0)) >= 0) {
			filname += 4;
		}
	}
	if (infil == -1 && (infil = open(filname, 0)) < 0)
		error(2, "cannot open");
	page[0].bno = page[1].bno = -1;
	page[0].nuser = page[1].nuser = 0;
	text.pno = reloc.pno = (struct page *)&fpage;
	fpage.nuser = 2;
	dseek(&text, 0L, sizeof(int));
	if (text.size <= 0)
		error(2, premeof);
#ifdef	pdp11
	if(get(&text) != ARMAG)
#endif
#ifdef	interdata
	if (get(&text) != (ARMAG>>16) || get(&text) != (ARMAG&0xffff))
#endif
		return(0);	/* regular file */
	dseek(&text, (long)sizeof(int)>>1, sizeof archdr);	/* [half-]word addressing */
	if(text.size <= 0)
		return(1);	/* regular archive */
	mget((unsigned short *)&archdr, sizeof archdr);
	if(strncmp(archdr.ar_name, goodnm, 14) != 0)
		return(1);	/* regular archive */
	else {
		fstat(infil, &x);
		if(x.st_mtime > archdr.ar_date)
		{
			return(3);
		}
		else return(2);
	}
}

struct nlist **lookup()
{
	int i; 
	int clash;
	register struct nlist **hp;
	register char *cp, *cp1;

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

struct nlist **slookup(s)
char *s;
{
	cp8c(s, cursym.n_name);
	cursym.n_type = N_EXT+N_UNDF;
	cursym.n_value = 0;
	return(lookup());
}

enter(hp)
register struct nlist **hp;
{
	register struct nlist *sp;

	if (*hp==0) {
		if (symindex>=NSYM)
			error(2, "Symbol table overflow");
		symhash[symindex] = hp;
		*hp = lastsym = sp = &symtab[symindex++];
		cp8c(cursym.n_name, sp->n_name);
		sp->n_type = cursym.n_type;
		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 > 1)
		delexit();
	errlev = n;
}

struct nlist *
lookloc(lp, r)
register struct local *lp;
{
	register struct local *clp;
	register sn;

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

readhdr(loc)
long loc;
{
	register st, sd;

	dseek(&text, loc, sizeof filhdr);
	mget((int *)&filhdr, sizeof filhdr);
	if (filhdr.a_magic != FMAGIC)
		error(2, "Bad format");
	st = (filhdr.a_text+01) & ~01;
	filhdr.a_text = st;
	cdrel = -st;
	sd = (filhdr.a_data+01) & ~01;
	cbrel = - (st+sd);
	filhdr.a_bss = (filhdr.a_bss+01) & ~01;
}

cp8c(from, to)
char *from, *to;
{
	register char *f, *t, *te;

	f = from;
	t = to;
	te = t+8;
	while ((*t++ = *f++) && t<te);
	while (t<te)
		*t++ = 0;
}

eq(s1, s2)
char *s1; 
char *s2;
{
	while (*s1==*s2++)
		if ((*s1++)==0)
			return(TRUE);
	return(FALSE);
}

putw(w, b)
register struct buf *b;
{
	*(b->xnext)++ = w;
	if (--b->nleft <= 0)
		flush(b);
}

flush(b)
register struct buf *b;
{
	register n;

	if ((n = (char *)b->xnext - (char *)b->iobuf) > 0)
		if (write(b->fildes, (char *)b->iobuf, n) != n)
			error(2, "output error");
	b->xnext = b->iobuf;
	b->nleft = sizeof(b->iobuf)/sizeof(unsigned short);
}

/***
 ***	atoi, with modifier
 ***/
atoi(p)
register char *p;
{
	register i = 0;
	register char c;

	while ((c = *p++) >= '0' && c <= '9')
		i = i * 10 + c - '0';
	switch (c) {
		case 'k': i <<= 10; break;
		case 'b': i <<= 9; break;
		case 'p': i <<= 8; break;
		case 'w': i <<= 2; break;
		case 'M': i <<= 20; break;
	}
	return (i);
}
E 1
