32V/usr/src/cmd/ld.c

Compare this file to the similar file:
Show the results in this format:

#
char LD[] = "@(#)ld.c 1.10 78/12/07 15:34:58";	/* sccs ident */
/*
 *  link editor for VAX
 */

/*	layout of a.out file:
 *
 *	header of 8 words	magic number 0410:
					data starts at 1st 0777
					boundary above text
				magic number 0407:
					data starts immediately after
					text
 *				text size	)
 *				data size	) in bytes
 *				bss size	)
 *				symbol table size
 *				entry point
 *				size of text relocation info
 *				size of data relocation info
 *
 *  'segment'   origin   comments
 *	header:		0
 *	text:		32       0 padded to multiple of 4 bytes
 *	data:		32+textsize     0 padded to multiple of 4 bytes
 *	text relocation:	32+textsize+datasize
 *	data relocation:	32+textsize+datasize+textrelocationsize
 *	symbol table:	32+textsize+datasize+textrelocationsize+datarelocationsize
 *
 */
#include <signal.h>
#include <stdio.h>
#include <ar.h>
#include <a.out.h>

struct {short hiword; short loword;}; /* stupid fp-11 */
fixl(p) register long *p;{
	register short t;
	t=p->hiword; p->hiword=p->loword; p->loword=t;
}

writel(p,n,f) long *p; FILE *f; {
#ifdef vax
	fwrite(p,sizeof(*p),n,f);
#else
	while (n--) {
		fwrite(&(*p).loword,2,1,f);
		fwrite(&(*p).hiword,2,1,f);
		p++;
	}
#endif
}

long htoi(p) register char *p; {/* hex to integer conversion */
register long n = 0;
while (*p) {
	n <<= 4;
	if 		(*p<='9' && *p>='0') n += *p - '0';
	else if (*p<='f' && *p>='a') n += *p -'a' +10;
	else if (*p<='F' && *p>='A') n += *p -'A' +10;
	p++;
}
return(n);
}

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

#define	OMAGIC	0407
#define	NMAGIC	0410

/*
 * Symbol types
 */
#define	UNDEF	0x0
#define	ABS	0x2
#define	TEXT	0x4
#define	DATA	0x6
#define	BSS	0x8
#define	DATAO	0xA
#define	BSSO	0xC
#define	TEXTO	0xE
#define	ABSO	0x10

#define	COMM	0x12	/* for internal use only */

#define	EXTERN	0x1
#define	TYPE	0x1E
#define STABTYPS 0xE0
/*
 * address reference types
 */
#define PCREL	1
#define LEN1	0
#define LEN2	2
#define LEN4	4

#define	HW	01
#define	FW	03
#define	DW	07

#define	PAGRND	0777

#define	TYPMASK	0x1E
#define	TYMASK	(0x1E)
#define TMASK	0x1F

#define	RABS	(ABS)
#define	RTEXT	TEXT
#define	RDATA	DATA
#define	RBSS	BSS
#define	RDATAO	DATAO
#define	RBSSO	BSSO
#define	RTEXTO	TEXTO
#define	RABSO	ABSO
#define	REXT	(01<<3)
#define	ROFF	(02<<3)
#define	REFMASK	0x7

#define NOVLY	1
#define	RELFLG	01
#define	NROUT	256
#define	NSYM	1103
#define	NSYMPR	500

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[512];
} 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;

struct	exec filhdr;

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

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


/* symbol management */
typedef struct {
	char	sname[8];
	char	stype;
	char	spare;
	short	symhash;	/* index of hash table entry pointing to this symbol */
	long	svalue;
} 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;			/* number of symbols allocated in symtab */
SYMBOL	*hshtab[NSYM+2];	/* hash table for symbols */
LOCAL	*local;

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

int	trace;
/* 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	nflag = 1;	/* pure procedure */
int	dflag;		/* define common even with rflag */
int	iflag;		/* I/D space separated */
BOOL	vflag;		/* overlays used */

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();
short	gets();
long	get3();
long	getl();
SYMBOL	**lookup();
FILE	*tcreat();
long	round();
SYMBOL	**slookup();
SYMBOL	*lookloc();

symwrite(sp,n,f) SYMBOL *sp; FILE *f; {
#ifdef vax
	fwrite(sp,sizeof(*symtab),n,f);
#else
	while (n--) {
		fwrite(sp,sizeof(*symtab)-sizeof(sp->svalue),1,f);
		writel(&(sp->svalue),1,f); sp++;
	}
#endif
}

delexit()
{
	unlink("l.out");
	unlink(doutn);
	unlink(troutn);
	unlink(droutn);
	unlink(soutn);
	if (delarg==0)
		chmod(ofilename, 0777);
	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=sbrk(0); nsym=0;
	/* 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 '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 'v':
				if (++c >= argc)
					error(1, "-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 'D':
				if (++c >= argc)
					error(1, "-D: arg missing");
				num = htoi(*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 = htoi(*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 't':
				trace++;
				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;

	brk(nextsym);
	filname = 0;
	middle();
	setupout();
	if (-1==(local=sbrk(NSYMPR*sizeof(*local)))) error(1,"Memory overflow");
	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 = htoi(*p); dorigin<dnum; dorigin++) putc(0, dout);
			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[(--sp)->symhash]=0;
	nextsym = saved;
}

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

	if (getfile(cp)==0)
		load1(0, 0L);
	else {
		loc = sizeof(int);
		for (;;) {
			dseek(&text, loc, (long)sizeof(archdr));
			if (text.size <= 0) {
				libp->loc = -1;
				libp++;
				return;
			}
			mget((short *)&archdr, sizeof archdr, &text);
			if (load1(1, loc+sizeof(archdr))) {
				libp->loc = loc;
				libp++;
			}
#ifndef vax
			if (archdr.ar_size.loword==0) fixl(&archdr.ar_size);
#endif
			loc += round(archdr.ar_size, 1) + sizeof(archdr);
		}
	}
	close(infil);
}

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

	readhdr(loc);
	ctrel = tsize;
	cdrel += dsize;
	cbrel += bsize;
	ndef = 0;
	nlocal = sizeof(cursym);
	savnext = nextsym;
/*	if (filhdr.a_trsize+filhdr.a_drsize==0) {
/*		error(0, "No relocation bits");
/*		return(0);
/*	}
*/
	loc += filhdr.a_text + filhdr.a_data +
			filhdr.a_trsize + filhdr.a_drsize + sizeof(filhdr);
	dseek(&text, loc, filhdr.a_syms);
	while (text.size > 0) {
		symget(&cursym, &text);
		type = cursym.stype;
		if ((type&EXTERN)==0) {
			if (Xflag==0 || cursym.sname[0]!='L' || type&STABTYPS)
				nlocal += sizeof cursym;
			continue;
		}
		symreloc();
		if (enter(lookup()))
			continue;
		if ((sp = lastsym)->stype != EXTERN+UNDEF)
			continue;
		if (cursym.stype == EXTERN+UNDEF) {
			if (cursym.svalue > sp->svalue)
				sp->svalue = cursym.svalue;
			continue;
		}
		if (sp->svalue != 0 && cursym.stype == EXTERN+TEXT)
			continue;
		ndef++;
		sp->stype = cursym.stype;
		sp->svalue = cursym.svalue;
	}
	if (libflg==0 || ndef) {
		tsize += filhdr.a_text;
		dsize += round(filhdr.a_data, FW);
		bsize += round(filhdr.a_bss, FW);
		ssize += nlocal;
		trsize += filhdr.a_trsize;
		drsize += filhdr.a_drsize;
		return(1);
	}
	/*
	 * No symbols defined by this library member.
	 * Rip out the hash table entries and reset the symbol table.
	 */
	while (nextsym>savnext)
		hshtab[(--nextsym)->symhash]=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_data = *slookup("_data");
	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->stype==EXTERN+UNDEF && sp->svalue==0
				&& sp!=p_end && sp!=p_edata && sp!=p_etext
				&& sp!=p_data) {
				rflag++;
				dflag = 0;
				break;
			}
	}
	if (rflag) 
		sflag = iflag = 0;
	/*
	 * Assign common locations.
	 */
	csize = 0;
	database = round(tsize+textbase, (nflag? PAGRND:FW));
	if (dflag || rflag==0) {
		ldrsym(p_data, (long)0 , EXTERN+DATA);
		ldrsym(p_etext, tsize, EXTERN+TEXT);
		ldrsym(p_edata, dsize, EXTERN+DATA);
		ldrsym(p_end, bsize, EXTERN+BSS);
		for (sp = symtab; sp<symp; sp++) {
			if ((s=sp->stype)==EXTERN+UNDEF && (t = sp->svalue)!=0) {
				if (t>DW)
					rnd = DW;
				else if (t>FW)
					rnd = FW;
				else
					rnd = HW;
				csize = round(csize, rnd);
				sp->svalue = csize;
				sp->stype = EXTERN+COMM;
				ocsize = csize;	
				csize += t;
			}
			if (((s&TMASK) == EXTERN+UNDEF) && (s & STABTYPS)) {
				sp->svalue = ocsize;
				sp->stype = (s & STABTYPS) | (EXTERN+COMM);
			}
		}
	}
	/*
	 * Now set symbols to their final value
	 */
	csize = round(csize, FW);
	torigin = textbase;
	dorigin = database;
	corigin = dorigin + dsize;
	borigin = corigin + csize;
	cdorel = 0;
	cborel = dsize+csize;
	nund = 0;
	for (sp = symtab; sp<symp; sp++) switch (sp->stype & TMASK) {
	case EXTERN+UNDEF:
		errlev |= 01;
		if ((arflag==0 || dflag) && sp->svalue==0) {
			if (nund==0)
				printf("Undefined:\n");
			nund++;
			printf("%.8s\n", sp->sname);
		}
		continue;

	case EXTERN+ABS:
	default:
		continue;

	case EXTERN+TEXT:
		sp->svalue += torigin;
		continue;

	case EXTERN+DATA:
		sp->svalue += dorigin;
		continue;

	case EXTERN+BSS:
		sp->svalue += borigin;
		continue;

	case EXTERN+COMM:
		sp->stype = (sp->stype & STABTYPS) | (EXTERN+BSS);
		sp->svalue += corigin;
		continue;
	}
	if (sflag || xflag)
		ssize = 0;
	bsize += csize;
	nsym = ssize / (sizeof cursym);
}

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

	if ((sp = asp) == 0)
		return;
	if (sp->stype != EXTERN+UNDEF || sp->svalue) {
		printf("%.8s: ", sp->sname);
		error(0, "Multiply defined (internal)");
		return;
	}
	sp->stype = type;
	sp->svalue = val;
}

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

setupout()
{
	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 = nflag? NMAGIC:OMAGIC;
	filhdr.a_text = nflag? tsize:round(tsize, FW);
	filhdr.a_data = dsize;
	filhdr.a_bss = bsize;
	filhdr.a_trsize = trsize;
	filhdr.a_drsize = drsize;
	filhdr.a_syms = sflag? 0: (ssize + (sizeof cursym)*(nextsym-symtab));
	if (entrypt) {
		if (entrypt->stype!=EXTERN+TEXT)
			error(0, "Entry point not in text");
		else
			filhdr.a_entry = entrypt->svalue;
	} else
		filhdr.a_entry=0;
	filhdr.a_trsize = (rflag ? trsize:0);
	filhdr.a_drsize = (rflag ? drsize:0);
	writel(&filhdr,8,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((short *)&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;

	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+filhdr.a_text+filhdr.a_data+
		filhdr.a_trsize+filhdr.a_drsize, filhdr.a_syms);
	while (text.size > 0) {
		symno++;
		symget(&cursym, &text);
		symreloc();
		type = cursym.stype;
		if ((type&EXTERN) == 0) {
			if (!sflag&&!xflag&&(!Xflag||cursym.sname[0]!='L'||type&STABTYPS))
				symwrite(&cursym, 1, sout);
			continue;
		}
		if ((sp = *lookup()) == 0)
			error(1, "internal error: symbol not found");
		if (cursym.stype == EXTERN+UNDEF) {
			if (lp >= local+NSYMPR)
				error(1, "Local symbol overflow");
			lp->locindex = symno;
			lp++->locsymbol = sp;
			continue;
		}
		if(cursym.stype & STABTYPS) continue;
		if (cursym.stype!=sp->stype || cursym.svalue!=sp->svalue) {
			printf("%.8s: ", cursym.sname);
			error(0, "Multiply defined");
		}
	}
	dseek(&text, loc, filhdr.a_text);
	dseek(&reloc, loc+filhdr.a_text+filhdr.a_data, filhdr.a_trsize);
	load2td(lp, ctrel, tout, trout);
	dseek(&text, loc+filhdr.a_text, filhdr.a_data);
	dseek(&reloc, loc+filhdr.a_text+filhdr.a_data+filhdr.a_trsize, filhdr.a_drsize);
	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 r1;
	register char r2; 
	register long t;
	register SYMBOL *sp;
	long tw,u,l;

	for (;;) {
	if (reloc.size==0) {while (text.size) putc(get(&text),b1); break;}
	t=getl(&reloc); /* position of relocatable stuff */
	if (rflag) putl(t+creloc,b2); /* remember for subsequent link editing */
	while (text.pos<t) putc(get(&text),b1); /* advance to proper position */
	r1=get3(&reloc); /* kind of relocation */
	r2 = getb(&reloc);
	switch (r2&06) {/* read raw datum according to its length */
		case LEN1: tw=get(&text); break;
		case LEN2: tw=gets(&text); break;
		case LEN4: tw=getl(&text); break;
	}
	if (r2&REXT) {
		sp=lookloc(lp,r1); /* find the symbol */
		if (sp->stype==EXTERN+UNDEF) { /* still undefined */
			r2=(r2&(REFMASK+REXT+ROFF));
			r1 = nsym+(sp-symtab); /* new reloc */
		}
		else {
			if (sp->stype==EXTERN+DATA && r2&ROFF) {
				r1=RDATAO;
				r2&=REFMASK;
			}
			else if (sp->stype==EXTERN+BSS && r2&ROFF) {
				r1=RBSSO;
				r2&=REFMASK;
			}
			else if (sp->stype==EXTERN+ABS && r2&ROFF) {
				r1=RABSO;
				r2&=REFMASK;
			}
			else if (sp->stype==EXTERN+TEXT && r2&ROFF) {
				r1=RTEXTO;
				r2&=REFMASK;
			}
			else {if (r2&ROFF) {if (rflag) {error(0,"!-r; see JFR"); rflag=0;}}
				 else tw += database;
				 r1=sp->stype&TYPE;
				 r2&=REFMASK;
			}
			tw += sp->svalue - database;
		}
	} else switch (r1&TYMASK) {
		case RTEXT:	tw += ctrel; break;
		case RTEXTO:tw += round(filhdr.a_text,PAGRND)+ctrel-database; break;
		case RDATA: tw += cdrel; break;
		case RDATAO:tw += cdorel; break;
		case RBSS:	tw += cbrel; break;
		case RBSSO: tw += cborel-filhdr.a_data; break;
		case RABSO: tw += round(filhdr.a_text,PAGRND)-database; break;
	}
	if (rflag) { /* remember for subsequent link editing */
		put3(r1,b2);
		putb(r2,b2);
	}
	if (r2&PCREL) tw -= creloc; /* assembler already subtracted text.pos */
	switch (r2&06) {/* output relocated datum according to its length */
		case LEN1: l= -128; u=127; putc((char)tw,b1); break;
		case LEN2: l= -32768; u=32767; puts((short)tw,b1); break;
		case LEN4: l=0x80000000; u=0x7FFFFFFF; putl(tw,b1); break;
	}
	if (tw<l || u<tw) error(0,"Displacement overflow");
	}
}

finishout()
{

	if (!nflag)
		while (tsize&FW) {
			putc(0, tout); tsize++;
		}
	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;
	cp8c(s, cursym.sname);
	cursym.stype = TEXT;
	cursym.svalue = 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);
}

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

char
getb(sp) STREAM *sp; {
char t; mget(&t,1,sp); return(t);
}

long
get3(sp) STREAM *sp; {
long t; t=0; mget(&t,3,sp); return(t);
}

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

symget(sp,f) SYMBOL *sp; STREAM *f; {
	mget(sp,sizeof(*sp),f);
#ifndef vax
	fixl(&sp->svalue);
#endif
}

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

	b = loc>>9;
	o = loc&0777;
	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 & ~0777L, 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 = (short *)(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)<<9), (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 = &fpage;
	}
	sp->pos += sizeof(char);
	return(*sp->ptr++);
}

getfile(acp)
STRING acp;
{
	register STRING cp;
	register int c;
	int arcmag;

	cp = acp; 
	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(1, "cannot open");
	page[0].bno = page[1].bno = -1;
	page[0].nuser = page[1].nuser = 0;
	text.pno = reloc.pno = &fpage;
	fpage.nuser = 2;
	dseek(&text, 0L, (long)sizeof(int));
	if (text.size <= 0)
		error(1, premeof);
	mget(&arcmag, sizeof(arcmag), &text);
	return(arcmag==ARMAG);
}

SYMBOL **lookup()
{
	int i; 
	BOOL clash;
	register SYMBOL **hp;
	register char *cp, *cp1;

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

SYMBOL **slookup(s)
char *s;
{
	cp8c(s, cursym.sname);
	cursym.stype = EXTERN+UNDEF;
	cursym.svalue = 0;
	return(lookup());
}

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

	if (*hp==0) {
		if ((nextsym-symtab)>=NSYM)
			error(1, "Symbol table overflow");
		if ((nextsym-symtab)>=nsym) {
			if (-1==sbrk(NSYM/5 * sizeof(*symtab))) error(1,"Memory overflow");
			nsym += NSYM/5;
		}
		*hp = lastsym = sp = nextsym++;
		cp8c(cursym.sname, sp->sname);
		sp->stype = cursym.stype;
		sp->symhash = hp-hshtab;
		sp->svalue = cursym.svalue;
		return(1);
	} else {
		lastsym = *hp;
		return(0);
	}
}

symreloc()
{
	switch (cursym.stype & 017) {

	case TEXT:
	case EXTERN+TEXT:
		cursym.svalue += ctrel;
		return;

	case DATA:
	case EXTERN+DATA:
		cursym.svalue += cdrel;
		return;

	case BSS:
	case EXTERN+BSS:
		cursym.svalue += cbrel;
		return;

	case EXTERN+UNDEF:
		return;
	}
	if (cursym.stype&EXTERN)
		cursym.stype = EXTERN+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;
	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((short *)&filhdr, sizeof(filhdr), &text);
#ifndef vax
	for (p= &filhdr,i=8;--i>=0;) fixl(p++);
#endif
	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");
	}
	filhdr.a_bss = round(filhdr.a_bss, FW);
	if (filhdr.a_magic == NMAGIC) {
		cdrel = -round(filhdr.a_text, PAGRND);
		cbrel = cdrel - filhdr.a_data;
	} else if (filhdr.a_magic == OMAGIC) {
		cdrel = -filhdr.a_text;
		cbrel = cdrel - filhdr.a_data;
	} else
		error(1, "Bad format");
}

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)
STRING s1; 
STRING s2;
{
	while (*s1==*s2++)
		if ((*s1++)==0)
			return(TRUE);
	return(FALSE);
}

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

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

putb(w, f)
FILE *f; char w; {
fwrite(&w,sizeof(char),1,f);
}

put3(w, f)
FILE *f; long w; {
fwrite(&w,3,1,f);
}

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