/*
 * 5799-WZQ (C) COPYRIGHT IBM CORPORATION 1988
 * LICENSED MATERIALS - PROPERTY OF IBM
 * REFER TO COPYRIGHT INSTRUCTIONS FORM NUMBER G120-2083
 */
/*
 * Copyright (c) 1983 Regents of the University of California.
 * All rights reserved. The Berkeley Software License Agreement
 * specifies the terms and conditions for redistribution.
 */
#ifndef lint
static char rcsid[] = "$Header:ld.c 12.0$";
#endif
/* $Source: /ibm/acis/usr/src/bin/RCS/ld.c,v $ */

/*
 * ld - string table version 
 * 
 * Common source for RT and 370.  
 * Note well: Use  ifdef ibm370   and  ifndef ibm370  to distinguish
 * hardware dependencies.  Do not use ibm032.  LMB 2/22/88
 */
/*
 * RT: JSW 6/25/86 - changed method of determining if archive modules are to be 
 * included as a fix for a bug induced by the new calling sequence.
 * WARNING: During pass 1 (load1()) the n_other field of the symbol table
 * entries is used as a flag.  If this field is ever needed in the future
 * then load1() must be changed.
 */

/*
  10-20-88:  Swiped branch and execute fix from AIX "ld.c".  -- JES
*/
#define	PAGE_CHECK

#include <sys/types.h>
#include <sys/signal.h>
#include <stdio.h>
#include <ctype.h>
#include <ar.h>
#include <a.out.h>
#include <ranlib.h>
#include <sys/stat.h>

/*
 * Basic strategy:
 *
 * The loader takes a number of files and libraries as arguments.
 * A first pass examines each file in turn.  Normal files are
 * unconditionally loaded, and the (external) symbols they define and require
 * are noted in the symbol table.   Libraries are searched, and the
 * library members which define needed symbols are remembered
 * in a special data structure so they can be selected on the second
 * pass.  Symbols defined and required by library members are also
 * recorded.
 *
 * After the first pass, the loader knows the size of the basic text
 * data, and bss segments from the sum of the sizes of the modules which
 * were required.  It has computed, for each ``common'' symbol, the
 * maximum size of any reference to it, and these symbols are then assigned
 * storage locations after their sizes are appropriately rounded.
 * The loader now knows all sizes for the eventual output file, and
 * can determine the final locations of external symbols before it
 * begins a second pass.
 *
 * On the second pass each normal file and required library member
 * is processed again.  The symbol table for each such file is
 * reread and relevant parts of it are placed in the output.  The offsets
 * in the local symbol table for externally defined symbols are recorded
 * since relocation information refers to symbols in this way.
 * Armed with all necessary information, the text and data segments
 * are relocated and the result is placed in the output file, which
 * is pasted together, ``in place'', by writing to it in several
 * different places concurrently.
 */

#ifdef	PAGE_CHECK
/*
 *  Branch with execute (no link) instructions... 
 */
#define	BBRX	0xef
#define	BBX	0x8f	/* 4 byte */
#define	BNBRX	0xe9
#define	BNBX	0x89	/* 4 byte */
/*
 * D format instruction limits...
 */
#define	MIN_D_OP	0xc0
#define	MAX_D_OP	0xde
/*
 * Other 4 byte instructions...
 */
#define	BALAX	0x8b
#define	BALIX	0x8d
#define	BALA	0x8a
#define	BALI	0x8c
#define	BB	0x8e
#define	BNB	0x88
#endif	PAGE_CHECK

/*
 * Internal data structures
 *
 * All internal data structures are segmented and dynamically extended.
 * The basic structures hold 1103 (NSYM) symbols, ~~200 (NROUT)
 * referenced library members, and 100 (NSYMPR) private (local) symbols
 * per object module.  For large programs and/or modules, these structures
 * expand to be up to 40 (NSEG) times as large as this as necessary.
 */
#define        NSEG    40              /* Number of segments, each data structure */
#define        NSYM    1103            /* Number of symbols per segment */
#define        NROUT   250             /* Number of library references per segment */
#define        NSYMPR  100             /* Number of private symbols per segment */

/*
 * Structure describing each symbol table segment.
 * Each segment has its own hash table.  We record the first
 * address in and first address beyond both the symbol and hash
 * tables, for use in the routine symx and the lookup routine respectively.
 * The symfree routine also understands this structure well as it used
 * to back out symbols from modules we decide that we don't need in pass 1.
 *
 * Csymseg points to the current symbol table segment;
 * csymseg->sy_first[csymseg->sy_used] is the next symbol slot to be allocated,
 * (unless csymseg->sy_used == NSYM in which case we will allocate another
 * symbol table segment first.)
 * 
 * JSW 6/25/86 - Added hssymseg, points to current segment used for 'hidden'
 * symbols used during processing of archive modules.
 */
struct symseg {
       struct  nlist *sy_first;        /* base of this alloc'ed segment */
       struct  nlist *sy_last;         /* end of this segment, for n_strx */
       int     sy_used;                /* symbols used in this seg */
       struct  nlist **sy_hfirst;      /* base of hash table, this seg */
       struct  nlist **sy_hlast;       /* end of hash table, this seg */
} symseg[NSEG], *csymseg;
#ifndef ibm370
	struct symseg *hssymseg;
#endif ibm370

/*
 * The lookup routine uses quadratic rehash.  Since a quadratic rehash
 * only probes 1/2 of the buckets in the table, and since the hash
 * table is segmented the same way the symbol table is, we make the
 * hash table have twice as many buckets as there are symbol table slots
 * in the segment.  This guarantees that the quadratic rehash will never
 * fail to find an empty bucket if the segment is not full and the
 * symbol is not there.
 */
#define        HSIZE   (NSYM*2)

/*
 * Xsym converts symbol table indices (ala x) into symbol table pointers.
 * Symx (harder, but never used in loops) inverts pointers into the symbol
 * table into indices using the symseg[] structure.
 */
#define        xsym(x) (symseg[(x)/NSYM].sy_first+((x)%NSYM))
/* symx() is a function, defined below */

struct nlist cursym;           /* current symbol */
struct nlist *lastsym;         /* last symbol entered */
struct nlist *nextsym;         /* next available symbol table entry */
struct nlist *addsym;          /* first sym defined during incr load */
int    nsym;                   /* pass2: number of local symbols in a.out */

#ifndef ibm370
/*
 * JSW 6/25/86 - added hsnextsym, points to next symbol table entry available
 * for 'hidden' symbols.
 */
struct nlist *hsnextsym;       /* next available hidden symbol */
#endif ibm370
/* nsym + symx(nextsym) is the symbol table size during pass2 */

struct nlist **lookup(), **slookup();
struct nlist *p_etext, *p_edata, *p_end, *entrypt;

/*
 * Definitions of segmentation for library member table.
 * For each library we encounter on pass 1 we record pointers to all
 * members which we will load on pass 2.  These are recorded as offsets
 * into the archive in the library member table.  Libraries are
 * separated in the table by the special offset value -1.
 */
off_t  li_init[NROUT];
struct libseg {
       off_t   *li_first;
       int     li_used;
       int     li_used2;
} libseg[NSEG] = {
       li_init, 0, 0,
}, *clibseg = libseg;

/*
 * In processing each module on pass 2 we must relocate references
 * relative to external symbols.  These references are recorded
 * in the relocation information as relative to local symbol numbers
 * assigned to the external symbols when the module was created.
 * Thus before relocating the module in pass 2 we create a table
 * which maps these internal numbers to symbol table entries.
 * A hash table is constructed, based on the local symbol table indices,
 * for quick lookup of these symbols.
 */
#define        LHSIZ   31
struct local {
       int     l_index;                /* index to symbol in file */
       struct  nlist *l_symbol;        /* ptr to symbol table */
       struct  local *l_link;          /* hash link */
} *lochash[LHSIZ], lhinit[NSYMPR];
struct locseg {
       struct  local *lo_first;
       int     lo_used;
} locseg[NSEG] = {
       lhinit, 0
}, *clocseg;

/*
 * Libraries are typically built with a table of contents,
 * which is the first member of a library with special file
 * name __.SYMDEF and contains a list of symbol names
 * and with each symbol the offset of the library member which defines
 * it.  The loader uses this table to quickly tell which library members
 * are (potentially) useful.  The alternative, examining the symbol
 * table of each library member, is painfully slow for large archives.
 *
 * See <ranlib.h> for the definition of the ranlib structure and an
 * explanation of the __.SYMDEF file format.
 */
int    tnum;           /* number of symbols in table of contents */
int    ssiz;           /* size of string table for table of contents */
struct ranlib *tab;    /* the table of contents (dynamically allocated) */
char   *tabstr;        /* string table for table of contents */

/*
 * We open each input file or library only once, but in pass2 we
 * (historically) read from such a file at 2 different places at the
 * same time.  These structures are remnants from those days,
 * and now serve only to catch ``Premature EOF''.
 * In order to make I/O more efficient, we provide routines which
 * work in hardware page sizes. The associated constants are defined
 * as BLKSIZE, BLKSHIFT, and BLKMASK.
 * (MRL:  BLKSIZE is also used for padding demand paged files,
 *        and so has been changed from 1024 to 2048 (AWS pagesize)
 * )
 */
#ifndef ibm370
#define BLKSIZE 2048
#define BLKSHIFT 11
#else ibm370
#define BLKSIZE 1024
#define BLKSHIFT 10
#endif ibm370
#define BLKMASK (BLKSIZE - 1)
typedef struct {
       short   *fakeptr;
       int     bno;
       int     nibuf;
       int     nuser;
       char    buff[BLKSIZE];
} 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;

/*
 * Header from the a.out and the archive it is from (if any).
 */
struct exec filhdr;
struct ar_hdr archdr;
#define        OARMAG 0177545

/*
 * Options.
 */
int    trace;
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    Tflag;	       /* a -T option has been specified */
int    Mflag;          /* print rudimentary load map */
int    nflag;          /* pure procedure */
int    dflag;          /* define common even with rflag */
int    zflag;          /* demand paged  */
long   hsize;          /* size of hole at beginning of data to be squashed */
int    Aflag;          /* doing incremental load */
int    Nflag;          /* want impure a.out */
int    funding;        /* reading fundamental file for incremental load */
int    yflag;          /* number of symbols to be traced */
char   **ytab;         /* the symbols */
/*
 * Flags controlling branch and execute padding...
 */
#ifdef	PAGE_CHECK
int	wflag;		/* warn of all modules with no solution...*/
int	Cflag;		/* padd Complete list of modules...*/
int	Fflag = 1;	/* padd all but first module...*/
int	Pflag;		/* Prevent all padding...*/
#endif	PAGE_CHECK

#ifndef ibm370
/* This flag added for loading of the Kernel where the data
 * must be loaded directly after the text
 */
int    Kflag;
#endif ibm370
/*
 * These are the cumulative sizes, set in pass 1, which
 * appear in the a.out header when the loader is finished.
 */
off_t  tsize, dsize, bsize, trsize, drsize, ssize;

/*
 * Symbol relocation: c?rel is a scale factor which is
 * added to an old relocation to convert it to new units;
 * i.e. it is the difference between segment origins.
 * (Thus if we are loading from a data segment which began at location
 * 4 in a .o file into an a.out where it will be loaded starting at
 * 1024, cdrel will be 1020.)
 */
long   ctrel, cdrel, cbrel;

/*
 * Textbase is the start address of all text, 0 unless given by -T.
 * Database is the base of all data, computed before and used during pass2.
 */
long   textbase, database;

#ifndef ibm370
/*
 * DATA_SEGMENT is where the data for pure procedures is relocated to
 * (ibm032 implementation - MRL)
 */
#define DATA_SEGMENT 0x10000000
#endif ibm370
/*
 * The base addresses for the loaded text, data and bss from the
 * current module during pass2 are given by torigin, dorigin and borigin.
 */
long   torigin, dorigin, borigin;

/*
 * Errlev is nonzero when errors have occured.
 * Delarg is an implicit argument to the routine delexit
 * which is called on error.  We do ``delarg = errlev'' before normal
 * exits, and only if delarg is 0 (i.e. errlev was 0) do we make the
 * result file executable.
 */
int    errlev;
int    delarg  = 4;

/*
 * The biobuf structure and associated routines are used to write
 * into one file at several places concurrently.  Calling bopen
 * with a biobuf structure sets it up to write ``biofd'' starting
 * at the specified offset.  You can then use ``bwrite'' and/or ``bputc''
 * to stuff characters in the stream, much like ``fwrite'' and ``fputc''.
 * Calling bflush drains all the buffers and MUST be done before exit.
 */
struct biobuf {
       short   b_nleft;                /* Number free spaces left in b_buf */
/* Initialize to be less than BUFSIZ initially, to boundary align in file */
       char    *b_ptr;                 /* Next place to stuff characters */
       char    b_buf[BUFSIZ];          /* The buffer itself */
       off_t   b_off;                  /* Current file offset */
       struct  biobuf *b_link;         /* Link in chain for bflush() */
} *biobufs;
#define        bputc(c,b) ((b)->b_nleft ? (--(b)->b_nleft, *(b)->b_ptr++ = (c)) \
                      : bflushc(b, c))
int    biofd;
off_t  boffset;
struct biobuf *tout, *dout, *trout, *drout, *sout, *strout;

/*
 * Offset is the current offset in the string file.
 * Its initial value reflects the fact that we will
 * eventually stuff the size of the string table at the
 * beginning of the string table (i.e. offset itself!).
 */
off_t	offset = sizeof (off_t);
int    ofilfnd;                /* -o given; otherwise move l.out to a.out */
char   *ofilename = "l.out";
int    ofilemode;              /* respect umask even for unsucessful ld's */
int    infil;                  /* current input file descriptor */
char   *filname;               /* and its name */

/*
 * Base of the string table of the current module (pass1 and pass2).
 */
char   *curstr;

/*
 * System software page size, as returned by getpagesize.
 */
int    pagesize;

char   get();
int    delexit();
char   *savestr();

/*
 * Default library names
 */
#define NDEFDIRS 	3	/* Predefined directories */
#define NDIRS		32
int	ndir;			/* Number of directories to be processed */
struct  Libn {
	char	*l_name;
	int	l_cpos;
}	libn[NDIRS];
char *malloc();

#ifndef ibm370
/*
 * Some info and constants for handling the BI and BA format pc relative,
 * address displacement which may need resolution to an external name.
 * Also constants for split addresses.
 */
#define MAX_POS_20BITS	524287
#define MAX_NEG_20BITS	-524288
#define BI_LO_OP	0x88
#define BI_HI_OP	0x8f
#define BALA_OP		0x8a
#define BALI_OP		0x8c
#define CAU_OP		0xd8
#define CAL_OP		0xc8
#define JI_HI_OP	0x0f

short	low_half;	/* low signed split addr */
int	bala_op;	/* flag for bala or balax */
#endif ibm370
#define LOW_BIT		1
#define IS_ODD(xx)	((long)(xx) & LOW_BIT)
/*
 * Data and constants for checking old-calling-sequence and new-calling-
 * sequence flags (.oVocs and .oVncs) and others that may checked in the future
 */
static char *ovsym[] = {
	".oVocs",
	".oVncs",
#ifndef ibm370
	"start",
	"FPaGET0",
	"FPGLUE"
#endif ibm370
};
#define OCSN	0
#define NCSN	1
#ifdef ibm370
#define	NOVSYM	2
#else ibm370
#define CRT0N	2
#define NFLN	3
#define RTFLN	4
#define	NOVSYM	5
#endif ibm370
char	sawovsym[NOVSYM];	/* per file flags */
char	sawocs, sawncs;		/* global flags */
#ifndef ibm370
char	sawnfl=0, sawrtfl=0, rtflwarned=0;	/* global flags */
#endif ibm370
int	overrflg;		/* mixed situation */

/*
 *  Padding declarations...
 */
#ifdef	PAGE_CHECK
struct	pad_item {
	long pad_size;
	struct pad_item *next;
};
struct	pad_item pad_list, *pad_ptr;
char	*words, pad_4bytes[] = {0x60, 0x00, 0x60, 0x00};
long	check_file(), check_text();
#endif	PAGE_CHECK

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

       if (signal(SIGINT, SIG_IGN) != SIG_IGN) {
               signal(SIGINT, delexit);
               signal(SIGTERM, delexit);
       }
       if (argc == 1)
               exit(4);
#ifndef	ibm370
	pagesize = getpagesize();
#else ibm370
/*
 * getpagesize() should work on 370 as well; I am leaving this kluge in here
 * to avoid complications in building ld on an rt as a cross-development
 * tool.  (The getpagesize() it would get is the 4.3/rt version, returning
 * 2048, instead of the 370 version that returns 4096.  This puts all
 * data-segment relocation off by 2048.)
 */
#define PAGESIZE   0x1000	/* 0x1000 */	
	pagesize = PAGESIZE;
	textbase = 0x000000; /* default for AMBLER */
#endif ibm370
#ifdef	PAGE_CHECK
	pad_ptr = &pad_list;
	if( NULL == (words = (char *)malloc(pagesize)))
		error(1, "Malloc:  no space.");
#endif	PAGE_CHECK
	/* 
	 * Pull out search directories. Mimic 4.3 using kludge.
	 */
	for (c = 1; c < argc; c++) {
		ap = argv[c];
		if (ap[0] == '-' && ap[1] == 'L') {
			char *p;
			int len;
			if (ap[2] == 0)
				error(1, "-L: pathname missing");
			if (ndir >= NDIRS - NDEFDIRS)
				error(1, "-L: too many directories");
			len = strlen(&ap[2]);
			p = malloc(len + 20); /* length of composite arg + null */
			if(p == (char *)0)
			    error(1,"Malloc:no space");
			strcpy(p, &ap[2]);
			strcat(p, "/libxxxxxxxxxxxxxxx");
			libn[ndir].l_name = p;
			libn[ndir++].l_cpos = len + sizeof("/lib") - 1;	/* /lib was appended */
		}
	}
	/* add default search directories */
#if defined(ibm032) && defined(ibm370)
	/* Cross environment root is in /usr/370root */
	libn[ndir].l_name = "/usr/370root/lib/libxxxxxxxxxxxxxxx";
	libn[ndir++].l_cpos = 20;

	libn[ndir].l_name = "/usr/370root/usr/lib/libxxxxxxxxxxxxxxx";
	libn[ndir++].l_cpos = 24;

	libn[ndir].l_name = "/usr/370root/usr/local/lib/libxxxxxxxxxxxxxxx";
	libn[ndir++].l_cpos = 30;
#else
	libn[ndir].l_name = "/lib/libxxxxxxxxxxxxxxx";
	libn[ndir++].l_cpos = 8;

	libn[ndir].l_name = "/usr/lib/libxxxxxxxxxxxxxxx";
	libn[ndir++].l_cpos = 12;

	libn[ndir].l_name = "/usr/local/lib/libxxxxxxxxxxxxxxx";
	libn[ndir++].l_cpos = 18;
#endif

	sawocs = sawncs = 0;
	p = argv+1;

       /*
        * Scan files once to find where symbols are defined.
        */
       for (c=1; c<argc; c++) {
               if (trace)
                       printf("%s:\n", *p);
               filname = 0;
               ap = *p++;
               if (*ap != '-') {
                       load1arg(ap);
                       continue;
               }
               for (i=1; ap[i]; i++) switch (ap[i]) {

               case 'o':
                       if (++c >= argc)
                               error(1, "-o where?");
                       ofilename = *p++;
                       ofilfnd++;
                       continue;
               case 'u':
               case 'e':
                       if (++c >= argc)
                               error(1, "-u or -c: arg missing");
                       enter(slookup(*p++));
                       if (ap[i]=='e')
                               entrypt = lastsym;
                       continue;
               case 'H':
                       if (++c >= argc)
                               error(1, "-H: arg missing");
                       if (tsize!=0)
                               error(1, "-H: too late, some text already loaded");
                       hsize = atoi(*p++);
                       continue;
               case 'A':
                       if (++c >= argc)
                               error(1, "-A: arg missing");
                       if (Aflag)
                               error(1, "-A: only one base file allowed");
                       Aflag = 1;
                       nflag = 0;
                       funding = 1;
                       load1arg(*p++);
                       trsize = drsize = tsize = dsize = bsize = 0;
                       ctrel = cdrel = cbrel = 0;
                       funding = 0;
                       addsym = nextsym;
                       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++);
			Tflag++;
                       continue;
               case 'l':
                       save = ap[--i];
                       ap[i]='-';
                       load1arg(&ap[i]);
                       ap[i]=save;
                       goto next;
               case 'M':
                       Mflag++;
                       continue;
               case 'x':
                       xflag++;
                       continue;
               case 'X':
                       Xflag++;
                       continue;
               case 'S':
                       Sflag++;
                       continue;
               case 'r':
                       rflag++;
                       arflag++;
			if (!Tflag) {
			  if (tsize!=0)
			     error(1, "-r: too late, some text already loaded");
			  textbase = 0;
			}
#ifdef	PAGE_CHECK
                       Pflag = 1;
                       Cflag = Fflag = 0;
#endif	PAGE_CHECK
                       continue;
               case 's':
                       sflag++;
                       xflag++;
                       continue;
               case 'n':
                       nflag++;
                       Nflag = zflag = 0;
                       continue;
               case 'N':
                       Nflag++;
                       nflag = zflag = 0;
                       continue;
               case 'd':
                       dflag++;
                       continue;
               case 'i':
                       printf("ld: -i ignored\n");
                       continue;
               case 't':
                       trace++;
                       continue;
               case 'y':
                       if (ap[i+1] == 0)
                               error(1, "-y: symbol name missing");
                       if (yflag == 0) {
                               ytab = (char **)calloc(argc, sizeof (char **));
                               if (ytab == 0)
                                       error(1, "ran out of memory (-y)");
                       }
                       ytab[yflag++] = &ap[i+1];
                       goto next;
               case 'z':
#ifndef ibm370
		       zflag++;
		       Nflag = nflag = 0;
#endif ibm370
                       continue;
#ifndef ibm370

		       /* This flag added for kernel loading where data must
			* be loaded directly after the text
			*/
		case 'K':
			Kflag++;
			continue;
		case 'L':
			goto next;

#endif ibm370
#ifdef	PAGE_CHECK
               case 'w':
                       wflag++;
                       continue;
               case 'C':
                       Cflag++;
                       Pflag = Fflag = 0;
                       continue;
               case 'F':
                       Fflag++;
		       Cflag = Pflag = 0;
                       continue;
               case 'P':
                       Pflag++;
                       Fflag = Cflag = 0;
                       continue;
#endif	PAGE_CHECK
               default:
                       filname = savestr("-x");        /* kludge */
                       filname[1] = ap[i];             /* kludge */
                       archdr.ar_name[0] = 0;          /* kludge */
                       error(1, "bad flag");
               }
next:
               ;
       }
       if (rflag == 0 && Nflag == 0 && nflag == 0)
#ifndef ibm370
               zflag++;
#else ibm370
		nflag++;
#endif ibm370
       endload(argc, argv);
       exit(0);
}

/*
 * Convert a ascii string which is a hex number.
 * Used by -T and -D options.
 */
htoi(p)
       register char *p;
{
       register int c, n;

       n = 0;
       while (c = *p++) {
               n <<= 4;
               if (isdigit(c))
                       n += c - '0';
               else if (c >= 'a' && c <= 'f')
                       n += 10 + (c - 'a');
               else if (c >= 'A' && c <= 'F')
                       n += 10 + (c - 'A');
               else
                       error(1, "badly formed hex number");
       }
       return (n);
}

delexit()
{
       struct stat stbuf;
       long size;
       char c = 0;

       bflush();
       unlink("l.out");
       if (delarg==0 && Aflag==0)
               chmod(ofilename, ofilemode);
       /*
        * We have to insure that the last block of the data segment
        * is allocated a full BLKSIZE block. If the underlying
        * file system allocates frags that are smaller than BLKSIZE,
        * a full zero filled BLKSIZE block needs to be allocated so
        * that when it is demand paged, the paged in block will be
        * appropriately filled with zeros.
        */
       fstat(biofd, &stbuf);
       size = round(stbuf.st_size, BLKSIZE);
       if (!rflag && size > stbuf.st_size) {
               lseek(biofd, size - 1, 0);
               write(biofd, &c, 1);
       }
       exit (delarg);
}

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

       clibseg = libseg;
       filname = 0;
#ifdef	PAGE_CHECK
       pad_ptr = &pad_list;
#endif
       middle();
       setupout();
       p = argv+1;
       for (c=1; c<argc; c++) {
               ap = *p++;
               if (trace)
                       printf("%s:\n", ap);
               if (*ap != '-') {
                       load2arg(ap);
                       continue;
               }
               for (i=1; ap[i]; i++) switch (ap[i]) {

               case 'D':
                       dnum = htoi(*p);
                       if (dorigin < dnum)
                               while (dorigin < dnum)
                                       bputc(0, dout), dorigin++;
                       /* fall into ... */
               case 'T':
               case 'u':
               case 'e':
               case 'o':
               case 'H':
                       ++c;
                       ++p;
                       /* fall into ... */
               default:
                       continue;
               case 'A':
                       funding = 1;
                       load2arg(*p++);
                       funding = 0;
                       c++;
                       continue;
               case 'y':
#ifndef ibm370
	       case 'L':
#endif ibm370
                       goto next;
               case 'l':
                       ap[--i]='-';
                       load2arg(&ap[i]);
                       goto next;
               }
next:
               ;
       }
       finishout();
}

/*
 * Scan file to find defined symbols.
 */
load1arg(cp)
       register char *cp;
{
       register struct ranlib *tp;
       off_t nloc;
       int kind;

       kind = getfile(cp);
#ifndef ibm370
	if (Mflag > 1 && !kind)
		printf("%s: ",filname);
       else if (Mflag)
#else ibm370
	if (Mflag)
#endif ibm370
               printf("%s\n", filname);
       switch (kind) {

       /*
        * Plain file.
        */
       case 0:
               if ( load1(0, 0L) && Mflag>1 )
			printncs();
               break;

       /*
        * Archive without table of contents.
        * (Slowly) process each member.
        */
       case 1:
               error(-1,
"warning: archive has no table of contents; add one using ranlib(1)");
               nloc = SARMAG;
               while (step(nloc))
                       nloc += sizeof(archdr) +
                           round(atol(archdr.ar_size), sizeof (short));
               break;

       /*
        * Archive with table of contents.
        * Read the table of contents and its associated string table.
        * Pass through the library resolving symbols until nothing changes
        * for an entire pass (i.e. you can get away with backward references
        * when there is a table of contents!)
        */
       case 2:
               nloc = SARMAG + sizeof (archdr);
               dseek(&text, nloc, sizeof (tnum));
               mget((char *)&tnum, sizeof (tnum), &text);
               nloc += sizeof (tnum);
               tab = (struct ranlib *)malloc(tnum);
               if (tab == 0)
                       error(1, "ran out of memory (toc)");
               dseek(&text, nloc, tnum);
               mget((char *)tab, tnum, &text);
               nloc += tnum;
printf("");
               tnum /= sizeof (struct ranlib);
               dseek(&text, nloc, sizeof (ssiz));
               mget((char *)&ssiz, sizeof (ssiz), &text);
               nloc += sizeof (ssiz);
               tabstr = (char *)malloc(ssiz);
               if (tabstr == 0)
                       error(1, "ran out of memory (tocstr)");
               dseek(&text, nloc, ssiz);
               mget((char *)tabstr, ssiz, &text);
               for (tp = &tab[tnum]; --tp >= tab;) {
                       if (tp->ran_un.ran_strx < 0 ||
                           tp->ran_un.ran_strx >= ssiz)
                               error(1, "mangled archive table of contents");
                       tp->ran_un.ran_name = tabstr + tp->ran_un.ran_strx;
               }
               while (ldrand())
                       continue;
               cfree((char *)tab);
               cfree(tabstr);
               nextlibp(-1);
               break;

       /*
        * Table of contents is out of date, so search
        * as a normal library (but skip the __.SYMDEF file).
        */
       case 3:
               error(-1,
"warning: table of contents for archive is out of date; rerun ranlib(1)");
               nloc = SARMAG;
               do
                       nloc += sizeof(archdr) +
                           round(atol(archdr.ar_size), sizeof(short));
               while (step(nloc));
               break;
       }
       close(infil);
}

/*
 * Advance to the next archive member, which
 * is at offset nloc in the archive.  If the member
 * is useful, record its location in the liblist structure
 * for use in pass2.  Mark the end of the archive in libilst with a -1.
 */
step(nloc)
       off_t nloc;
{

       dseek(&text, nloc, (long) sizeof archdr);
       if (text.size <= 0) {
               nextlibp(-1);
               return (0);
       }
       getarhdr();
       if (load1(1, nloc + (sizeof archdr)))
               nextlibp(nloc);
       return (1);
}

/*
 * Record the location of a useful archive member.
 * Recording -1 marks the end of files from an archive.
 * The liblist data structure is dynamically extended here.
 */
nextlibp(val)
       off_t val;
{

       if (clibseg->li_used == NROUT) {
               if (++clibseg == &libseg[NSEG])
                       error(1, "too many files loaded from libraries");
               clibseg->li_first = (off_t *)malloc(NROUT * sizeof (off_t));
               if (clibseg->li_first == 0)
                       error(1, "ran out of memory (nextlibp)");
       }
       clibseg->li_first[clibseg->li_used++] = val;
       if (val != -1 && Mflag)
		if (Mflag > 1) {
		   printf("\t%s: ", archdr.ar_name);
		   printncs();
		} else
                   printf("\t%s\n", archdr.ar_name);
}

/*
 * One pass over an archive with a table of contents.
 * Remember the number of symbols currently defined,
 * then call step on members which look promising (i.e.
 * that define a symbol which is currently externally undefined).
 * Indicate to our caller whether this process netted any more symbols.
 */
ldrand()
{
       register struct nlist *sp, **hp;
       register struct ranlib *tp, *tplast;
       off_t loc;
       int nsymt = symx(nextsym);

       tplast = &tab[tnum-1];
       for (tp = tab; tp <= tplast; tp++) {
               if ((hp = slookup(tp->ran_un.ran_name)) == 0)
                       continue;
               sp = *hp;
#ifdef ibm370
		if (!sp) {
			continue;
		}
#endif ibm370
               if (sp->n_type != N_EXT+N_UNDF)
                       continue;
               step(tp->ran_off);
               loc = tp->ran_off;
               while (tp < tplast && (tp+1)->ran_off == loc)
                       tp++;
       }
       return (symx(nextsym) != nsymt);
}

/*
 * Examine a single file or archive member on pass 1.
 */
load1(libflg, loc)
       off_t loc;
{
#ifndef ibm370
/*JSW 6/25/86 -
 * added hscursym, p3hscursym, thscursym, hstmpseg, p3hstmpseg and hp
 */
       register struct nlist *sp, *hscursym, **hp;
       struct symseg *hstmpseg, *p3hstmpseg;
       struct nlist *savnext, *thscursym, *p3hscursym;
#else ibm370
	register struct nlist *sp;
	struct nlist *savnext;
#endif ibm370
#ifdef	PAGE_CHECK
	long	pad_size;
	off_t	objloc;
#endif	PAGE_CHECK
       int ndef, nlocal, type, size, nsymt;
       register int i;
       off_t maxoff;
       struct stat stb;

	resetovsym();
       readhdr(loc);
       if (filhdr.a_syms == 0) {
               if (filhdr.a_text+filhdr.a_data == 0)
                       return (0);
               error(1, "no namelist");
       }
       if (libflg)
               maxoff = atol(archdr.ar_size);
       else {
               fstat(infil, &stb);
               maxoff = stb.st_size;
       }
       if (N_STROFF(filhdr) + sizeof (off_t) >= maxoff)
               error(1, "too small (old format .o?)");
       ctrel = tsize;
       cdrel += dsize; cbrel += bsize;
       ndef = 0;
       nlocal = sizeof(cursym);
       savnext = nextsym;
#ifndef ibm370
/*
 * JSW 6/25/86 - Next several lines added to initialize 'hidden' symbol pointers
 */

       slookup(" ");
#ifdef DEBUG
printf("csymseg = %x\n",csymseg);
#endif

       hstmpseg = hssymseg = csymseg;
       hscursym = hsnextsym = csymseg->sy_first + csymseg->sy_used;

/* if at the end of the current segment go to the next one */
/*
	if ( hscursym == hstmpseg->sy_first + NSYM )
               {
               hstmpseg++;
               hscursym = hstmpseg->sy_first + hstmpseg->sy_used;
               }
*/
	if ( hsnextsym == hssymseg->sy_first+NSYM )
		{
		hssymseg++;
		hstmpseg++;
		hscursym = hsnextsym = hssymseg->sy_first + hssymseg->sy_used;
		}
	if ( hssymseg->sy_first == 0 )
		{
		makeseg( hssymseg );
		hscursym = hsnextsym = hssymseg->sy_first + hssymseg->sy_used;
		}

/*
 */

#endif ibm370
#ifdef	PAGE_CHECK
       objloc = loc;	/* remember location of start of object */
#endif	PAGE_CHECK
       loc += N_SYMOFF(filhdr);
       dseek(&text, loc, filhdr.a_syms);
       dseek(&reloc, loc + filhdr.a_syms, sizeof(off_t));
       mget(&size, sizeof (size), &reloc);
       dseek(&reloc, loc + filhdr.a_syms+sizeof (off_t), size-sizeof (off_t));
       curstr = (char *)malloc(size);
       if (curstr == NULL)
               error(1, "no space for string table");
       mget(curstr+sizeof(off_t), size-sizeof(off_t), &reloc);
#ifndef ibm370
/*
 * JSW 6/25/86
 * Read all symbols from module into memory and convert string table offsets
 * to pointers, and convert segment relative offsets to addresses if possible.
 * Symbol entries are stored in the symbol table but are hidden from the 
 * symbol table access routines because they are not entered in the hash table
 * and are stored passed what is thought to be the end of the symbol table.
 * They are not entered now so that there are no conflicts with previously
 * entered symbols.
 */
#endif ibm370
       while (text.size > 0) {
               mget((char *)&cursym, sizeof(struct nlist), &text);
               if (cursym.n_un.n_strx) {
                       if (cursym.n_un.n_strx<sizeof(size) ||
                           cursym.n_un.n_strx>=size)
                               error(1, "bad string table index (pass 1)");
                       cursym.n_un.n_name = curstr + cursym.n_un.n_strx;
               }
		/*
		 * Look for symbols that affect linkability;
		 * note them in sawovsym.
		 */
		if (cursym.n_un.n_name)
			for (i = 0; i < NOVSYM; i++)
			   /* fast check for 2d character! */
			   if (ovsym[i][1] == cursym.n_un.n_name[1] &&
			   !strcmp(ovsym[i], cursym.n_un.n_name)) {
				sawovsym[i] = 1;
			   }
               type = cursym.n_type;
               if ((type&N_EXT)==0) {
                       if (Xflag==0 || cursym.n_un.n_name[0]!='L' ||
                           type & N_STAB)
                               nlocal += sizeof cursym;
                       continue;
               }
               symreloc();
#ifndef ibm370
               hsenter();   /* enter as hidden symbol */
       }

/* save for third pass through table */
       p3hscursym = hscursym;
       p3hstmpseg = hstmpseg;


/*
 * JSW 6/25/86
 * examine the symbols from the current module and determine if the module
 * should be included or not, also flag symbols that are definitions for
 * undefined symbols that have been already entered into the symbol table.
 * NOTE: Uses the n_other field of the symbol table entries.  If this field
 * is needed in the future this code will have to change.
 */
       while ( hscursym != hsnextsym )
               {

#ifdef DEBUG
	printf("load1:hscursym = %x, hsnextsym = %x\n",hscursym, hsnextsym );
#endif

               /* get the curent symbol */
               cursym.n_un.n_name = hscursym->n_un.n_name;
               cursym.n_type = hscursym->n_type;
               cursym.n_value = hscursym->n_value;

#ifdef DEBUG
	printf("working on symbol: %s type: %x value: %x\n", cursym.n_un.n_name, cursym.n_type, cursym.n_value );
#endif

               /* save the one we are working on now */
               thscursym = hscursym;

               /* set up next hidden symbol for next time through the loop */
               hscursym++;
               if ( hscursym == hstmpseg->sy_first + NSYM )
                       {
                       hstmpseg++;
                       hscursym = hstmpseg->sy_first + hstmpseg->sy_used;
                       }

/*
 * determine if this module should be included
 */
               hp = lookup();
               if(*hp==0)
                       continue;
               if ((sp = *hp)->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;

/*
 * JSW 6/25/86
 * if it passes all the tests so far it defines the symbol if the module is 
 * included.
 */
               thscursym->n_other=1;

/*
 * JSW 6/25/86
 * does not include module if _sym has a value other than zero, _.sym exists
 * and is in the text segment
 */
               if( sp->n_value != 0 && findud() )
                       continue;
               ndef++;  /* include this module */

       }
/*
 * The module is to be included if it is not a library module, or if it is
 * a library module and it was determined that it is needed.
 */
       if (libflg==0 || ndef) {

#ifdef DEBUG
printf("including this module\n");
#endif
/*
 *  Compute padding for this module, if necessary...
 */
#ifdef	PAGE_CHECK
	if (Cflag) {
		if ((pad_size = check_file(&text, objloc, tsize)) < 0) {
			pad_size = 0;
			if (wflag)
				error(-1,"warning:  unable to properly align object");
		}
	} else {
		pad_size = 0;
		if (Fflag) {
			Fflag = 0;
			Cflag++;
			if (wflag && check_file(&text, objloc, tsize))
				error(-1,"warning:  unable to properly align first object");
                }
	}
#endif	PAGE_CHECK

               /* restore to their proper starting values */
               hscursym = p3hscursym;
               hstmpseg = p3hstmpseg;

               while ( hscursym != hsnextsym )
                       {

                       /* get the curent symbol */
                       cursym.n_un.n_name = hscursym->n_un.n_name;
                       cursym.n_type = hscursym->n_type;
                       cursym.n_value = hscursym->n_value;
#ifdef	PAGE_CHECK
                       switch (cursym.n_type & 017) {
                         case N_TEXT:
                         case N_EXT+N_TEXT:
                           cursym.n_value += pad_size;
                       }
#endif
                       cursym.n_other = hscursym->n_other;

#ifdef DEBUG
	printf("pass3 - working on symbol: %s type: %x value: %x\n", cursym.n_un.n_name, cursym.n_type, cursym.n_value );
#endif

                       /* update current hidden symbol */
                       hscursym++;
                       if ( hscursym == hstmpseg->sy_first + NSYM )
                               {
                               hstmpseg++;
                               hscursym = hstmpseg->sy_first + hstmpseg->sy_used;
                               }

                   /* enter into the symbol table if it is not already there */
#endif ibm370
                       if (enter(lookup()))
                               continue;
#ifndef ibm370
#ifdef DEBUG
	printf("symbol: %s already entered\n",cursym.n_un.n_name );
#endif

                   /* if this defines a previously entered symbol then do it */
                       if ( cursym.n_other )
                               {
#ifdef DEBUG
	printf("symbol: %s defines previously entered symbol in other module\n",cursym.n_un.n_name );
#endif
                               lastsym->n_type = cursym.n_type;
                               lastsym->n_value = cursym.n_value;
                               }
			else
				{
#endif ibm370
               			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;
#ifndef ibm370
#ifdef DEBUG
	printf("symbol: %s defines previously entered symbol in this module\n",cursym.n_un.n_name );
#endif
#else ibm370
		ndef++;
#endif ibm370
                               	sp->n_type = cursym.n_type;
                              	sp->n_value = cursym.n_value;

				}
#ifndef ibm370


                       }

               /* update segment sizes */
#else ibm370
	if (libflg==0 || ndef) {
#endif ibm370
#ifdef	PAGE_CHECK
               tsize += filhdr.a_text + pad_size;
               pad_ptr->next = (struct pad_item *)malloc(sizeof(struct pad_item));
               if (pad_ptr->next == NULL)
                 error(1,"out of memory");
               pad_ptr->pad_size = pad_size;
               pad_ptr = pad_ptr->next;
#else
               tsize += filhdr.a_text;
#endif	PAGE_CHECK
               dsize += round(filhdr.a_data, sizeof (long));
               bsize += round(filhdr.a_bss, sizeof (long));
               ssize += nlocal;
               trsize += filhdr.a_trsize;
               drsize += filhdr.a_drsize;
               if (funding)
                       textbase = (*slookup("_end"))->n_value;
               nsymt = symx(nextsym);
               for (i = symx(savnext); i < nsymt; i++) {
                       sp = xsym(i);
                       sp->n_un.n_name = savestr(sp->n_un.n_name);
               }
		checkovsym();
               free(curstr);
               return (1);
       }
       /*
        * No symbols defined by this library member.
        * Rip out the hash table entries and reset the symbol table.
        */
       symfree(savnext);
       free(curstr);
       return(0);
}

middle()
{
       register struct nlist *sp;
       long csize, t, corigin, ocsize;
       int nund, rnd;
       char s;
       register int i;
       int nsymt;

       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.
        */
       nsymt = symx(nextsym);
       if (rflag==0) {
               for (i = 0; i < nsymt; i++) {
                       sp = xsym(i);
                       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 = zflag = 0;
       /*
        * Assign common locations.
        */
       csize = 0;
       if (!Aflag)
               addsym = symseg[0].sy_first;
#ifndef ibm370
       if (Kflag)
	       database = round(tsize+textbase,
			  (nflag||zflag?pagesize:sizeof (long)));
       else
	       database = nflag||zflag? DATA_SEGMENT : (round(tsize+textbase,
                          sizeof (long))); /* MRL */
#else ibm370
       database = round(tsize+textbase,
	    (nflag||zflag? pagesize : sizeof (long)));
#endif ibm370
       database += hsize;
       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 (i = symx(addsym); i < nsymt; i++) {
                       sp = xsym(i);
                       if ((s=sp->n_type)==N_EXT+N_UNDF &&
                           (t = sp->n_value)!=0) {
                               if (t >= sizeof (double))
                                       rnd = sizeof (double);
                               else if (t >= sizeof (long))
                                       rnd = sizeof (long);
                               else
                                       rnd = sizeof (short);
                               csize = round(csize, rnd);
                               sp->n_value = csize;
                               sp->n_type = N_EXT+N_COMM;
                               ocsize = csize;
                               csize += t;
                       }
                       if (s&N_EXT && (s&N_TYPE)==N_UNDF && s&N_STAB) {
                               sp->n_value = ocsize;
                               sp->n_type = (s&N_STAB) | (N_EXT+N_COMM);
                       }
               }
       }
       /*
        * Now set symbols to their final value
        */
       csize = round(csize, sizeof (long));
       torigin = textbase;
       dorigin = database;
       corigin = dorigin + dsize;
       borigin = corigin + csize;
       nund = 0;
       nsymt = symx(nextsym);
       for (i = symx(addsym); i<nsymt; i++) {
               sp = xsym(i);
               switch (sp->n_type & (N_TYPE+N_EXT)) {

               case N_EXT+N_UNDF:
                       if (arflag == 0)
                               errlev |= 01;
                       if ((arflag==0 || dflag) && sp->n_value==0) {
                               if (sp==p_end || sp==p_etext || sp==p_edata)
                                       continue;
                               if (nund==0)
                                       printf("Undefined:\n");
                               nund++;
                               printf("%s\n", sp->n_un.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 = (sp->n_type & N_STAB) | (N_EXT+N_BSS);
                       sp->n_value += corigin;
                       continue;
               }
       }
       if (sflag || xflag)
               ssize = 0;
       bsize += csize;
       nsym = ssize / (sizeof cursym);
       if (Aflag) {
               fixspec(p_etext,torigin);
               fixspec(p_edata,dorigin);
               fixspec(p_end,borigin);
       }
}

fixspec(sym,offset)
       struct nlist *sym;
       long offset;
{

       if(symx(sym) < symx(addsym) && sym!=0)
               sym->n_value += offset;
}

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

       if (sp == 0)
               return;
       if ((sp->n_type != N_EXT+N_UNDF || sp->n_value) && !Aflag) {
               printf("%s: ", sp->n_un.n_name);
               error(0, "user attempt to redfine loader-defined symbol");
               return;
       }
       sp->n_type = type;
       sp->n_value = val;
}

off_t  wroff;
struct biobuf toutb;

setupout()
{
       int bss;
       extern char *sys_errlist[];
       extern int errno;

       ofilemode = 0777 & ~umask(0);
       biofd = creat(ofilename, 0666 & ofilemode);
       if (biofd < 0) {
               filname = ofilename;            /* kludge */
               archdr.ar_name[0] = 0;          /* kludge */
               error(1, sys_errlist[errno]);   /* kludge */
       } else {
               struct stat mybuf;              /* kls kludge */
               fstat(biofd, &mybuf);           /* suppose file exists, wrong*/
               if(mybuf.st_mode & 0111) {      /* mode, ld fails? */
                       chmod(ofilename, mybuf.st_mode & 0666);
                       ofilemode = mybuf.st_mode;
               }
       }
       tout = &toutb;
       bopen(tout, 0);
       filhdr.a_magic = nflag ? NMAGIC : (zflag ? ZMAGIC : OMAGIC);
       filhdr.a_text = nflag ? tsize :
           round(tsize, zflag ? pagesize : sizeof (long));
       filhdr.a_data = zflag ? round(dsize, pagesize) : dsize;
       bss = bsize - (filhdr.a_data - dsize);
       if (bss < 0)
               bss = 0;
       filhdr.a_bss = bss;
       filhdr.a_trsize = trsize;
       filhdr.a_drsize = drsize;
       filhdr.a_syms = sflag? 0: (ssize + (sizeof cursym)*symx(nextsym));
       if (entrypt) {
               if (entrypt->n_type!=N_EXT+N_TEXT)
                       error(0, "entry point not in text");
               else
#ifndef	ibm370
                       filhdr.a_entry = entrypt->n_value;
#else
			filhdr.a_entry = entrypt->n_value - textbase;
#endif
       } else
#ifdef	PAGE_CHECK
               filhdr.a_entry = pad_list.pad_size;
#else
               filhdr.a_entry = 0;
#endif	PAGE_CHECK
       filhdr.a_trsize = (rflag ? trsize:0);
       filhdr.a_drsize = (rflag ? drsize:0);

       bwrite((char *)&filhdr, sizeof (filhdr), tout);

       if (zflag) {
               bflush1(tout);
               biobufs = 0;
               bopen(tout, pagesize);
       }
       wroff = N_TXTOFF(filhdr) + filhdr.a_text;
       outb(&dout, filhdr.a_data);
       if (rflag) {
               outb(&trout, filhdr.a_trsize);
               outb(&drout, filhdr.a_drsize);
       }
       if (sflag==0 || xflag==0) {
               outb(&sout, filhdr.a_syms);
               wroff += sizeof (offset);
               outb(&strout, 0);
       }
}

outb(bp, inc)
       register struct biobuf **bp;
{

       *bp = (struct biobuf *)malloc(sizeof (struct biobuf));
       if (*bp == 0)
               error(1, "ran out of memory (outb)");
       bopen(*bp, wroff);
       wroff += inc;
}

load2arg(acp)
char *acp;
{
       register char *cp;
       off_t loc;

       cp = acp;
       if (getfile(cp) == 0) {
               while (*cp)
                       cp++;
               while (cp >= acp && *--cp != '/');
               mkfsym(++cp);
               load2(0L);
       } else {        /* scan archive members referenced */
               for (;;) {
                       if (clibseg->li_used2 == clibseg->li_used) {
                               if (clibseg->li_used < NROUT)
                                       error(1, "libseg botch");
                               clibseg++;
                       }
                       loc = clibseg->li_first[clibseg->li_used2++];
                       if (loc == -1)
                               break;
                       dseek(&text, loc, (long)sizeof(archdr));
                       getarhdr();
                       mkfsym(archdr.ar_name);
                       load2(loc + (long)sizeof(archdr));
               }
       }
       close(infil);
}

load2(loc)
long loc;
{
       int size;
       register struct nlist *sp;
       register struct local *lp;
       register int symno, i;
       int type;
#ifdef	PAGE_CHECK
       long pad_count;
#endif	PAGE_CHECK

       readhdr(loc);
       if (!funding) {
#ifdef	PAGE_CHECK
               ctrel = torigin + pad_ptr->pad_size;
#else
               ctrel = torigin;
#endif	PAGE_CHECK
               cdrel += dorigin;
               cbrel += borigin;
       }
       /*
        * Reread the symbol table, recording the numbering
        * of symbols for fixing external references.
        */
       for (i = 0; i < LHSIZ; i++)
               lochash[i] = 0;
       clocseg = locseg;
       clocseg->lo_used = 0;
       symno = -1;
       loc += N_TXTOFF(filhdr);
       dseek(&text, loc+filhdr.a_text+filhdr.a_data+
               filhdr.a_trsize+filhdr.a_drsize+filhdr.a_syms, sizeof(off_t));
       mget(&size, sizeof(size), &text);
       dseek(&text, loc+filhdr.a_text+filhdr.a_data+
               filhdr.a_trsize+filhdr.a_drsize+filhdr.a_syms+sizeof(off_t),
               size - sizeof(off_t));
       curstr = (char *)malloc(size);
       if (curstr == NULL)
               error(1, "out of space reading string table (pass 2)");
       mget(curstr+sizeof(off_t), size-sizeof(off_t), &text);
       dseek(&text, loc+filhdr.a_text+filhdr.a_data+
               filhdr.a_trsize+filhdr.a_drsize, filhdr.a_syms);
       while (text.size > 0) {
               symno++;
               mget((char *)&cursym, sizeof(struct nlist), &text);
               if (cursym.n_un.n_strx) {
                       if (cursym.n_un.n_strx<sizeof(size) ||
                           cursym.n_un.n_strx>=size)
                               error(1, "bad string table index (pass 2)");
                       cursym.n_un.n_name = curstr + cursym.n_un.n_strx;
               }
/* inline expansion of symreloc() */

#ifndef ibm370
/* In the RT linkage, function addresses are in data, not text.
 * The following kluge on n_type relocates function stabs into data
 * rather than text.  0x24 would have been N_FUN had we included stabs.h.
 */
		switch ((cursym.n_type == 0x24) ? N_DATA : cursym.n_type & 0x0f) {
#endif ibm370
#ifdef  ibm370
		switch (cursym.n_type & 0x0f) {
#endif ibm370
               case N_TEXT:
               case N_EXT+N_TEXT:
                       cursym.n_value += ctrel;
                       break;
               case N_DATA:
               case N_EXT+N_DATA:
                       cursym.n_value += cdrel;
                       break;
               case N_BSS:
               case N_EXT+N_BSS:
                       cursym.n_value += cbrel;
                       break;
               case N_EXT+N_UNDF:
                       break;
               default:
                       if (cursym.n_type&N_EXT)
                               cursym.n_type = N_EXT+N_ABS;
               }
/* end inline expansion of symreloc() */
               type = cursym.n_type;
               if (yflag && cursym.n_un.n_name)
                       for (i = 0; i < yflag; i++)
                               /* fast check for 2d character! */
                               if (ytab[i][1] == cursym.n_un.n_name[1] &&
                                   !strcmp(ytab[i], cursym.n_un.n_name)) {
                                       tracesym();
                                       break;
                               }
               if ((type&N_EXT) == 0) {
                       if (!sflag&&!xflag&&
                           (!Xflag||cursym.n_un.n_name[0]!='L'||type&N_STAB))
                               symwrite(&cursym, sout);
                       continue;
               }
               if (funding)
                       continue;
               if ((sp = *lookup()) == 0)
                       error(1, "internal error: symbol not found");
               if (cursym.n_type == N_EXT+N_UNDF) {
                       if (clocseg->lo_used == NSYMPR) {
                               if (++clocseg == &locseg[NSEG])
                                       error(1, "local symbol overflow");
                               clocseg->lo_used = 0;
                       }
                       if (clocseg->lo_first == 0) {
                               clocseg->lo_first = (struct local *)
                                   malloc(NSYMPR * sizeof (struct local));
                               if (clocseg->lo_first == 0)
                                       error(1, "out of memory (clocseg)");
                       }
                       lp = &clocseg->lo_first[clocseg->lo_used++];
                       lp->l_index = symno;
                       lp->l_symbol = sp;
                       lp->l_link = lochash[symno % LHSIZ];
                       lochash[symno % LHSIZ] = lp;
                       continue;
               }
               if (cursym.n_type & N_STAB)
                       continue;
               if (cursym.n_type!=sp->n_type || cursym.n_value!=sp->n_value) {
                       printf("%s: ", cursym.n_un.n_name);
                       error(0, "multiply defined");
               }
       }
       if (funding)
               return;
       dseek(&text, loc, filhdr.a_text);
       dseek(&reloc, loc+filhdr.a_text+filhdr.a_data, filhdr.a_trsize);
/*
 *  The moment of truth...
 */
#ifdef	PAGE_CHECK
       if (pad_ptr->pad_size)
           for (pad_count = 0;  pad_count < pad_ptr->pad_size;  pad_count += 4)
               bwrite(pad_4bytes, 4, tout);
#endif	PAGE_CHECK
       load2td(ctrel, torigin - textbase, 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(cdrel, dorigin - database, dout, drout);
       while (filhdr.a_data & (sizeof(long)-1)) {
               bputc(0, dout);
               filhdr.a_data++;
       }
#ifdef	PAGE_CHECK
       torigin += filhdr.a_text + pad_ptr->pad_size;
       pad_ptr = pad_ptr->next;
#else
       torigin += filhdr.a_text;
#endif	PAGE_CHECK
       dorigin += round(filhdr.a_data, sizeof (long));
       borigin += round(filhdr.a_bss, sizeof (long));
       free(curstr);
}

struct tynames {
       int     ty_value;
       char    *ty_name;
} tynames[] = {
       N_UNDF, "undefined",
       N_ABS,  "absolute",
       N_TEXT, "text",
       N_DATA, "data",
       N_BSS,  "bss",
       N_COMM, "common",
       0,      0,
};

tracesym()
{
       register struct tynames *tp;

       if (cursym.n_type & N_STAB)
               return;
       printf("%s", filname);
       if (archdr.ar_name[0])
               printf("(%s)", archdr.ar_name);
       printf(": ");
       if ((cursym.n_type&N_TYPE) == N_UNDF && cursym.n_value) {
               printf("definition of common %s size %d\n",
                   cursym.n_un.n_name, cursym.n_value);
               return;
       }
       for (tp = tynames; tp->ty_name; tp++)
               if (tp->ty_value == (cursym.n_type&N_TYPE))
                       break;
       printf((cursym.n_type&N_TYPE) ? "definition of" : "reference to");
       if (cursym.n_type&N_EXT)
               printf(" external");
       if (tp->ty_name)
               printf(" %s", tp->ty_name);
       printf(" %s\n", cursym.n_un.n_name);
}

/*
 * This routine relocates the single text or data segment argument.
 * Offsets from external symbols are resolved by adding the value
 * of the external symbols.  Non-external reference are updated to account
 * for the relative motion of the segments (ctrel, cdrel, ...).  If
 * a relocation was pc-relative, then we update it to reflect the
 * change in the positioning of the segments by adding the displacement
 * of the referenced segment and subtracting the displacement of the
 * current segment (creloc).
 *
 * If we are saving the relocation information, then we increase
 * each relocation datum address by our base position in the new segment.
 */
load2td(creloc, position, b1, b2)
       long creloc;
#ifdef ibm370
	long position;
#endif ibm370
       struct biobuf *b1, *b2;
{
       register struct nlist *sp;
       register struct local *lp;
       long tw;
       long twa;
#ifndef ibm370
       long twb;
#endif ibm370
       register struct relocation_info *rp, *rpend;
       struct relocation_info *relp;
       char *codep;
       register char *cp;
       int relsz, codesz;
#ifndef ibm370
       int opcode, reg;
#endif ibm370

       relsz = reloc.size;
       relp = (struct relocation_info *)malloc(relsz);
       codesz = text.size;
       codep = (char *)malloc(codesz);
       if (relp == 0 || codep == 0)
               error(1, "out of memory (load2td)");
       mget((char *)relp, relsz, &reloc);
       rpend = &relp[relsz / sizeof (struct relocation_info)];
       mget(codep, codesz, &text);
       for (rp = relp; rp < rpend; rp++) {
               cp = codep + rp->r_address;
               /*
                * Pick up previous value at location to be relocated.
                */
               switch (rp->r_length) {

               case 0:         /* byte */
                       tw = *cp;
                       break;

               case 1:         /* word */
			if (IS_ODD(cp))
				error(0, "Half-word relocation not on half-word boundary");
                       tw = *(short *)cp;
                       break;

               case 2:         /* long */
            /*         tw = *(long *)cp;       */
			if (IS_ODD(cp))
				error(0, "Full-word relocation not on half-word boundary");
			twa = *(short *)cp;
			tw = twa<<16 | *(unsigned short *)(cp+2);
                       break;
#ifndef ibm370
		case 3:		/* split 'immediate' address */
		    if (IS_ODD(cp))
			error(0,"Relocation address not on half-word boundary");
		    if ( *(cp-2) == CAU_OP ) { /* low value is signed */
			tw = (( *(short *)cp )<<16) + *(short *)(cp+4);
		    } else {   
			tw = ( *(short *)(cp+4) )<<16 | *(unsigned short *)cp;
		    }
			break;
	/*
	 * no default at this time.
         *       default:
         *             error(1, "load2td botch: bad length");
	 */
#else ibm370

		default:
			error(1, "load2td botch: bad length");
#endif ibm370
               }
               /*
                * If relative to an external which is defined,
                * resolve to a simpler kind of reference in the
                * result file.  If the external is undefined, just
                * convert the symbol number to the number of the
                * symbol in the result file and leave it undefined.
                */
#ifndef ibm370
		/*
		 * The pc realtive bit is a special case:
		 * It signals a 20 or 24 bit value within a 4 byte 
		 * intruction (BI or BA format). The value of r_address is the
		 * first byte of the instruction. The relative address within
		 * the instruction has NOT had the pc subtracted from it as yet
		 * and that must be done here.
		 */
	       opcode = 0; /* flag for special case */
#endif ibm370
               if (rp->r_extern) {
#ifndef ibm370
		   if (rp->r_pcrel) {
			opcode = tw>>24 & 0xff;
			if (opcode >= BI_LO_OP && opcode <= BI_HI_OP) {
			   if (opcode>>1 != BALA_OP>>1) {
				bala_op = 0;
				reg = tw>>20 & 0xf;
				/* pick up a signed 20 bit value */
				/*   and multiply by two         */
				tw = ((tw & 0xfffff) << 12) >> 11;
			   } else {
				bala_op = 1;
				tw = tw & 0xffffff;
			   }
			} else {  /* as or ld botch ??? */
			   error(0,"Op-code invalid for 20/24 bit data. as/ld botch?");
			}
		   }
#endif ibm370
                       /*
                        * Search the hash table which maps local
                        * symbol numbers to symbol tables entries
                        * in the new a.out file.
                        */
                       lp = lochash[rp->r_symbolnum % LHSIZ];
                       while (lp->l_index != rp->r_symbolnum) {
                               lp = lp->l_link;
                               if (lp == 0)
                                       error(1, "local symbol botch");
                       }
                       sp = lp->l_symbol;
                       if (sp->n_type == N_EXT+N_UNDF)
                               rp->r_symbolnum = nsym+symx(sp);
                       else {
                               rp->r_symbolnum = sp->n_type & N_TYPE;
                               tw += sp->n_value;
#ifndef ibm370
			       /* if special case, remove pc value */
			       if (rp->r_pcrel & !bala_op)
					tw -= rp->r_address;
			       if (!bala_op)
                               		rp->r_extern = 0; /* don't do this for bala! */
#else ibm370
				rp->r_extern = 0;
#endif ibm370
                       }
               } else switch (rp->r_symbolnum & N_TYPE) {
               /*
                * Relocation is relative to the loaded position
                * of another segment.  Update by the change in position
                * of that segment.
                */
               case N_TEXT:
                       tw += ctrel;
                       break;
               case N_DATA:
                       tw += cdrel;
                       break;
               case N_BSS:
                       tw += cbrel;
                       break;
               case N_ABS:
                       break;
               default:
                       error(1, "relocation format botch (symbol type))");
               }
               /*
                * Relocation is pc relative, so decrease the relocation
                * by the amount the current segment is displaced.
                * (E.g if we are a relative reference to a text location
                * from data space, we added the increase in the text address
                * above, and subtract the increase in our (data) address
                * here, leaving the net change the relative change in the
                * positioning of our text and data segments.)
                */
#ifndef ibm370
		/*
		 * If pc relative and still unresolved, leave tw unchanged.
		 */
               if (rp->r_pcrel & ~rp->r_extern)
		   if ( (((tw>>25)&0x7f) != BALA_OP>>1 ) )
                       tw -= creloc;		 /* don't do this for bala(x) */
	       /* if special case build 4 byte instruction */
		if (opcode) {
		    if ( bala_op ) {
			if ( tw & 0xff000000 )
				error(0, "24-bit overflow");
			tw = opcode<<24 | tw & 0xffffff;
		    } else {
			if (tw>>1 > MAX_POS_20BITS || tw>>1 < MAX_NEG_20BITS) 
				{
				/* JSW - 7/29/86 - fix for apar #230
				   if the instruction is a bali and uses
				   r15 then we can make it a bala and change
				   the address to absolute instead of relative
				   but the absolute address must be in the
				   first 16 meg.
				*/
				twb = tw + rp->r_address + creloc;
				if (opcode>>1 == BALI_OP>>1 && reg==15 && (twb&0xff000000)==0 )
					{
					opcode = (opcode&1) | BALA_OP;
					tw = opcode<<24 | (twb&0xffffff);
					}
				else
					error(0, "20-bit overflow");
				}
			else
				tw = opcode<<24 | reg<<20 | tw>>1 & 0xfffff;
		    }
		}
	    /*   if (opcode)  tw = opcode<<24 | reg<<20 | tw>>1 & 0xfffff; */
#else ibm370
		if (rp->r_pcrel)
			tw -= creloc;
#endif ibm370
		twa = tw;

               /*
                * Put the value back in the segment,
                * while checking for overflow.
                */
               switch (rp->r_length) {

               case 0:         /* byte */
		       if ((unsigned)tw > 255)
                               error(0, "byte displacement overflow");
                       *cp = tw;
                       break;
               case 1:         /* word */
                       if (tw < -32768 || tw > 32767)
                               error(0, "word displacement overflow");
		       *(short *)cp = tw;
                       break;
               case 2:         /* long */
                       /* *(long *)cp = tw; */
			*(short *)cp = twa>>16;
			*(short *)(cp + 2) = twa;
                       break;
#ifndef ibm370
	       case 3:		/* split address */
	/* (ef) 6/3/87 -- 'cal' instructions sign-extend.  Simulate	*/
	/*	(and compensate for) the sign extension for 'cau-cal'	*/
	/*	and 'cal-cau' sequences.				*/
		  if ( *(cp-2) == CAU_OP ) {
			low_half = tw & 0xffff;
			*(short *)cp = ((tw - low_half)>>16) & 0xffff;
			*(short *)(cp+4) = low_half;
		  } else if ( *(cp-2) == CAL_OP ) {
			low_half = tw &0xffff;
			*(short *)cp = low_half;
			*(short *)(cp+4) = ((tw - low_half)>>16) & 0xffff;
		  } else {
			*(short *)cp = tw;	 /* low order two */
			*(short *)(cp+4) = tw>>16;
		  }
#endif ibm370
               }
               /*
                * If we are saving relocation information,
                * we must convert the address in the segment from
                * the old .o file into an address in the segment in
                * the new a.out, by adding the position of our
                * segment in the new larger segment.
                */
               if (rflag)
                       rp->r_address += position;
       }
       bwrite(codep, codesz, b1);
       if (rflag)
               bwrite(relp, relsz, b2);
       cfree((char *)relp);
       cfree(codep);
}

finishout()
{
       register int i;
       int nsymt;

       if (sflag==0) {
               nsymt = symx(nextsym);
               for (i = 0; i < nsymt; i++)
                       symwrite(xsym(i), sout);
               bwrite(&offset, sizeof offset, sout);
       }
       if (!ofilfnd) {
               unlink("a.out");
               if (link("l.out", "a.out") < 0)
                       error(1, "cannot move l.out to a.out");
               ofilename = "a.out";
       }
       delarg = errlev;
       delexit();
}

mkfsym(s)
char *s;
{

       if (sflag || xflag)
               return;
       cursym.n_un.n_name = s;
       cursym.n_type = N_TEXT;
       cursym.n_value = torigin;
       symwrite(&cursym, sout);
}

getarhdr()
{
       register char *cp;

       mget((char *)&archdr, sizeof archdr, &text);
       for (cp=archdr.ar_name; cp<&archdr.ar_name[sizeof(archdr.ar_name)];)
               if (*cp++ == ' ') {
                       cp[-1] = 0;
                       return;
               }
}

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

top:
       if (n == 0)
               return;
       if (sp->size && sp->nibuf) {
               p = sp->ptr;
               take = sp->size;
               if (take > sp->nibuf)
                       take = sp->nibuf;
               if (take > n)
                       take = n;
               n -= take;
               sp->size -= take;
               sp->nibuf -= take;
               sp->pos += take;
	       if(take <= 1)
		       *loc++ = *p++;
	       else {
		       bcopy(p, loc, take);
		       loc += take;
		       p += take;
	       }
               sp->ptr = p;
               goto top;
       }
       if (n > BUFSIZ) {
               take = n - n % BLKSIZE;
               lseek(infil, (sp->bno+1)*BLKSIZE, 0);
               if (take > sp->size || read(infil, loc, take) != take)
                       error(1, "premature EOF");
               loc += take;
               n -= take;
               sp->size -= take;
               sp->pos += take;
               dseek(sp, (sp->bno+1+take/BLKSIZE)*BLKSIZE, -1);
               goto top;
       }
       *loc++ = get(sp);
       --n;
       goto top;
}

symwrite(sp, bp)
       struct nlist *sp;
       struct biobuf *bp;
{
       register int len;
       register char *str;

       str = sp->n_un.n_name;
       if (str) {
               sp->n_un.n_strx = offset;
               len = strlen(str) + 1;
               bwrite(str, len, strout);
               offset += len;
       }
       bwrite(sp, sizeof (*sp), bp);
       sp->n_un.n_name = str;
}

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

       b = loc>>BLKSHIFT;
       o = loc&BLKMASK;
       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)BLKMASK, 0);
                       if ((n = read(infil, p->buff, sizeof(p->buff))) < 0)
                               n = 0;
                       p->nibuf = n;
       } else
               error(1, "botch: no pages");
       ++p->nuser;
       sp->bno = b;
       sp->pno = p;
       if (s != -1) {sp->size = s; sp->pos = 0;}
       sp->ptr = (char *)(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)<<BLKSHIFT), (long)-1);
               sp->nibuf -= sizeof(char);
       }
       if ((sp->size -= sizeof(char)) <= 0) {
               if (sp->size < 0)
                       error(1, "premature EOF");
               ++fpage.nuser;
               --sp->pno->nuser;
               sp->pno = (PAGE *) &fpage;
       }
       sp->pos += sizeof(char);
       return(*sp->ptr++);
}

getfile(acp)
char *acp;
{
       register char *cp;
       register int c;
       int i;
       char arcmag[SARMAG+1];
       struct stat stb;

       cp = acp;
       infil = -1;
       archdr.ar_name[0] = '\0';
       filname = cp;
       if (cp[0]=='-' && cp[1]=='l') {
/*             char *locfilname = "/usr/local/lib/libxxxxxxxxxxxxxxx"; */
               if(cp[2] == '\0')
                       cp = "-la";
/*
 *	       filname = "/usr/lib/libxxxxxxxxxxxxxxx";
 *             for(c=0; cp[c+2]; c++) {
 *                     filname[c+12] = cp[c+2];
 *                     locfilname[c+18] = cp[c+2];
 *             }
 *             filname[c+12] = locfilname[c+18] = '.';
 *             filname[c+13] = locfilname[c+19] = 'a';
 *             filname[c+14] = locfilname[c+20] = '\0';
 *             if ((infil = open(filname+4, 0)) >= 0) {
 *                     filname += 4;
 *             } else if ((infil = open(filname, 0)) < 0) {
 *                     filname = locfilname;
 *             }
 */
	       for ( i=0; i<ndir; i++) {
		       filname = libn[i].l_name;
                       for (c=0; cp[c+2]; c++) {
			       filname[c+libn[i].l_cpos] = cp[c+2];
			       }
		       c += libn[i].l_cpos;
		       filname[c] = '.';
		       filname[c+1] = 'a';
		       filname[c+2] = '\0';
		       if ((infil = open(filname,0) ) >= 0)
			       break;
	       }
       }
       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 = (PAGE *) &fpage;
       fpage.nuser = 2;
       dseek(&text, 0L, SARMAG);
       if (text.size <= 0)
               error(1, "premature EOF");
       mget((char *)arcmag, SARMAG, &text);
       arcmag[SARMAG] = 0;
       if (strcmp(arcmag, ARMAG))
               return (0);
       dseek(&text, SARMAG, sizeof archdr);
       if(text.size <= 0)
               return (1);
       getarhdr();
       if (strncmp(archdr.ar_name, "__.SYMDEF", sizeof(archdr.ar_name)) != 0)
               return (1);
       fstat(infil, &stb);
       return (stb.st_mtime > atol(archdr.ar_date) ? 3 : 2);
}

/*
 * looks for the current symbol in the symbol table.  If found returns a pointer
 * to the hash table entry for the symbol.  If the symbol is not found then
 * find a place for it in the hash table and return a pointer to this.
 * Will allocate another symbol table segment if necessary.
 */

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

       sh = 0;
       for (cp = cursym.n_un.n_name; *cp;)
               sh = (sh<<1) + *cp++;
       sh = (sh & 0x7fffffff) % HSIZE;
       for (gp = symseg; gp < &symseg[NSEG]; gp++) {
               if (gp->sy_first == 0) {
                       gp->sy_first = (struct nlist *)
                           calloc(NSYM, sizeof (struct nlist));
                       gp->sy_hfirst = (struct nlist **)
                           calloc(HSIZE, sizeof (struct nlist *));
                       if (gp->sy_first == 0 || gp->sy_hfirst == 0)
                               error(1, "ran out of space for symbol table");
                       gp->sy_last = gp->sy_first + NSYM;
                       gp->sy_hlast = gp->sy_hfirst + HSIZE;
               }
               if (gp > csymseg)
                       csymseg = gp;
               hp = gp->sy_hfirst + sh;
               i = 1;
               do {
                       if (*hp == 0) {
                               if (gp->sy_used == NSYM)
                                       break;
                               return (hp);
                       }
                       cp1 = (*hp)->n_un.n_name;
                       for (cp = cursym.n_un.n_name; *cp == *cp1++;)
                               if (*cp++ == 0)
                                       return (hp);
                       hp += i;
                       i += 2;
                       if (hp >= gp->sy_hlast)
                               hp -= HSIZE;
               } while (i < HSIZE);
               if (i > HSIZE)
                       error(1, "hash table botch");
       }
       error(1, "symbol table overflow");
       /*NOTREACHED*/
}

/*
 * Removes all symbols from the symbol table back to the one pointed to by
 * saved.  ( is this needed anymore? - JSW )
 */

symfree(saved)
       struct nlist *saved;
{
       register struct symseg *gp;
       register struct nlist *sp;

       for (gp = csymseg; gp >= symseg; gp--, csymseg--) {
               sp = gp->sy_first + gp->sy_used;
               if (sp == saved) {
                       nextsym = sp;
                       return;
               }
               for (sp--; sp >= gp->sy_first; sp--) {
                       gp->sy_hfirst[sp->n_hash] = 0;
                       gp->sy_used--;
                       if (sp == saved) {
                               nextsym = sp;
                               return;
                       }
               }
       }
       if (saved == 0)
               return;
       error(1, "symfree botch");
}

/*
 * looks for symbol s in the symbol table, destroys cursym
 */

struct nlist **
slookup(s)
       char *s;
{

       cursym.n_un.n_name = s;
       cursym.n_type = N_EXT+N_UNDF;
       cursym.n_value = 0;
       return (lookup());
}

/*
 * Enters the current symbol in the symbol table.  Is passed a pointer to the
 * hash table entry for the symbol.  If the symbol is not already entered then
 * it is entered.  Returns true if the symbol was entered, false if the symbol
 * already existed.
 */

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

       if (*hp==0) {
               if (hp < csymseg->sy_hfirst || hp >= csymseg->sy_hlast)
                       error(1, "enter botch");
               *hp = lastsym = sp = csymseg->sy_first + csymseg->sy_used;
               csymseg->sy_used++;
               sp->n_un.n_name = cursym.n_un.n_name;
               sp->n_type = cursym.n_type;
               sp->n_hash = hp - csymseg->sy_hfirst;
               sp->n_value = cursym.n_value;
               nextsym = lastsym + 1;
               return(1);
       } else {
               lastsym = *hp;
               return(0);
       }
}

symx(sp)
       struct nlist *sp;
{
       register struct symseg *gp;

       if (sp == 0)
               return (0);
       for (gp = csymseg; gp >= symseg; gp--)
               /* <= is sloppy so nextsym will always work */
               if (sp >= gp->sy_first && sp <= gp->sy_last)
                       return ((gp - symseg) * NSYM + sp - gp->sy_first);
       error(1, "symx botch");
       /*NOTREACHED*/
}

symreloc()
{
	if(funding) return;
#ifndef ibm370
/* In the RT linkage, function addresses are in data, not text.
 * The following kluge on n_type relocates function stabs into data
 * rather that text.  0x24 would have been N_FUN had we included stabs.h.
 */
	switch ((cursym.n_type == 0x24) ? N_DATA : cursym.n_type & 0x0f) {
#endif ibm370
#ifdef ibm370
	switch (cursym.n_type & 0x0f) {
#endif ibm370
       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;

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

error(n, s)
char *s;
{

       if (errlev==0)
               printf("ld:");
       if (filname) {
               printf("%s", filname);
               if (n != -1 && archdr.ar_name[0])
                       printf("(%s)", archdr.ar_name);
               printf(": ");
       }
       printf("%s\n", s);
       if (n == -1)
               return;
       if (n)
               delexit();
       errlev = 2;
}

readhdr(loc)
off_t loc;
{

       dseek(&text, loc, (long)sizeof(filhdr));
       mget((char *)&filhdr, sizeof(filhdr), &text);
       if (N_BADMAG(filhdr)) {
               if (filhdr.a_magic == OARMAG)
                       error(1, "old archive");
               error(1, "bad magic number");
       }
       if (filhdr.a_text&01 || filhdr.a_data&01)
               error(1, "text/data size odd");
       if (filhdr.a_magic == NMAGIC || filhdr.a_magic == ZMAGIC) {
               cdrel = -round(filhdr.a_text, pagesize);
               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");
}

round(v, r)
       int v;
       u_long r;
{

       r--;
       v += r;
       v &= ~(long)r;
       return(v);
}

#define        NSAVETAB        8192
char   *savetab;
int    saveleft;

char *
savestr(cp)
       register char *cp;
{
       register int len;

       len = strlen(cp) + 1;
       if (len > saveleft) {
               saveleft = NSAVETAB;
               if (len > saveleft)
                       saveleft = len;
               savetab = (char *)malloc(saveleft);
               if (savetab == 0)
                       error(1, "ran out of memory (savestr)");
       }
       strncpy(savetab, cp, len);
       cp = savetab;
       savetab += len;
       saveleft -= len;
       return (cp);
}

bopen(bp, off)
       struct biobuf *bp;
{

       bp->b_ptr = bp->b_buf;
       bp->b_nleft = BUFSIZ - off % BUFSIZ;
       bp->b_off = off;
       bp->b_link = biobufs;
       biobufs = bp;
}

int    bwrerror;

bwrite(p, cnt, bp)
       register char *p;
       register int cnt;
       register struct biobuf *bp;
{
       register int put;
       register char *to;

top:
       if (cnt == 0)
               return;
       if (bp->b_nleft) {
               put = bp->b_nleft;
               if (put > cnt)
                       put = cnt;
               bp->b_nleft -= put;
               to = bp->b_ptr;
	       bcopy((caddr_t) p, (caddr_t) to, put);
               bp->b_ptr += put;
               p += put;
               cnt -= put;
               goto top;
       }
       if (cnt >= BUFSIZ) {
               if (bp->b_ptr != bp->b_buf)
                       bflush1(bp);
               put = cnt - cnt % BUFSIZ;
               if (boffset != bp->b_off)
                       lseek(biofd, bp->b_off, 0);
               if (write(biofd, p, put) != put) {
                       bwrerror = 1;
                       error(1, "output write error");
               }
               bp->b_off += put;
               boffset = bp->b_off;
               p += put;
               cnt -= put;
               goto top;
       }
       bflush1(bp);
       goto top;
}

bflush()
{
       register struct biobuf *bp;

       if (bwrerror)
               return;
       for (bp = biobufs; bp; bp = bp->b_link)
               bflush1(bp);
}

bflush1(bp)
       register struct biobuf *bp;
{
       register int cnt = bp->b_ptr - bp->b_buf;

       if (cnt == 0)
               return;
       if (boffset != bp->b_off)
               lseek(biofd, bp->b_off, 0);
       if (write(biofd, bp->b_buf, cnt) != cnt) {
               bwrerror = 1;
               error(1, "output write error");
       }
       bp->b_off += cnt;
       boffset = bp->b_off;
       bp->b_ptr = bp->b_buf;
       bp->b_nleft = BUFSIZ;
}

bflushc(bp, c)
       register struct biobuf *bp;
{

       bflush1(bp);
       bputc(c, bp);
}
printncs()
{
	if (sawovsym[OCSN]) printf("\t%s\n", "OCS");
	else if (sawovsym[NCSN]) printf("\t%s\n", "NCS");
	else printf("\t%s\n", "   ");
}
resetovsym()
{
	register	int	i;
	for (i=0; i<NOVSYM; i++)
		sawovsym[i] = 0;
}
checkovsym()
{
	register	int	ocs, ncs;
	ocs = sawovsym[OCSN];
	ncs = sawovsym[NCSN];
	if ( ocs && ncs ) {
		error(0, "Both OCS and NCS flags present in file.");
		return;
	}
	if (Mflag > 1)
	   return;
	sawocs |= ocs + !ncs;
	sawncs |= ncs;
	if ( sawocs && sawncs ) {
	   if (!overrflg) {
		error(0, "Mixed use of old and new calling sequences.");
		/* decide what is correct by what is now wrong */
		overrflg = (ocs | (!ncs)) + 2*ncs;
	   }
	   if (overrflg == 2) {
		if (ncs) {
		   error(0, "NCS flag present");
		   sawncs = 0;  /* reset */
		}
	   } else 
		if (ocs) {
		   error(0, "OCS flag assumed");
		   sawocs = 0;
		}
		else if (!ncs) {
		   error(0, "No NCS flag present");
		   sawocs = 0;
		}
	}
#ifndef ibm370
	if ( !sawovsym[CRT0N] ) {
		sawnfl |= sawovsym[NFLN];
		sawrtfl |= sawovsym[RTFLN];
		if ( sawnfl && sawrtfl && !rtflwarned) {
			error(-1, 
"warning: Old and new floating point linkages encountered\n \
(symbols FPaGET0 and FPGLUE present); performance degradation may occur.");
			rtflwarned = 1;
		}
	}
#endif ibm370
}
#ifndef ibm370

/*
 * JSW 6/25/86
 * Routines added to manipulate 'hidden' symbols.  These are used to determine
 * if a module from an archive should be included or not.  'Hidden' symbols
 * are stored at the end of the symbol table, but are not actually entered.
 * There is no entry in the hash table for hidden symbols, and they may be 
 * duplicates of symbols already in the symbol table.  They are stored in the
 * symbol table because it is the most convenient place for them.
 */

/*
 * JSW 6/25/86
 * hide the current symbol in the symbol table
 */
hsenter()
	{

#ifdef DEBUG
printf("hsenter - %s\n", cursym.n_un.n_name);
#endif

	hsnextsym->n_un.n_name=cursym.n_un.n_name;
	hsnextsym->n_type=cursym.n_type;
	hsnextsym->n_value=cursym.n_value;
        hsnextsym->n_other=0;
	hsnextsym++;
	if ( hsnextsym == hssymseg->sy_first+NSYM )
		{
		hssymseg++;
		hsnextsym = hssymseg->sy_first + hssymseg->sy_used;
		}
	if ( hssymseg->sy_first == 0 )
		{
		makeseg( hssymseg );
		hsnextsym = hssymseg->sy_first + hssymseg->sy_used;
		}

#ifdef DEBUG
printf("leaving hsenter\n");
#endif
	}

/* 
 * JSW 6/25/86
 * search for a hidden symbol
 */
struct nlist *hssearch(s)
	char *s;
	{
	register char *hss;
	register char *ts;
	register struct symseg *hstmpseg;
	register struct nlist *hscursym;

#ifdef DEBUG
printf("hssearch\n");
#endif

	hscursym = csymseg->sy_first+csymseg->sy_used;
	hstmpseg = csymseg;
	if ( hscursym >= hstmpseg->sy_first + NSYM )
		{
		hstmpseg++;
		hscursym = hstmpseg->sy_first + hstmpseg->sy_used;
		}
	while( hscursym != hsnextsym )
		{

#ifdef DEBUG
printf("comparing %s and %s\n", s, hscursym->n_un.n_name);
#endif

		ts = s;
		for ( hss=hscursym->n_un.n_name; *hss==*ts++; )
			if ( *hss++ == 0 )
				return(hscursym);
		hscursym++;
		if ( hscursym >= hstmpseg->sy_first + NSYM )
			{
			hstmpseg++;
			hscursym = hstmpseg->sy_first + hstmpseg->sy_used;
			}
		}
	return(0);
	}

/*
 * JSW 6/25/86
 * allocate a new symbol table segment.
 */
makeseg(gp)
	struct symseg *gp;
	{

#ifdef DEBUG
printf("makeseg\n");
#endif

        gp->sy_first = (struct nlist *)calloc(NSYM, sizeof (struct nlist));
        gp->sy_hfirst = (struct nlist **)calloc(HSIZE, sizeof (struct nlist *));
        if (gp->sy_first == 0 || gp->sy_hfirst == 0)
                error(1, "ran out of space for symbol table");
        gp->sy_last = gp->sy_first + NSYM;
        gp->sy_hlast = gp->sy_hfirst + HSIZE;
        }

/*
 * JSW 6/25/86
 * if the current symbol is of the form _sym, and a symbol _.sym is defined
 * in the text segment of the current module, then return true( the _sym is
 * not really the one we want )
 */
findud()
	{
	char *dummys;
	struct nlist *hsp, **hp;
	register char *ptr;
	int len;

#ifdef DEBUG
printf("findud\n");
#endif

/*
 * make sure we care about this one.
 */
	if ( cursym.n_un.n_name[0] != '_' )
		return(0);
/*
 * allocate space for and create the _.sym string
 */
	ptr = cursym.n_un.n_name;
	for ( len = 0; ptr[len++] != 0;);

#ifdef DEBUG
	printf("length of string %s is %d\n", ptr, len );
#endif

	if ( ( dummys = (char *) calloc( 1, len+1 ) ) == 0 )
		error(1, "no space for temporary string");
	for ( len = 0; ( dummys[len+1]=ptr[len] ) != 0; len++ );
	dummys[0]='_';
	dummys[1]='.';

#ifdef DEBUG
  	printf("%s\n%s\n",ptr,dummys);
#endif

/*
 * check if the symbol is 'hidden'
 */
	hsp = hssearch(dummys);
	if ( hsp != 0 && hsp->n_type==N_EXT+N_TEXT )
		{
		free(dummys);

#ifdef DEBUG
printf("found!\n");
#endif

		return(1);
		}
	free(dummys);

#ifdef DEBUG
printf("not found!\n");
#endif

	return(0);
	}
#endif ibm370



#ifdef PAGE_CHECK
long
check_file(t_file, t_loc, t_off)
/*
	Check the text section of the current object file.  Return the total
	number of pad bytes required to eliminate potential problems in this
	object file.  If it is impossible to resolve all problems or we get
	an unrecoverable error return -1.
*/
STREAM *t_file; /* Subject text stream */
off_t t_loc; /*location within file of object */
long t_off; /* current length of text section */
{
	long tmp_size, pad_size = 0;
	
	t_off = t_off % pagesize; /* offset into page */

	if((filhdr.a_text + t_off) < pagesize)
		return(0); /* doesn't cross page */

	while(tmp_size = check_text(t_file, t_loc, t_off, filhdr.a_text)){
		pad_size += tmp_size;
		if((pagesize - pad_size) < 2)
			return(-1); /* Impossible to resolve all */
		t_off = (t_off + tmp_size) % pagesize;
		}

	return(pad_size);
}

long
check_text(txt_file, t_loc, pad, txt_size)
/*
	Find the first potential problem in the text section.  Return
	the number of pad bytes required to eliminate the potential problem.
	Note: Pad bytes must be a long word multiple.
*/
STREAM *txt_file;
off_t t_loc;
long pad, txt_size;	/* amount to pad before text */
{
	long read_size, pad_size=0, check_size=0;
	int num_words, back_2, back_4, back_6, check_first = 0;

        t_loc += N_TXTOFF(filhdr);  /* go to text section... */

	if((num_words = pagesize - pad) <= 0) /* check page wrap */
		num_words += pagesize;
	if((back_6 = num_words - 6) < 0)
		back_6 += pagesize;
	if((back_4 = num_words - 4) < 0)
		back_4 += pagesize;
	if((back_2 = num_words - 2) < 0)
		back_2 += pagesize;

	while(txt_size > num_words){ /* read until text does not span page */
		if (txt_size >= pagesize) 
			read_size = pagesize;
		else
			read_size = txt_size;
		dseek(txt_file, t_loc, read_size);
		mget(words, read_size, txt_file);
		txt_size -= read_size;
		t_loc += read_size;

		/* check for continuation of previous page */
		if(check_first) {
			check_first = 0;
			if((words[back_2] == BALAX) || (words[back_2] == BALIX)
			|| (words[back_2] == BBX) || (words[back_2] == BNBX) ||
			((words[back_2] >= MIN_D_OP) &&
			(words[back_2] <= MAX_D_OP)))
				pad_size = 4;
			}

		/* check case of 2 byte branch before boundary */
		switch(words[back_2]){
			case BBRX:
			case BNBRX: pad_size = 4;
				break;
			default:
				break;
			}

		/* check case of 4 byte branch before boundary */
		if(read_size < back_4)
			; /* do nothing */
		else	{
			switch(words[back_4]){
				case BBX:
				case BNBX: pad_size = 4;
					break;
				default:
					break;
				}
		
			/* Check case of 2 byte branch followed by a 4 byte
			   instruction that spans page boundary.
			*/
			switch(words[back_4]){
				case BBRX:
				case BNBRX: check_size = 4;
					break;
				default:
					break;
				}
			if(check_size)
				if(back_2 < back_4)
					check_first = 1;
				else if((words[back_2] == BALAX) ||
				(words[back_2] == BALIX) ||
				(words[back_2] == BBX) ||
				(words[back_2] == BNBX) ||
				((words[back_2] >= MIN_D_OP) &&
				(words[back_2] <= MAX_D_OP)))
					pad_size = check_size;
			check_size = 0;

			/* Check case of 4 byte branch followed by a 4 byte
			   instruction that spans page boundary.
			*/
			if(read_size < back_6)
				; /* do nothing */
			else	{
				switch(words[back_6]){
					case BBX:
					case BNBX: check_size = 4;
						break;
					default:
						break;
					}
				if(check_size)
					if(back_2 < back_6)
						check_first = 1;
					else if((words[back_2] == BALAX) ||
					(words[back_2] == BALIX) ||
					(words[back_2] == BBX) ||
					(words[back_2] == BNBX) ||
					((words[back_2] >= MIN_D_OP) &&
					(words[back_2] <= MAX_D_OP)))
						pad_size = check_size;
				check_size = 0;
				} /* else */
			} /* else */
		if(pad_size)
			return(pad_size); /* return if found */
		} /* while */
	return(pad_size);
}
#endif PAGE_CHECK
