/*	@(#)ld2.c	2.1		*/
static char ID[] = "@(#) ld2.c: 1.14 7/7/83";
#include "system.h"

#include <stdio.h>
#include "reloc.h"
#include "linenum.h"
#include "structs.h"
#include "y.tab.h"

#if TRVEC
#include "tv.h"
#include "ldtv.h"
#endif

#include "extrns.h"
#include "list.h"
#include "params.h"
#include "sgsmacros.h"
#include "patch.h"


#define BASIC_TYPES (STYP_TEXT | STYP_DATA | STYP_BSS)
#if	M32
#define BOUNDARY	0x20000L	/* 128K */
#else
#define BOUNDARY	0x10000L	/*  64K */
#endif
#define	K8	0x2000L
static ITEMID dotacid;
static OUTSECT *dotosptr;
/*eject*/
pboslist()
{

/*
 * Process the BLDOUTSC List
 */

	register OUTSECT *osptr;
	register INSECT *insptr;
	register INFILE *inflptr;
#if !ONEPROC
	ACTITEM a;
#endif
	OUTSECT *dfn_scn_grp();
	long	check_sect();


/*
 * Process the list of output sections.  Output sections can be 
 * defined in two places:
 *
 *	1. PASS 1: user-supplied; bldcnt will be > 0, and the bldoutsc 
 *			list head will be NULL
 *	2. PASS 2: default-generation; bldcnt will be 0, and the
 *			bldoutsc list will describe the output
 *			sections
 *
 *	These two uses are mutually exclusive
 */

#if DEBUG
	if( dflag > 2 )
		fprintf( stderr, "\nBLDOSCN data structures:" );
#endif
#if !ONEPROC
	while( bldcnt-- ) {

		fread( &a, 1, sizeof(ACTITEM), trnfdes );
		a.dfnscn.aiinflnm = (char *) ((int) a.dfnscn.aiinflnm + (int) strbase);

		osptr = dfn_scn_grp(&a, 0);
		copy(osptr->oshdr.s_name, a.dfnscn.ainame, 8);
#if TRVEC
		/*
		 * User can define attributes of TV in two ways:
		 * (1) using TV { } directive
		 * (2) using SECTIONS { .tv : {} } directive
		 * so force them to agree
		 */
		if ( equal(a.dfnscn.ainame, _TV, 8) ) { /* defining .tv */
			if ( tvspec.tvbndadr == -1L )
				tvspec.tvbndadr = a.dfnscn.aibndadr;
			else
				a.dfnscn.aibndadr = tvspec.tvbndadr;
			}
#endif
		if( a.dfnscn.aitype == AIDFNSCN )
			numoutsc++;

		osptr->oshdr.s_paddr = -1L;
		listadd(l_OS, &outsclst, osptr);
		}
#endif

	/*
	 * We now know everything there is to know about the
	 * transfer vector:
	 *	At the end of ploadfil(), we knew how big it
	 *	had to be.
	 *	Here we know whether it was defined by ld or
	 *	whether it is defined by .tv input sections
	 *	together with a .tv SECTION directive
	 * Go check everything and complete tvspec definition.
	 */

#if TRVEC
	if ( tvflag )
		tvupdat();	/* update definition of tv */
#endif

	if( bldoutsc.head ) {
		register ACTITEM *aptr, *a2ptr;

		aptr = (ACTITEM *) bldoutsc.head;
		while( aptr ) {

			osptr = dfn_scn_grp(aptr, 1);
	
			copy(osptr->oshdr.s_name, aptr->dfnscn.ainame, 8);
			if( aptr->dfnscn.aitype == AIDFNSCN )
				numoutsc++;
	
			osptr->oshdr.s_paddr = -1L;
			listadd(l_OS, &outsclst, osptr);

			a2ptr = aptr;
			aptr = aptr->dfnscn.ainext;
			free( (char *) a2ptr);
			}
		}

/*
 * Make sure all input sections are allocated
 * into some output section.
 */

	for( inflptr = (INFILE *) infilist.head; inflptr; inflptr = inflptr->flnext ) {
		for( insptr = inflptr->flishead; insptr; insptr=insptr->isnext ) {
			if (insptr->isoutsec <= 0) {
				if ((osptr = fndoutsec(insptr->ishdr.s_name)) != NULL)
					inscadd(insptr,osptr);
				else {
					osptr = (OUTSECT *) myalloc(sizeof(OUTSECT));
					numoutsc++;
					copy(osptr->oshdr.s_name, insptr->ishdr.s_name, 8);
					osptr->osfill = globfill;
					osptr->oshdr.s_paddr = -1l;
					listadd(l_OS,&outsclst,osptr);
					inscadd(insptr,osptr);
					}
				}
			}
		}

	/*
	 * for each output section, check the flags, and for a group,
	 * check the size.
	 */

	for( osptr = (OUTSECT *) outsclst.head; osptr; osptr = osptr->osnext ) 
		{
#if PAGING
			ACTITEM *aptr;
			ADDRESS textbegin, textsize;
			textbegin = hflag + FILHSZ + SCNHSZ * numoutsc + memorg;
#endif
		if (osptr->oshdr.s_flags & STYP_GROUP) {
			OUTSECT *op;
			for( op = ((OUTSECT *) osptr->osinclhd); op; op = op->osnext ) {
#if PAGING
				if (bond_t_d && !Nflag && equal(op->oshdr.s_name,_DATA,8)) {
					osptr->osalign = 0L;
					aptr = (ACTITEM *) myalloc(sizeof(ACTITEM));
					aptr->bond.aitype = AIBOND;
					aptr->bond.aiinflnm = "*default.bond.file*";
					aptr->bond.aiinlnno = 3;
					aptr->bond.aioutsec = osptr;
					aptr->bond.aiadrbnd = ((textsize + textbegin) / BOUNDARY + 1) 
						* BOUNDARY + (textsize + textbegin) % K8;
					listadd(l_AI,&bondlist,aptr);
					}
#endif
				osptr->oshdr.s_size += check_sect( op );
				}
			if (osptr->oshdr.s_size > MAXSCNSIZE)
				lderror(1,0,NULL, "GROUP containing section %.8s is too big",
				    osptr->osinclhd->ishdr.s_name);
		} else {
#if PAGING
			if (bond_t_d && !Nflag && equal(osptr->oshdr.s_name,_TEXT,8)) {
				textsize = osptr->oshdr.s_size;
				aptr = (ACTITEM *) myalloc(sizeof(ACTITEM));
				aptr->bond.aitype = AIBOND;
				aptr->bond.aiinflnm = "*default.bond.file*";
				aptr->bond.aiinlnno = 2;
				aptr->bond.aioutsec = osptr;
				aptr->bond.aiadrbnd = textbegin;
				listadd(l_AI,&bondlist,aptr);
				}
#endif
			check_sect( osptr );
			}
		}

	/*
	 * If .tv is user-defined, we now know the true size of
	 * the section ( the sum of the sizes of the input
	 * sections) which we didn't know before. Now call the
	 * the final update routine to insure .tv symtab entry
	 * is correct
	 */

#if TRVEC
	if ( tvflag )
		tvupdt2();	/* final update of tvrange and tvlength */
#endif

#if DEBUG
	if (dflag)
		dmp_outsects();
#endif
}
/*eject*/
OUTSECT *
dfn_scn_grp(aptr, dflt)

ACTITEM *aptr;		/* DFNSCN or DFNGRP entry    */
int dflt;		/* user-supplied or ld-default entry */
{

/*
 * Process one AIDFNSCN or AIDFNGRP data structure from
 * the list of output sections
 */

	OUTSECT *osptr, *op;
	INFILE *inflptr;
	INSECT *insptr;
	ENODE *ep;
	long val;
	int fill, fillflag;
	ACTITEM *a2ptr;
#if !ONEPROC
	ACTITEM b;
#endif

#if DEBUG
	if( dflag > 2 )
		fprintf( stderr, "\n\t%04x %s (%s) %d (%s) %04x (%s)\n\t\t%08lx %d %08lx %08lx %d %d (%04x,%04x)",
			aptr->dfnscn.ainext,
			(aptr->dfnscn.aitype == AIDFNSCN) ? "AIDFNSCN" : "AIDFNGRP",
			aptr->dfnscn.aiinflnm,
			aptr->dfnscn.aiinlnno, aptr->dfnscn.ainame, aptr->dfnscn.aifill,
			aptr->dfnscn.aiowname, aptr->dfnscn.aibndadr, aptr->dfnscn.aiattown,
			aptr->dfnscn.aialign, aptr->dfnscn.aiblock, (unsigned) aptr->dfnscn.aifillfg,
			(unsigned) aptr->dfnscn.aisctype, aptr->dfnscn.sectspec.head, aptr->dfnscn.sectspec.tail );
#endif

	osptr = (OUTSECT *) myalloc(sizeof(OUTSECT));
	if (aptr->dfnscn.aibndadr != -1L) {		/* section bonded */
		a2ptr = (ACTITEM *) myalloc(sizeof(ACTITEM));
		a2ptr->bond.aitype = AIBOND;
		a2ptr->bond.aiinflnm = aptr->dfnscn.aiinflnm;
		a2ptr->bond.aiinlnno = aptr->dfnscn.aiinlnno;
		a2ptr->bond.aioutsec = osptr;
		a2ptr->bond.aiadrbnd = aptr->dfnscn.aibndadr;
		listadd(l_AI,&bondlist,a2ptr);
		}
	else if (aptr->dfnscn.aiowname[0] != '\0') {  /*owner specified */
		a2ptr = (ACTITEM *) myalloc(sizeof(ACTITEM));
		a2ptr->dfownr.aitype = AIDFOWNR;
		a2ptr->dfownr.aiinflnm = aptr->dfnscn.aiinflnm;
		a2ptr->dfownr.aiinlnno = aptr->dfnscn.aiinlnno;
		a2ptr->dfownr.aioutsec = osptr;
		copy(a2ptr->dfownr.ainamown, aptr->dfnscn.aiowname, 8);
		listadd(l_AI,&ownlist,a2ptr);
		}
	else if (aptr->dfnscn.aiattown != 0) {	/* attr of owner */
		a2ptr = (ACTITEM *) myalloc(sizeof(ACTITEM));
		a2ptr->ownatr.aitype = AIOWNATR;
		a2ptr->ownatr.aiinflnm = aptr->dfnscn.aiinflnm;
		a2ptr->ownatr.aiinlnno = aptr->dfnscn.aiinlnno;
		a2ptr->ownatr.aioutsec = osptr;
		a2ptr->ownatr.aiownatt = aptr->dfnscn.aiattown;
		listadd(l_AI,&atownlst,a2ptr);
		}

	if (aptr->dfnscn.aitype == AIDFNGRP)
		osptr->oshdr.s_flags = STYP_GROUP;
	else
		osptr->oshdr.s_flags = aptr->dfnscn.aisctype;
	osptr->osalign = aptr->dfnscn.aialign;
	osptr->osblock = aptr->dfnscn.aiblock;
	osptr->osfill = aptr->dfnscn.aifill;
	osptr->osflags |= aptr->dfnscn.aifillfg;

/*
 * A DFNSCN need have no subordinate entries, since the following is
 * legal:
 *		.text : {}
 */

	if( aptr->dfnscn.sectspec.head == NULL )
		return( osptr );

/*
 * A DFNGRP is a sequence of one or more DFNSCN entries
 */

	if( aptr->dfnscn.aitype == AIDFNGRP ) {
		a2ptr = (ACTITEM *) aptr->dfnscn.sectspec.head;
		do {
			if( ! dflt ) {
#if !ONEPROC
				fread( &b, 1, sizeof(ACTITEM), trnfdes );
				b.dfnscn.aiinflnm = (char *) ((int) b.dfnscn.aiinflnm + (int) strbase);
				a2ptr = &b;
#endif
				}

			op = dfn_scn_grp(a2ptr, dflt);

			op->oshdr.s_paddr = -1L;
			listadd(l_GRP, osptr, op);
			numoutsc++;
			copy(op->oshdr.s_name, a2ptr->dfnscn.ainame, 8);

			if( (a2ptr = a2ptr->dfnscn.ainext) == NULL )
				return( osptr );
			}
		while( 1 );
		}

/*
 * Process a DFNSCN with one or more subordinate entries, of type
 * AIADFILE, AIADDSCN, or AIEVEXPR
 */

	a2ptr = (ACTITEM *) aptr->dfnscn.sectspec.head;
	do {
		if( ! dflt ) {
#if !ONEPROC
			fread( &b, 1, sizeof(ACTITEM), trnfdes );
			b.adfile.aiinflnm = (char *) ((int) b.adfile.aiinflnm + (int) strbase);
			a2ptr = &b;
#endif
			}

		switch( a2ptr->adfile.aitype ) {

		case AIADFILE:

			if( ! dflt )
				a2ptr->adfile.aifilnam = (char *) ((int) a2ptr->adfile.aifilnam + (int) strbase);

#if DEBUG
			if( dflag > 2 )
				fprintf( stderr, "\n\t%04x AIADFILE (%s) %d (%s) %d %d %d",
					a2ptr->adfile.ainext, a2ptr->adfile.aiinflnm,
					a2ptr->adfile.aiinlnno, a2ptr->adfile.aifilnam, a2ptr->adfile.ainadscs,
					a2ptr->adfile.aifill2, (unsigned) a2ptr->adfile.aifilflg );
#endif

			if( (inflptr = fndinfil(a2ptr->adfile.aifilnam)) == NULL )
				break;
			if( a2ptr->adfile.ainadscs == 0 ) {
				insptr = inflptr->flishead;
				while( insptr ) {
					if( insptr->isoutsec == NULL ) {
						inscadd(insptr, osptr);
						insptr->isfill = a2ptr->adfile.aifill2;
						insptr->isfillfg = a2ptr->adfile.aifilflg;
						}
					insptr = insptr->isnext;
					/*
					 * In case inflptr is library,
					 *  get next one
					 */
					if( (insptr == NULL)  &&  (inflptr->flfiloff > 0L) )
						while( (inflptr = inflptr->flnext) )
							if (strcmp(a2ptr->adfile.aifilnam,inflptr->flname) == 0) {
								insptr = inflptr->flishead;
								break;
								}
					}
				}
			else {
				fill = a2ptr->adfile.aifill2;
				fillflag = a2ptr->adfile.aifilflg;
				}
			break;

		case AIADDSCN:

			if( ! dflt )
				a2ptr->addscn.aiscfile = (char *) ((int) a2ptr->addscn.aiscfile + (int) strbase);

#if DEBUG
			if( dflag > 2 )
				fprintf( stderr, "\n\t%04x AIADDSCN (%s) %d (%s) %04x (%s)",
					a2ptr->addscn.ainext, a2ptr->addscn.aiinflnm,
					a2ptr->addscn.aiinlnno, a2ptr->addscn.ainame, a2ptr->addscn.aifill,
					a2ptr->addscn.aiscfile );
#endif

			if( (inflptr = fndinfil(a2ptr->addscn.aiscfile)) == NULL )
				break;
			insptr = inflptr->flishead;
			while(insptr) {
				if (equal(a2ptr->addscn.ainame,insptr->ishdr.s_name,8))  {
					if (insptr->isoutsec) {
						lderror(1, a2ptr->addscn.aiinlnno, a2ptr->addscn.aiinflnm,
						   "adding %s(%.8s) to multiple output sections",
						   inflptr->flname,
						   insptr->ishdr.s_name);
						goto contin1;
						}
					inscadd(insptr,osptr);
					insptr->isfill = fill;
					insptr->isfillfg = fillflag;
					insptr = NULL;
					if (inflptr->flfiloff > 0L)
					/*
					 * In case inflptr is library,
					 *  get next one
					 */
						while( (inflptr = inflptr->flnext) )
							if (strcmp(a2ptr->addscn.aiscfile,inflptr->flname) == 0) {
								insptr = inflptr->flishead;
								break;
								}
					if ( insptr != NULL )
						continue;
					else
						goto contin1;
					}
				insptr = insptr->isnext;
				}
			lderror(1, a2ptr->addscn.aiinlnno, a2ptr->addscn.aiinflnm,
				"%s(%.8s) not found",
				a2ptr->addscn.aiscfile,a2ptr->addscn.ainame);
			break;

		case AIEVEXPR:

#if DEBUG
			if( dflag > 2 )
				fprintf( stderr, "\n\t%04x AIEVEXPR (%s) %d %04x\n\t\tROOT:",
					a2ptr->evexpr.ainext, a2ptr->evexpr.aiinflnm,
					a2ptr->evexpr.aiinlnno, a2ptr->evexpr.aiexptr );
#endif

#if ONEPROC
			aptr = a2ptr;
#else
			aptr = ldexp( &b );
#endif

			a2ptr = a2ptr->adfile.ainext;
			ep = aptr->evexpr.aiexptr;
			cur_dot = osptr->oshdr.s_size;

			if (ep->gnode.exleft->gnode.exop != DOT) {
				/*
				 * LHS of assnment is regular symbol
				 */
				dotacid = 0;
				dotosptr = osptr;
				chgdot(ep->gnode.exright);
				listadd(l_AI, &explist, aptr);
				}
			else {
				/*
				 * LHS is DOT symbol
				 */
				curexp = aptr;
				val = eval(ep->gnode.exright);
				if (val < cur_dot)
					lderror(1, aptr->evexpr.aiinlnno, aptr->evexpr.aiinflnm,
						"attempt to decrement DOT");
				else
					osptr->oshdr.s_size = val;

				clrexp( aptr );
				}
			goto contin2;
		}

	contin1:
		a2ptr = a2ptr->adfile.ainext;
	contin2:
		if (a2ptr == NULL)
			return( osptr );

		}
	while( 1 );
}
/*eject*/
inscadd(pin,pout)
INSECT *pin;
OUTSECT *pout;
{

/*
 * Add an input section to an output section.
 *
 * The text, relocation, and lineno info of the input section
 * are allocated at the end of the output section.
 */

	long btype;

	pin->isoutsec = pout;
	pin->isdispl = pout->oshdr.s_size;
	pin->islndisp = ((long) pout->oshdr.s_nlnno * (long) LINESZ);
	pout->oshdr.s_size += pin->ishdr.s_size;
	pout->oshdr.s_nlnno += pin->ishdr.s_nlnno;

	if (rflag) {
		pin->isrldisp = ((long) pout->oshdr.s_nreloc * (long) RELSZ);
		pout->oshdr.s_nreloc += pin->ishdr.s_nreloc;
		}

	btype = pin->ishdr.s_flags & BASIC_TYPES;
	if (btype)
		pout->oshdr.s_flags |= btype;
	else if ( equal( _TEXT, pin->ishdr.s_name, 8 ))
		pout->oshdr.s_flags |= STYP_TEXT;
	else if ( equal( _DATA, pin->ishdr.s_name, 8 ))
		pout->oshdr.s_flags |= STYP_DATA;
	else if ( equal( _BSS, pin->ishdr.s_name, 8 ))
		pout->oshdr.s_flags |= STYP_BSS;

	pout->osflags |= (pin->ishdr.s_flags & STYP_DSECT) ? OSDSECT : OSREG;

	listadd(l_INC,pout,pin);
}
/*eject*/
INFILE *
fndinfil(fnam)
char *fnam;
{

/*
 * Locate the input file structure by a given name, and
 * return a pointer to the structure.
 *
 *  Note: fnam must be a null terminated string.
 */

	register INFILE *p;

	p = (INFILE *) infilist.head;
	while (p) {
		if (strcmp(fnam,p->flname) == 0) 
			return(p);
		p = p->flnext;
		}

	return(NULL);
}
/*eject*/
chgdot(p)
ENODE *p;
{
	register SYMTAB *sym;
	ITEMID add_dot();

/*
 * This function is called when an assignment is encountered
 * within an output section specification, and the LHS of the
 * assnment is to a symbol other than DOT.  Any occurances of
 * DOT within the RHS expression must be replaced by a reference
 * to a dummy symbol table entry, which will have the correct
 * value after the output section is relocated.
 */

	switch(p->gnode.exop) {
	case DOT:
		if( dotacid == 0 )
			dotacid = add_dot(cur_dot, dotosptr);
		p->nnode.exsymptr = dotacid;
		break;
	case NAME:
	case INT:
		break;
	default:
		if (p->gnode.exleft) 
			chgdot(p->gnode.exleft);
		if (p->gnode.exright)
			chgdot(p->gnode.exright);
	}
}
/*eject*/
add_pad()
{

/*
 * If the "-B" flag was specified, add any necessary "padding sections"
 * to the outpt file
 */

	register ANODE *ap;		/* ptr into avlist */
	ANODE *newap;			/* ptr to padding section node */
	register OUTSECT *osp;
	register INSECT *isp;
	int no_need_pad,		/* = 1 iff don't need padding */
	    counter;			/* used to make unique names */
	char padname[8];		/* padding section name */

/*
 * Look for an output section which meets one of two conditions:
 *
 *	1. The section is comprised entirely of uninitialized .bss
 *		sections
 *	2. The section is of zero length
 *
 * For every output section of these types, add an additional output
 * section immediatly after it.  This "padding section" has only two
 * non-zero/non-NULL/non-trivial characteristics:
 *	1. It is composed of Bflag bytes of zero
 *	2. It is written under control of BLOCK(Bflag)
 *
 * Note that padding sections have a virtual/physical address
 * of -1 (to generate errors from any user program or utility
 * attempting to use these values), and flags indicating that
 * it is a padding section
 */

	counter = 0;
	for( ap = (ANODE *) avlist.head; ap; ap = ap->adnext ) {
		if( ap->adtype != ADSECT )
			continue;

		osp = ap->adscnptr;
		no_need_pad = (osp->osflags & FILL);
		for( isp = osp->osinclhd; isp; isp = isp->isincnxt )
			if( (isp->ishdr.s_scnptr > 0)  ||  (isp->isfillfg) )
				no_need_pad = 1;
		if( ap->adsize == 0L )
			no_need_pad = 0;
		
		if( no_need_pad )
			continue;
		
		osp = (OUTSECT *) myalloc(sizeof(OUTSECT));
		numoutsc++;
		sprintf( padname, "-pad%2d-", counter++ );
		copy( osp->oshdr.s_name, padname, 8 );
		osp->oshdr.s_paddr = -1L;
		osp->oshdr.s_vaddr = -1L;
		osp->oshdr.s_size = Bflag;
		osp->oshdr.s_flags = STYP_PAD;
		osp->osblock = Bflag;
		
		newap = (ANODE *) myalloc(sizeof(ANODE));
		newap->adtype = ADPAD;
		newap->adsize = Bflag;
		newap->adscnptr = osp;

		listins( l_ADR, &avlist, ap, newap );
		}

#if DEBUG
	if( dflag > 1 ) {
		fprintf( stderr, "\nadd_pad" );
		dump_mem();
		}
#endif

}
/*eject*/
updatsms()
{

/*
 * After allocation, this function sets
 *	1. The section number of all output sections
 *	2. The virtual addresses of all input sections (isnewvad)
 *	3. The file offset of output sections
 *
 * In addition, the relocated value for all global symbols is 
 * computed and stored in the symbol table, along with the new
 * section number.
 *
 * The virtual addresses of output sections are set by
 * audit_regions in allocation.
 */

	register SYMTAB *symp;
	register OUTSECT *osp;
	register INSECT *isp;
	int snum, init;
	long vaddr, indx;
	long filoffst, lnnoptr, nrelocs;
	ANODE *ap;
	INFILE *fp;

/*
 * Compute the starting file offset of the first output section
 *
 * ********** NOTE NOTE NOTE NOTE **********
 *
 *	Increase the size of the optional header by the amount of
 *	space needed to generate a patch list
 *
 * ********** NOTE NOTE NOTE NOTE **********
 */

	hflag += psize();
	filoffst = (long) FILHSZ + hflag + SCNHSZ * numoutsc;

	nrelocs = 0L;
	snum = 0;

	for( ap = (ANODE *) avlist.head; ap; ap = ap->adnext ) {
		if (ap->adtype == ADSECT) {
			osp = ap->adscnptr;
			/*
			 ***** Special processing *****
			 *
			 * The description of the _TV output section is
			 * changed, so as to make it equivalent to that
			 * section of the transfer vector actually used
			 * in this ld run
			 *
			 * Note that in the default case - no ASSIGN or
			 * RANGE commands, the entire transfer vector
			 * is used, and hence this code produces no
			 * change.
			 *
			 * When ASSIGN and RANGE commands are present,
			 * the actual section of the transfer vector
			 * used is typically a subset of the entire
			 * vector. Output only the piece used
			 */
#if TRVEC
			if( tvflag && equal(osp->oshdr.s_name,_TV,8) ) {
				osp->oshdr.s_paddr +=
					(tvspec.tvrange[0] * TVENTSZ);
				osp->oshdr.s_vaddr += 
					(tvspec.tvrange[0] * TVENTSZ);
				osp->oshdr.s_size =
					((tvspec.tvrange[1]-tvspec.tvrange[0]+1)*TVENTSZ);
				}
#endif
			init = (osp->osflags & FILL);	/* section is initialized*/
			osp->ossecnum = ++snum;
			vaddr = osp->oshdr.s_vaddr;
			for( isp = osp->osinclhd; isp; isp = isp->isincnxt ) {
				isp->isnewvad = vaddr + isp->isdispl;
				isp->isnewpad = osp->oshdr.s_paddr + isp->isdispl;
				if (isp->ishdr.s_scnptr > 0 || isp->isfillfg)
					init = 1;
				}

			if ((osp->oshdr.s_flags & STYP_NOLOAD) || (osp->oshdr.s_flags & STYP_DSECT))
				init = 0;
			if (init) {
				filoffst += alignment(osp->osblock,filoffst);
				osp->oshdr.s_scnptr = filoffst;
				filoffst += osp->oshdr.s_size;
				if (rflag)
					nrelocs += (long) osp->oshdr.s_nreloc;
				if (filoffst % 2)
					++filoffst;
				}
			else
				osp->oshdr.s_scnptr = 0L;
			}
		else if( ap->adtype == ADPAD ) {
			osp = ap->adscnptr;
			osp->ossecnum = ++snum;
			filoffst += alignment(osp->osblock,filoffst);
			osp->oshdr.s_scnptr = filoffst;
			filoffst += osp->oshdr.s_size;
			if( filoffst % 2 )
				++filoffst;
			}
		}

/*
 * Do the same for the "dummy" sections (DSECTS)
 */

	for( osp = (OUTSECT *) dsectlst.head; osp; osp = osp->osdsnext ) {
		osp->ossecnum = ++snum;
		for( isp = osp->osinclhd; isp; isp = isp->isincnxt ) {
			isp->isnewvad = osp->oshdr.s_vaddr + isp->isdispl;
			isp->isnewpad = osp->oshdr.s_paddr + isp->isdispl;
			}
		if ( osp->oshdr.s_flags & STYP_COPY ) {
			filoffst += alignment(osp->osblock,filoffst);
			osp->oshdr.s_scnptr = filoffst;
			filoffst += osp->oshdr.s_size;
			if ( rflag )
				nrelocs += (long) osp->oshdr.s_nreloc;
			if ( filoffst % 2)
				++filoffst;
		} else {
			osp->oshdr.s_nreloc = 0;
			osp->oshdr.s_nlnno  = 0;
			}
		}

/*
 * Loop through sections a 2nd time to set the
 * file pointer to relocation and line numbers for each section
 */

	lnnoptr = filoffst + nrelocs * (long) RELSZ;
	for( ap = (ANODE *) avlist.head; ap; ap = ap->adnext ) {
		if (ap->adtype == ADSECT) {
			osp = ap->adscnptr;
			if (osp->oshdr.s_flags & STYP_NOLOAD) {
				osp->oshdr.s_nreloc = 0;
				osp->oshdr.s_nlnno  = 0;
				continue;
				}
			if( rflag  &&  (osp->oshdr.s_nreloc != 0) ) {
				osp->oshdr.s_relptr = filoffst;
				filoffst += ((long)osp->oshdr.s_nreloc *
							(long) RELSZ);
				}
#if UNIX || XL
			if(  (!sflag && !xflag)  &&  (osp->oshdr.s_nlnno != 0) ) {
#else
			if(  (!sflag )  &&  (osp->oshdr.s_nlnno != 0) ) {
#endif
				osp->oshdr.s_lnnoptr = lnnoptr;
				lnnoptr += ((long)osp->oshdr.s_nlnno *
							(long) LINESZ);
				}
			}
		}

/*
 * Do the same for the "copy" sections (COPY)
 */

	for( osp = (OUTSECT *) dsectlst.head; osp; osp = osp->osdsnext ) {
		if ( osp->oshdr.s_flags & STYP_COPY ) {
			if ( rflag && (osp->oshdr.s_nreloc != 0) ) {
				osp->oshdr.s_relptr = filoffst;
				filoffst += ((long) osp->oshdr.s_nreloc
						* (long) RELSZ);
				}
			if ( (! sflag) && (osp->oshdr.s_nlnno != 0) ) {
				osp->oshdr.s_lnnoptr = lnnoptr;
				lnnoptr += ((long) osp->oshdr.s_nlnno *
							(long) LINESZ);
				}
			}
		}

	symtborg = filoffst = lnnoptr;
	fp = (INFILE *) infilist.head;
	indx = 0;
	while (fp) {
		fp->flsymndx = indx;
		indx += fp->flnlsyms;
		fp = fp->flnext;
		}

	symp = NULL;
	while( (symp = loopsym(symp, 1)) != NULL ) {
		if( symp->sment.n_scnum > 0 ) {
			isp = symp->smscnptr;
			symp->sment.n_scnum = isp->isoutsec->ossecnum;
			symp->smnewval = symp->sment.n_value 
				+ isp->isnewpad - isp->ishdr.s_paddr;
			symp->smnewvirt = symp->smnewval
				- isp->isnewpad + isp->isnewvad;
			if( symp->smlocflg ) {
				symp->smoutndx += isp->isfilptr->flsymndx;
				}
			else {
				symp->smoutndx = indx++;
				indx += symp->sment.n_numaux;
				}
			}
		else {
			if ((symp->sment.n_zeroes == 0) ||
					(strncmp( symp->sment.n_name, ".dot", 4 ) != 0))
				symp->smoutndx = indx++;
			symp->smnewval = symp->sment.n_value;
			symp->smnewvirt = symp->sment.n_value;
			indx += symp->sment.n_numaux;
			}
		}
#if FLEXNAMES
	tnumsyms = indx;
#endif
}
/*eject*/
OUTSECT *
findoscn(number)
int number;		/* number of desired section */
{

/*
 * Look through the output section list, outsclst,
 * for a section with the specified number.  Return
 * a pointer to that element of the list.  If no
 * section exists with that number, return NULL.
 */

	register OUTSECT *osptr;

	osptr = (OUTSECT *) outsclst.head;
	while ( osptr != NULL ) {
		if ( osptr->ossecnum == number )
			return (osptr);
		osptr = osptr->osnext;
		}

	return (NULL);
}
/*eject*/
split_scns()
{

/*
 * Run though the output section list.  If the size of any output
 * section is greater then MAXSCNSZ, split the output section into
 * two (or more) output sections
 *
 * The name of a new section is the first char of the old name,
 * followed by a number, followed by the rest of the old
 * name.  Only eight characters are kept.
 */

	OUTSECT	*osp, *newosp, *nxtosp;
	INSECT	*isp, *nxtisp;
	char	*oldname, newname[20];		/* 8 + 4 + extra */
	int	scnnumber;
	long	newdisp, startdisp;	/* OUTSECT displacements */

/*
 * "osp" scans down the outsclst, and "nxtosp" will
 * always point to the next section on the original
 * list.  If we have to split osp, the new node
 * will be newosp, and nxtosp will not change.
 */

	for( osp = (OUTSECT *) outsclst.head; osp; osp = nxtosp ) {
		nxtosp = osp->osnext;

/* begin KLUDGE */
		adjsize(osp);
/* end KLUDGE */

		if (osp->oshdr.s_flags & STYP_DSECT)
			continue;

		if( (osp->osinclhd == osp->osincltl)  ||  (osp->oshdr.s_size <= MAXSCNSZ) ) {
			/* Either there is only one input section in the
			 * output section, or the output section is
			 * within the permissible size
			 */
			if( (isp = osp->osinclhd) == NULL )
				continue;
			if( isp->ishdr.s_size > MAXSCNSIZE ) {
				lderror(1,0,NULL, "section %.8s in file %s too big",
					isp->ishdr.s_name, isp->isfilptr->flname);
				osp->osinclhd = osp->osincltl = NULL;
				}
			continue;
			}

		/*
		 * An output section is too large, and must be split.
		 *
		 * Walk through the list of input sections, to build
		 * two (or more) output sections which are all within
		 * the permissible size range
		 */

		osp->oshdr.s_nreloc = 0;
		osp->oshdr.s_nlnno = 0;
		isp = osp->osinclhd;
		osp->osinclhd = osp->osincltl = NULL;
		scnnumber = 0;
		oldname = osp->oshdr.s_name;

		newdisp = 0L;
		if( isp != NULL )
			startdisp = osp->oshdr.s_size = isp->isdispl;
		else
			startdisp = osp->oshdr.s_size = 0L;

		/*
		 * "isp" points to the current input section which we
		 * are trying to add to the output section given by
		 * "osp"
		 */

		for(; isp; isp = nxtisp ) {
			nxtisp = isp->isincnxt;
			if( osp->oshdr.s_size + isp->ishdr.s_size <= MAXSCNSZ ) {
				newdisp = isp->isdispl - startdisp;
				if( osp->oshdr.s_size > newdisp )
					lderror(1,0, NULL,
						"internal error: split_scns, size of %.8s exceeds its new displacement",
						osp->oshdr.s_name);

				osp->oshdr.s_size = newdisp;
				isp->isincnxt = NULL;
				inscadd(isp, osp);
				}
			else if ( osp->oshdr.s_size != 0 ) {
				nxtisp = isp;	/* back up to reprocess */
				newosp = (OUTSECT *) myalloc(sizeof(OUTSECT));
				numoutsc++;
				sprintf(newname, "%c%x%.6s", *oldname,
					scnnumber++, oldname + 1);
				copy(newosp->oshdr.s_name, newname, 8);
				newosp->oshdr.s_paddr = -1L;
				newosp->osnext = osp->osnext;
				osp->osnext = newosp;
				if( (OUTSECT *) outsclst.tail == osp )
					outsclst.tail = (char *) newosp;
				osp = newosp;
				newdisp = 0L;
				startdisp = isp->isdispl;
				}
			else if ( isp->ishdr.s_size <= MAXSCNSIZE ) {
				isp->isincnxt = NULL;
				inscadd(isp, osp);
				}
			else
				lderror(1,0,NULL, "section %.8s in file %s is too big",
					isp->ishdr.s_name, isp->isfilptr->flname);
			}

		}

}
/*eject*/
psize()
{
/*
 * Determine the size of the patch list. This is done by looking for
 * all output sections having the name of ".patchnn", where nn is a
 * two-digit number
 *
 * This routine returns, as an int value, the size of the patch list
 */

	register int index, space;
	char pname[10];
	OUTSECT *fndoutsec();

	if( ! pflag )
		return( 0 );
	else
		space = sizeof(long);

/*
 * Patch sections, if they exist, must be named consecutively, starting
 * at ".patch00"
 */

	for( index = 0; index < 100; index++ ) {
		if (index < 10)
			sprintf(pname, ".patch0%d", index);
		else
			sprintf( pname, ".patch%d", index );
		if( fndoutsec(pname) == NULL )
			break;
		space += PESIZE;
		}

	return( space );
}
/*eject*/
OUTSECT *
fndoutsec(nam)
char *nam;
{
	register OUTSECT *p, *q;

	for( p = (OUTSECT *) outsclst.head; p; p=p->osnext ) {
		if (p->oshdr.s_flags & STYP_GROUP)
			for( q = (OUTSECT *) p->osinclhd; q; q=q->osnext ) {
				if (equal(nam,q->oshdr.s_name,8))
					return(q);
			}
		else
			if (equal(nam,p->oshdr.s_name,8))
				return(p);
	}
	return(NULL);
}




#if ILDOPT
add_extra()
{

/* If the "-ild" flag was specified, add sections to the output file to
 * cover any remaining configured memory.
 */

	register ANODE *ap, *newap;	/* ptr into avlist */
	OUTSECT *osp1, *osp2;		/* ptrs to the new output section headers */
	int counter;			/* used to make unique names */
	char sectname[9];		/* new section name */

/* For each remaining area of available memory, create a fake output section
 * that fits in that area. The purpose of this is to cover all of configured
 * memory so that the incremental ld will be able to determine the memory
 * configuration. Increase numoutsc to leave room for the .history section
 * header.
 */

	numoutsc++;
	counter = 0;
	for (ap = (ANODE *) avlist.head; ap; ap = ap->adnext) {
		if (counter > 99)
			break;
		if (ap->adtype != ADAVAIL)
			continue;
		if ( ap != (ANODE *) avlist.head && ap->adprev->adtype != ADAVAIL &&
		      ap->adprev->adscnptr != NULL &&
		      (ap->adprev->adscnptr->oshdr.s_flags & STYP_NOLOAD)  &&
		     ap != (ANODE *) avlist.tail && ap->adnext->adtype != ADAVAIL &&
		      ap->adnext->adscnptr != NULL &&
		      (ap->adnext->adscnptr->oshdr.s_flags & STYP_NOLOAD)    )
			continue;

		ap->adtype = ADSECT;
		newap = (ANODE *) myalloc( sizeof(ANODE) + 2 * sizeof(OUTSECT));
		osp1 = (OUTSECT *) (newap + 1);
		osp2 = osp1 + 1;
		numoutsc += 2;
		if (counter < 10)
			sprintf(sectname, ".i_l_d0%d", counter++);
		else
			sprintf( sectname, ".i_l_d%d", counter++);
		copy( osp1->oshdr.s_name, sectname, 8 );
		osp1->oshdr.s_flags = STYP_DSECT;
		ap->adscnptr = osp1;

		if (counter < 10)
			sprintf( sectname, ".i_l_d0%d", counter++);
		else
			sprintf( sectname, ".i_l_d%d", counter++ );
		copy( osp2->oshdr.s_name, sectname, 8 );
		osp2->oshdr.s_paddr = ap->adpaddr;
		osp2->oshdr.s_vaddr = ap->adpaddr;
		osp2->oshdr.s_size = ap->adsize;
		osp2->oshdr.s_flags = STYP_DSECT;
		osp2->osflags = 0;

		newap->adtype = ADSECT;
		newap->adpaddr = ap->adpaddr;
		newap->adsize = ap->adsize;
		newap->adscnptr = osp2;
		newap->admemp = ap->admemp;
		newap->adregp = ap->adregp;
		listins( l_ADR, &avlist, ap, newap );
	}
}
#endif




long
check_sect( osptr )
OUTSECT *osptr;
{

/* 
 * check the flags in the output section. if all the input sections are dummy
 * sections, the output section should be a dummy section.
 * if a bss section is combined with data or text, the bss section is
 * initialized, and becomes data.
 * return the size of the section if the output section is not a dummy
 * (the size is used in allocating space for a group).
 */

	if ((osptr->osflags & OSDSECT) && !(osptr->osflags & OSREG))
		osptr->oshdr.s_flags |= STYP_DSECT;

	if ((osptr->oshdr.s_flags & STYP_BSS) &&
			((osptr->oshdr.s_flags & (STYP_TEXT | STYP_DATA)) ||
			(osptr->osflags & FILL))) {
		osptr->oshdr.s_flags ^= STYP_BSS;
		osptr->oshdr.s_flags |= STYP_DATA;
		}

	if ( !(osptr->oshdr.s_flags & BASIC_TYPES))
		if (equal( _TEXT, osptr->oshdr.s_name, 8 ))
			osptr->oshdr.s_flags |= STYP_TEXT;
		else if (equal( _DATA, osptr->oshdr.s_name, 8 ))
			osptr->oshdr.s_flags |= STYP_DATA;
		else if (equal( _BSS, osptr->oshdr.s_name, 8 ))
			osptr->oshdr.s_flags |= STYP_BSS;

	return ((osptr->oshdr.s_flags & STYP_DSECT) ? 0L : osptr->oshdr.s_size);
}
