/*
 *    (c)Copyright 1992-1997 Obvious Implementations Corp.  Redistribution and
 *    use is allowed under the terms of the DICE-LICENSE FILE,
 *    DICE-LICENSE.TXT.
 */

/*
 *  RELOC.C
 *
 *
 *  hunk usually points to the hunk needing parts of itself
 *  relocated.
 *
 *  HL points to the HunkListNode that the data is actually defined
 *     in.
 *
 *  HX points to the HunkListNode... the final HunkNo that we will
 *     use in the relocation list.
 */

/*
**      $Filename: reloc.c $
**      $Author: dice $
**      $Revision: 30.6 $
**      $Date: 1994/08/04 04:50:55 $
**      $Log: reloc.c,v $
 * Revision 30.6  1994/08/04  04:50:55  dice
 * .
 *
 * Revision 30.5  1994/06/13  18:38:37  dice
 * byte ordering portability
 *
 * Revision 30.0  1994/06/10  18:05:40  dice
 * .
 *
 * Revision 1.2  1993/10/13  19:48:08  jtoebes
 * Fix BUG01125 - Enforce hit in Dlink.
 *
**/

#include "defs.h"

Prototype void	copy_reloc(long *, long *, long, long);
Prototype void	ScanHunkReloc32(Hunk *, long);
Prototype void	ScanHunkReloc8_16(ulong *, Hunk *, long);
Prototype void	ScanHunkReloc8(Hunk *);
Prototype void	ScanHunkReloc16(Hunk *);
Prototype void	ScanHunkRelocD16(Hunk *);
Prototype int	ScanHunkExt(Hunk *, long);
Prototype int	HunkExtSymCK(Hunk *, ubyte, ulong, ulong *);
Prototype int	HunkExtSymIN(Hunk *, ubyte, ulong, ulong *);
Prototype int	HunkExtSymCNT(Hunk *, ubyte, ulong, ulong *);
Prototype int	HunkExtSymRUN(Hunk *, ubyte, ulong, ulong *);
Prototype int	HunkExtSymComResolve(Hunk *, ubyte, ulong, ulong *);
Prototype void	IllegalHunk(short, Hunk *);

/*
 *  Copy relocation information to the final relocation array, modifying it
 *  as we go along.  When hunks are combined together the relocation info
 *  for them must also be modified to point to the new location of the hunk
 *  in the section.
 */

void
copy_reloc(s, d, nlw, off)
long *s;
long *d;
long nlw;
long off;
{
    if (off == 0) {
	movmem(s, d, nlw * 4);
	return;
    }
    while (nlw--) {
	*d = ToMsbOrder(FromMsbOrder(*s) + off);
	++s;
	++d;
    }
}

void
ScanHunkReloc32(hunk, copy)
Hunk *hunk;
long copy;
{
    ulong *scan = hunk->Reloc32;

    if (scan == NULL)
	return;

    Assert(hunk->Module);

    while (FromMsbOrder(*scan)) {
	Hunk *destHunk = hunk->Module->Hunks[FromMsbOrder(scan[1])];
	long hunkNo;

	if ((ulong)FromMsbOrder(scan[1]) >= hunk->Module->NumHunks)
	    IllegalHunk(EF_FATAL, NULL);
	if (destHunk->Node.ln_Type != NT_BSS && destHunk->Node.ln_Type != NT_CODE && destHunk->Node.ln_Type != NT_DATA)
	    IllegalHunk(EF_FATAL, destHunk);
	if (PIOpt)
	    cerror(EERROR_RELOC32_ILLEGAL_PI, hunk->Module->Node.ln_Name, hunk->Node.ln_Name);
	if (ResOpt && !AbsWordOpt && (destHunk->Node.ln_Type == NT_BSS || destHunk->Node.ln_Type == NT_DATA))
	    cerror(EERROR_RELOC32_DATABSS_RES, hunk->Module->Node.ln_Name, hunk->Node.ln_Name);

	hunkNo = destHunk->HX->FinalHunkNo;
	if (hunkNo < 0 || hunkNo >= NumExtHunks)
	    cerror(EFATAL_RELOCSCAN_HUNK_RANGE, hunkNo, NumExtHunks);
	if (copy == 0) {			/*  just count # of relocs */
	    hunk->HL->CntReloc32[hunkNo] += FromMsbOrder(*scan);
	} else {				/*  actually copy stuff    */
	    {
		long bas = hunk->HL->CpyReloc32[hunkNo];
		long fnl = bas + FromMsbOrder(*scan);
		if (fnl > hunk->HL->CntReloc32[hunkNo])
		    cerror(EFATAL_RELOC_NOEXIST_HUNK, fnl, hunk->HL->CntReloc32[hunkNo]);
		copy_reloc(scan + 2, hunk->HL->ExtReloc32[hunkNo] + bas, FromMsbOrder(*scan), hunk->Offset);
		hunk->HL->CpyReloc32[hunkNo] = fnl;
	    }

	    /*
	     *	Handle fixing up the base offset.
	     */

	    Assert(hunk->Module);

	    if (FromMsbOrder(scan[0])) {
		char *dbase = (char *)hunk->Data;
		ulong dlen = hunk->Bytes;
		Hunk *destHunk = hunk->Module->Hunks[FromMsbOrder(scan[1])];
		long n;
		long destOff;
		ulong *newScan;

		if ((ulong)FromMsbOrder(scan[1]) >= hunk->Module->NumHunks)
		    IllegalHunk(EF_FATAL, NULL);
		if (destHunk->Node.ln_Type != NT_BSS && destHunk->Node.ln_Type != NT_CODE && destHunk->Node.ln_Type != NT_DATA)
		    IllegalHunk(EF_FATAL, destHunk);

		destOff = destHunk->Offset;
		if (destHunk->HL != destHunk->HX)
		    destOff += destHunk->HX->FinalSize;

		if (hunk->Node.ln_Type == NT_BSS)
		    cerror(EFATAL_RELOC_BSS_ILLEGAL);

		newScan = scan + 2;	/*  base of offsets */
		n = FromMsbOrder(*scan);/*  # of offsets    */

		while (n--) {
		    ulong doff = FromMsbOrder(*newScan);
		    long rval;

		    dbprintf(2, ("mod --- reloc32 %s@%ld(%ld) to %s@%ld\n", HunkToStr(hunk), doff, FromMsbOrder(*(ulong *)(dbase + doff)), HunkToStr(destHunk), destOff));

		    if (doff >= dlen)
			cerror(EFATAL_RANGE_HUNK, doff, HunkToStr(hunk));
		    rval = destOff + FromMsbOrder(*(ulong *)(dbase + doff));
		    *(ulong *)(dbase + doff) = ToMsbOrder(rval);
		    ++newScan;
		}
	    }
	}
	scan += FromMsbOrder(scan[0]) + 2;
    }
}

/*
 *  This runs actual 8 and 16 bit PC relative relocations.
 *	 runs actual 16 bit DATA relative relocations
 */

void
ScanHunkReloc8_16(scan, hunk, bits)
ulong *scan;
Hunk *hunk;
long bits;
{
    char *dbase;
    long dlen;

    if (scan == NULL)
	return;

    dbase = (char *)hunk->Data;
    dlen = hunk->Bytes;

    Assert(hunk->Module);

    while (FromMsbOrder(*scan)) {
	Hunk *destHunk = hunk->Module->Hunks[FromMsbOrder(scan[1])];
	long hunkNo;
	long n;
	long destOff;
	short absWord = 0;

	if ((ulong)FromMsbOrder(scan[1]) >= hunk->Module->NumHunks)
	    IllegalHunk(EF_FATAL, NULL);
	if (destHunk->Node.ln_Type != NT_BSS && destHunk->Node.ln_Type != NT_CODE && destHunk->Node.ln_Type != NT_DATA)
	    IllegalHunk(EF_FATAL, destHunk);
	hunkNo = destHunk->HX->FinalHunkNo;
	if (hunkNo < 0 || hunkNo >= NumExtHunks)
	    cerror(EFATAL_RELOCSCAN_HUNK_RANGE, hunkNo, NumExtHunks);

	destOff = destHunk->Offset;
	if (destHunk->HL != destHunk->HX)
	    destOff += destHunk->HX->FinalSize;

	/*
	 *  modify hunk->Data
	 */

	if (bits > 0) {     /*	PC-RELATIVE RELOCATION	*/
	    if (hunk->Node.ln_Type == NT_BSS) {
		cerror(EERROR_RELOCPCREL_BSS_ILLEGAL);
		return;
	    }
	    if (hunk->HL != destHunk->HX) {
		if (AbsWordOpt && hunk->Node.ln_Type == NT_CODE) {
		    absWord = 1;
		    dbprintf(0, ("Absolute Word Relocation"));
		} else {
		    cerror(EERROR_RELOCPCREL_ILLEGAL,
			hunk->Module->Node.ln_Name, hunk->Node.ln_Name,
			destHunk->Module->Node.ln_Name, destHunk->Node.ln_Name
		    );
		    return;
		}
	    }
	} else {	    /*	DATA BASE RELATIVE RELOCATION	*/
	    if (destHunk->Node.ln_Type == NT_CODE) {
		cerror(EERROR_RELOC_LABREL_CODE_ILL);
		return;
	    }
	    /* XXX warning msg if not to same data segment! */
	}

	n = FromMsbOrder(*scan);  /*	# of relocs	    */
	scan += 2;  /*	beginning of relocs */

	while (n--) {
	    ulong doff = FromMsbOrder(*scan); /* offset into this hunk's data */
	    long pcrel;

	    if (doff >= dlen) {
		cerror(EERROR_RANGE_HUNK, doff);
		return;
	    }
	    switch(bits) {
	    case 8:
		dbprintf(2, ("mod --- reloc08 %s@%ld(%ld) to %s\n", HunkToStr(hunk), doff, *(ubyte *)(dbase + doff), HunkToStr(destHunk)));
		pcrel = (destOff + *(char *)(dbase + doff)) - (hunk->Offset + doff);
		if (pcrel < -128 || pcrel > 127)
		    cerror(EERROR_RELOC8_RANGE, pcrel, HunkToStr(hunk));
		*(ubyte *)(dbase + doff) = pcrel;
		break;
	    case 16:
		dbprintf(2, ("mod --- reloc16 %s@%ld(%ld) to %s\n", HunkToStr(hunk), doff, FromMsbOrderShort(*(short *)(dbase + doff)), HunkToStr(destHunk)));
		if (absWord)
		    pcrel = (destOff + FromMsbOrderShort(*(short *)(dbase + doff))) + WordBaseAddr;
		else
		    pcrel = (destOff + FromMsbOrderShort(*(short *)(dbase + doff))) - (hunk->Offset + doff);
		if (pcrel < -32768 || pcrel > 32767)
		    cerror(EERROR_RELOC16_RANGE, pcrel, HunkToStr(hunk));
		*(short *)(dbase + doff) = ToMsbOrderShort(pcrel);
		break;
	    case -16:	    /*	16 bit data relative	*/
		dbprintf(2, ("mod --- reloD16 %s@%ld(%ld) to %s\n", HunkToStr(hunk), doff, FromMsbOrderShort(*(short *)(dbase + doff)) - 32766, HunkToStr(destHunk)));
		pcrel = (destOff + FromMsbOrderShort(*(short *)(dbase + doff))) - 32766;
		if (pcrel < -32768 || pcrel > 32767)
		    cerror(EERROR_RELOC16_RANGE, pcrel, HunkToStr(hunk));
		*(short *)(dbase + doff) = ToMsbOrderShort(pcrel);
		break;
	    }
	    ++scan;
	}
    }
}

void
ScanHunkReloc8(hunk)
Hunk *hunk;
{
    ScanHunkReloc8_16(hunk->Reloc8, hunk, 8);
}

void
ScanHunkReloc16(hunk)
Hunk *hunk;
{
    ScanHunkReloc8_16(hunk->Reloc16, hunk, 16);
}

void
ScanHunkRelocD16(hunk)
Hunk *hunk;
{
    ScanHunkReloc8_16(hunk->Reloc16D, hunk, -16);
}


/*
 *				SYMBOLS
 *	    -----------------------------------------------------
 */

/*
 *  External Symbol Scan
 *
 *  Count / handle references from the external symbol table.  This functions
 *  returns the sum of all subroutine calls.
 */

int
ScanHunkExt(hunk, cmd)
Hunk *hunk;
long cmd;
{
    ulong *scan = hunk->Ext;
    ubyte type;
    ulong len;
    ulong result = 0;

    if (scan == NULL)
	return(0);

    /*
     *	scan symbol units for hunk.
     */

    while (FromMsbOrder(*scan)) {
	ulong *newScan;


	type = FromMsbOrder(*scan) >> 24;
	len  = FromMsbOrder(*scan) & 0x00FFFFFF;
	++scan;

	switch(cmd) {
	case SCAN_RELOC_SYMCK:
	    result += HunkExtSymCK(hunk, type, len, scan);
	    break;
	case SCAN_RELOC_SYMIN:
	    result += HunkExtSymIN(hunk, type, len, scan);
	    break;
	case SCAN_RELOC_CNT:
	    result += HunkExtSymCNT(hunk, type, len, scan);
	    break;
	case SCAN_RELOC_RUN:
	    result += HunkExtSymRUN(hunk, type, len, scan);
	    break;
	case SCAN_RELOC_JUMP:
	    result += HunkExtSymJUMP(hunk, type, len, scan);
	    break;
	case SCAN_COMMON_RESOLVE:
	    if (type == 130)
		result += HunkExtSymComResolve(hunk, type, len, scan);
	    break;
	}

	newScan = scan + len;	    /*	skip symbol name    */

	switch(type) {
	case 0: 	/*  symbol table	*/
	case 1: 	/*  exported reloc def	*/
	case 2: 	/*  exported abs def	*/
	case 3: 	/*  exported res lib def*/
	    scan = newScan + 1;
	    break;
	case 130:	/*  common		*/
	    scan = newScan + 1;
	    break;
	case RESERVED_PCJMP_TYPE:
	case 135:	/*  imported ref32-pc	*/
	case 129:	/*  imported ref32	*/
	case 131:	/*  imported ref16	*/
	case 132:	/*  imported ref8	*/
	case 134:	/*  imported ref16D	*/
	    scan = newScan + FromMsbOrder(newScan[0]) + 1;
	    break;
	default:
	    cerror(EERROR_UNKNOWN_SYM_TYPE, type, len, scan);
	    return(0);
	}
    }
    return((long)result);
}

/*
 *  CK
 *
 *  This is called to check exported (xdef)d symbols in a library module
 *  searching for undefined symbols to see if we have to include the library
 *  or not.  It returns (1) if a previously undefined symbol is matched with
 *  an xdef.
 *
 *  HunkListNode has not been created at this point, so hunk->HL is invalid
 *  as well as hn.  Also, sym->Hunk may be NULL for linker ABS symbols.
 *
 *  COMMON symbols are references for this routine
 */

int
HunkExtSymCK(Hunk *hunk, ubyte type, ulong len, ulong *scan)
{
    Sym *sym;

    if (type == 1 || type == 2 || type == 3) {
	if ((sym = FindSymbol(scan, len)) != NULL) {
	    if (sym->Type == 0)
		return(1);
	}
    }
    return(0);
}

/*
 *  IN
 *
 *  This is called to bring the ref'd and def'd symbols associated with a module
 *  into the master symbol table.  This routine returns (1) If adding the
 *  symbol creates a NEW undefined reference (to force another pass through
 *  the library).
 *
 *  HunkListNode has not been created at this point, so hunk->HL is invalid
 *  as well as hn
 *
 *  COMMON symbols are ignored for this routine
 */

int
HunkExtSymIN(Hunk *hunk, ubyte type, ulong len, ulong *scan)
{
    Sym *sym;

    if (type == 1 || type == 2 || type == 3) {	    /*	def */
	/*
	 *  Note that a def symbol may overide a common symbol
	 */

	if ((sym = FindSymbol(scan, len)) != NULL) {
	    /*
	     *	If duplicate def and both hunks are object modules or both
	     *	hunks are the *same* library then complain.
	     */

	    if (sym->Hunk) {	    /*	duplicate   */
		Assert(sym->Hunk->Module);
		Assert(hunk->Module);

		if (sym->Type == 130) {
		    /*
		     *	always overide a common symbol
		     */
		    SetSymbol(sym, hunk, FromMsbOrder(scan[len]), type);
		} else if (sym->Hunk->Module->Node.ln_Type == NT_FTOBJ && hunk->Module->Node.ln_Type == NT_FTOBJ) {
		    cerror(EWARN_MULT_DEF_SYM, len*4, scan, sym->Hunk->Module->Node.ln_Name, hunk->Module->Node.ln_Name);
		}
		if (sym->Hunk->Module->Node.ln_Type == NT_FTLIB && hunk->Module->Node.ln_Type == NT_FTLIB && sym->Hunk->Module->FNode == hunk->Module->FNode)
		    cerror(EWARN_MULT_DEF_SYM_LIB, len*4, scan);
	    } else {		    /*	first def   */
		SetSymbol(sym, hunk, FromMsbOrder(scan[len]), type);
		dbprintf(1, ("SetSym type %-3d value %-3d %.*s\n", type, sym->Value, sym->SymLen, sym->SymName));
	    }
	} else {
	    CreateSymbol(scan, len, hunk, FromMsbOrder(scan[len]), type);
	}
    } else if (type == 129 || type == 131 || type == 132 || type == 134 || type == 135) {     /*  ref */
	if ((sym = FindSymbol(scan, len)) != NULL) {
	    ++sym->Refs;			/*  def'd or undef'd    */
	} else {
	    sym = CreateSymbol(scan, len, NULL, 0, 0);	  /*  new undef'd         */
	    ++sym->Refs;
	    return(1);
	}
    } else if (type == 130) {
	/*
	 *  A common symbol is ignored if it already exists, else it is
	 *  entered.
	 */

	if ((sym = FindSymbol(scan, len)) == NULL) {
	    CreateSymbol(scan, len, hunk, FromMsbOrder(scan[len]), type);
	    /*printf("Create common %d\n", type);*/
	} else {
	    if (sym->Type == 0) {
		SetSymbol(sym, hunk, FromMsbOrder(scan[len]), type);
		/*printf("OVR common %d\n", type);*/
	    } else {
		/*printf("Ignore common %d\n", type);*/
	    }
	}
    } else if (type != 0) {
	cerror(EERROR_UNKNOWN_SYM_TYPE, type, len * 4, scan);
    }
    return(0);
}

/*
 *  COMMON_RESOLVE
 *
 *  This routine picks out unresolved common symbols and resolves them.
 *  It returns a dummy 0.  Note that the passed hunk is ignored since
 *  common variables overide later common variables, only sym->Hunk is
 *  valid.
 *
 *  This routine is only called with type == 130
 */

int
HunkExtSymComResolve(Hunk *hunk, ubyte type, ulong len, ulong *scan)
{
    Sym *sym;
    long size;


    if ((sym = FindSymbol(scan, len)) != NULL) {
	hunk = sym->Hunk;   /*	hunk symbol applies to	*/

	if (sym->Type == 130) {
	    Assert(sym->Hunk);
	    size = (sym->Value + 3) & ~3;

	    /*printf("CommonSymbolResolve: offset=%d size=%d %.*s\n", hunk->Bytes, size, len * 4, scan);*/

	    SetSymbol(sym, hunk, hunk->Bytes, 1);
	    hunk->Bytes += size;
	    hunk->TotalBytes += size;
	}
    }
    return(0);
}

/*
 *  CNT
 *
 *  This routine returns a dummy 0 always.  This routine updates the
 *  the HunkListNode's CntReloc32 for 32 bit Reloc32 references.  We are
 *  counting the number of relocs required for this hunk to allocate the master
 *  reloc array.
 *
 *  This routine also handles Reloc16 and Reloc8's
 */

int
HunkExtSymCNT(Hunk *hunk, ubyte type, ulong len, ulong *scan)
{
    Sym *sym;

    if (type == 129) {
	if ((sym = FindSymbol(scan, len)) != NULL) {
	    Hunk *destHunk;
	    long hunkNo;

	    if (sym->Type == 2)
		return(0);

	    if ((destHunk = sym->Hunk) != NULL) {
		hunkNo = destHunk->HX->FinalHunkNo;
		if (hunkNo < 0 || hunkNo >= NumExtHunks)
		    cerror(EFATAL_RELOC_NOEXIST_HUNK, hunkNo, NumExtHunks);
		if (sym->Type == 1)	/*  32 bit relocatable def */
		    hunk->HL->CntReloc32[hunkNo] += FromMsbOrder(scan[len]);
		return(0);
	    }
	}
	cerror(EERROR_UNDEF_SYM, len*4, scan, hunk->Module->Node.ln_Name);
    } else if (type == 131 || type == 132 || type == 134 || type == 135) {     /*  16dr,pc 8pc, 32pc	*/
	if ((sym = FindSymbol(scan, len)) != NULL) {
	    Hunk *destHunk = sym->Hunk; 	/*  might be NULL	     */
	    long hunkNo;
	    long n;
	    ulong *newScan = scan + len;

	    if (destHunk) {
		hunkNo = destHunk->HX->FinalHunkNo;
		if (hunkNo < 0 || hunkNo >= NumExtHunks)
		    cerror(EFATAL_RELOC_NOEXIST_HUNK, hunkNo, NumExtHunks);
	    }
	    if (destHunk || sym->Type == 2) {
		long destOff = 0;

		if (sym->Type != 2) {		    /*	DATA symbol */
		    destOff = destHunk->Offset;
		    if (destHunk->HL != destHunk->HX)
			destOff += destHunk->HX->FinalSize;
		}
		if ((n = FromMsbOrder(*newScan)) != 0) {
		    char *dbase = (char *)hunk->Data;
		    ulong dlen = hunk->Bytes;

		    /*
		     *	relocate each 8/16 bit PC relative reference
		     */

		    for (++newScan; n--; ++newScan) {
			ulong doff = FromMsbOrder(*newScan);
			long pcrel = 0;

			if (doff >= dlen)   /*	illegal or jump table reloc */
			    continue;
			switch(type) {
			case 134:   /*	16 bit data-base relative reloc (NOT pcrel) */
			    dbprintf(2, ("ext sym t=%d reloc16D %s@%ld(%ld) to %s@%ld\n", sym->Type, HunkToStr(hunk), doff, FromMsbOrderShort(*(short *)(dbase + doff)), HunkToStr(destHunk), sym->Value));
			    switch(sym->Type) {
			    case 2:	/*  ABS symbol	*/
				pcrel = FromMsbOrderShort(*(short *)(dbase + doff)) + sym->Value;
				break;
			    case 1:	/*  DATA symbol */
				pcrel = (destOff + sym->Value + FromMsbOrderShort(*(short *)(dbase + doff))) - 32766;
				if ((destHunk->Flags & HF_SMALLDATA) == 0)
				    cerror(EERROR_A4REL_TO_FAR_OBJECT, sym->SymLen, sym->SymName, HunkToStr(destHunk), HunkToStr(hunk));
				break;
			    default:
				cerror(EERROR_RELOC_ILLEGAL_SYM, sym->Type);
				break;
			    }
			    if (pcrel < -32768 || pcrel > 32767)
				cerror(EERROR_RELOC16DATA_SYM_RANGE, pcrel, sym->SymLen, sym->SymName, HunkToStr(hunk));
			    *(short *)(dbase + doff) = ToMsbOrderShort(pcrel);
			    break;
			case 132:   /*	8 bit reloc */
			    dbprintf(2, ("ext sym t=%d reloc08 %s@%ld(%ld) to %s@%ld\n", sym->Type, HunkToStr(hunk), doff, *(char *)(dbase + doff), HunkToStr(destHunk), sym->Value));
			    switch(sym->Type) {
			    case 2:
				pcrel = *(char *)(dbase + doff) + sym->Value;
				break;
			    case 1:
				pcrel = (destOff + sym->Value + *(char *)(dbase + doff)) - (hunk->Offset + doff);
				break;
			    default:
				cerror(EERROR_RELOC_ILLEGAL_SYM, sym->Type);
				break;
			    }
			    if (pcrel < -128 || pcrel > 127)
				cerror(EERROR_RELOC8_RANGE, pcrel);
			    *(ubyte *)(dbase + doff) = pcrel;
			    break;
			case 131:   /*	16 bit pc-relative reloc */
			    dbprintf(2, ("ext sym t=%d reloc16pc %s@%ld(%ld) to %s@%ld\n", sym->Type, HunkToStr(hunk), doff, FromMsbOrderShort(*(short *)(dbase + doff)), HunkToStr(destHunk), sym->Value));

			    switch(sym->Type) {
			    case 2:	/*  Absolute Symbol */
				pcrel = FromMsbOrderShort(*(short *)(dbase + doff)) + sym->Value;
				break;
			    case 1:	/*  Data Symbol     */
				if (hunk->HL != destHunk->HX) {
				    if (AbsWordOpt) {
					pcrel = (destOff + sym->Value + FromMsbOrderShort(*(short *)(dbase + doff))) + WordBaseAddr;
				    } else {
					cerror(EERROR_PCREL_RELOC_TO_DATA,
					    hunk->Module->Node.ln_Name,
					    hunk->Node.ln_Name,
					    sym->SymLen, sym->SymName
					);
				    }
				} else {
				    pcrel = (destOff + sym->Value + FromMsbOrderShort(*(short *)(dbase + doff))) - (hunk->Offset + doff);
				}
				break;
			    default:
				cerror(EERROR_RELOC_ILLEGAL_SYM, sym->Type);
				break;
			    }
			    if (pcrel < -32768 || pcrel > 32767)
				cerror(EERROR_RELOC16DATA_SYM_RANGE, pcrel, sym->SymLen, sym->SymName, HunkToStr(hunk));
			    *(short *)(dbase + doff) = ToMsbOrderShort(pcrel);
			    break;
			case 135:   /*	32 bit pc-relative reloc */
			    dbprintf(2, ("ext sym t=%d reloc32pc %s@%ld(%ld) to %s@%ld\n", sym->Type, HunkToStr(hunk), doff, FromMsbOrder(*(long *)(dbase + doff)), HunkToStr(destHunk), sym->Value));

			    switch(sym->Type) {
			    case 2:	/*  Absolute Symbol	*/
				pcrel = FromMsbOrder(*(long *)(dbase + doff)) + sym->Value;
				break;
			    case 1:	/*  std label Symbol	*/
				pcrel = (destOff + sym->Value + FromMsbOrder(*(long *)(dbase + doff))) - (hunk->Offset + doff);
				break;
			    default:
				cerror(EERROR_RELOC_ILLEGAL_SYM, sym->Type);
				break;
			    }
			    *(long *)(dbase + doff) = ToMsbOrder(pcrel);
			    break;
			default:
			    break;
			}
		    }
		}
		return(0);
	    }
	}
	cerror(EERROR_UNDEF_SYM, len*4, scan, hunk->Module->Node.ln_Name);
    } else if (type == RESERVED_PCJMP_TYPE) {
	if ((sym = FindSymbol(scan, len)) != NULL) {
	    Hunk *destHunk;
	    long hunkNo;

	    if ((destHunk = sym->Hunk) != NULL) {
		hunkNo = destHunk->HX->FinalHunkNo;
		if (hunkNo < 0 || hunkNo >= NumExtHunks)
		    cerror(EFATAL_RANGE_HUNK, hunkNo, NumExtHunks);
		if (sym->Type != 1)	/*  32 bit relocatable def */
		    cerror(EFATAL_PC_REL_ILLEGAL_SYMBOL, sym->Type);
		++hunk->HL->CntReloc32[hunkNo];
		return(0);
	    }
	}
	cerror(EERROR_UNDEF_SYM, len*4, scan, hunk->Module->Node.ln_Name);
    }
    return(0);
}

/*
 *  RUN
 *
 *  This routine returns a dummy value, 0.  This routine modifies the hunk
 *  data appropriately for a reloc32.
 */

int
HunkExtSymRUN(Hunk *hunk, ubyte type, ulong len, ulong *scan)
{
    Sym *sym;
    Hunk *destHunk;
    long hunkNo = 0;

    if (type != 129 && type != RESERVED_PCJMP_TYPE)
	return(0);
    if ((sym = FindSymbol(scan, len)) == NULL)
	return(0);

    if ((destHunk = sym->Hunk) != NULL) {
	hunkNo = destHunk->HX->FinalHunkNo;
	if (hunkNo < 0 || hunkNo >= NumExtHunks)
	    cerror(EFATAL_EXTSYM_RELOC);
    }

    /*
     *	Special case... single relocation jump table beyond end of
     *	hunk->Bytes.
     *
     *	if scan[len] > 1 we actually only do one... see 'jump.c'.  We
     *	converted N pc-relative jumps to external jumps.
     */

    if (type == RESERVED_PCJMP_TYPE) {
	SanityCheck(32);
	if (destHunk == NULL)
	    return(0);
	SanityCheck(33);
	if (FromMsbOrder(scan[len])) {		    /*	# of relocs */
	    long bas = hunk->HL->CpyReloc32[hunkNo];
	    long n = FromMsbOrder(scan[len+1]) - hunk->Bytes;     /*	first reloc */

	    hunk->HL->ExtReloc32[hunkNo][bas] = ToMsbOrder(hunk->Offset + hunk->Bytes + n);
	    ++bas;
	    hunk->HL->CpyReloc32[hunkNo] = bas;

	    if (n < 0 || n >= hunk->TotalBytes - hunk->Bytes)
		cerror(ESOFT_JUMP_TABLE);

	    *(long *)((char *)hunk->JmpData + n) = ToMsbOrder(destHunk->Offset + sym->Value);
	}
	SanityCheck(34);
	return(0);
    }

    /*
     *	ABS symbols need no destination hunk.
     */

    if (destHunk == NULL && sym->Type != 2)
	return(0);

    /*
     *	copy relocation info
     */

    SanityCheck(35);
    if (sym->Type == 1) {	/*  only if relocatable def    */
	long bas = hunk->HL->CpyReloc32[hunkNo];
	long fnl = bas + FromMsbOrder(scan[len]);

#ifdef NOTDEF
	printf("HUNK %s hunkno %d vs %d NUMEXT %d\n", HunkToStr(hunk), hunk->HunkNo, hunkNo, NumExtHunks);
	printf("BAS %d FNL %d CNT %d\n", bas, fnl, hunk->HL->CntReloc32[hunkNo]);
	printf("LEN=%d SCAN[LEN]= %d relocs OFF=%d\n", len, scan[len], hunk->Offset);
#endif

	if (fnl > hunk->HL->CntReloc32[hunkNo])
	    cerror(EFATAL_EXTSYM_RELOC);
	copy_reloc(scan + len + 1, hunk->HL->ExtReloc32[hunkNo] + bas, FromMsbOrder(scan[len]), hunk->Offset);

	hunk->HL->CpyReloc32[hunkNo] = fnl;
    }

    SanityCheck(36);
    /*
     *	handle relocation
     */

    if (FromMsbOrder(scan[len])) {
	ulong n = FromMsbOrder(scan[len]);
	ulong *newScan;
	char *dbase = (char *)hunk->Data;
	ulong dlen = hunk->Bytes;
	ulong destOff = 0;

	if (sym->Type != 2) {
	    destOff = destHunk->Offset;
	    if (destHunk->HL != destHunk->HX)
		destOff += destHunk->HX->FinalSize;
	}

	if ((ResOpt || PIOpt) && sym->Type != 2) {
	    if (destHunk->Node.ln_Type == NT_BSS && !AbsWordOpt)
		cerror(EERROR_ABS_REFS_DATA_BSS, sym->SymLen, sym->SymName);
	    if (destHunk->Node.ln_Type == NT_DATA && sym != DataBasSym && !AbsWordOpt)
		cerror(EERROR_ABS_REFS_DATA_BSS, sym->SymLen, sym->SymName);
	}

	for (newScan = scan + len + 1; n--; ++newScan) {
	    ulong doff = FromMsbOrder(*newScan);
	    long rval = 0;

	    dbprintf(3, ("ext sym t=%d reloc32 %s@%ld to %s@%ld (%.*s)\n", sym->Type, HunkToStr(hunk), doff, HunkToStr(destHunk), sym->Value, sym->SymLen, sym->SymName));

	    if (doff >= dlen)	/*  illegal or jump table */
		continue;
	    if (doff & 1)
		cerror(EFATAL_RELOC_WORD_ALIGN);

	    switch(sym->Type) {
	    default:
		cerror(EFATAL_RELOC_ILLEGAL_SYM, sym->Type);
		break;
	    case 2:	    /*	abs  */
		rval = FromMsbOrder(*(ulong *)(dbase + doff)) + sym->Value;
		break;
	    case 1:	    /*	def  */
		rval = destOff + FromMsbOrder(*(ulong *)(dbase + doff)) + sym->Value;
		break;
	    }
	    *(ulong *)(dbase + doff) = ToMsbOrder(rval);
	}
    }
    SanityCheck(37);
    return(0);
}

void
IllegalHunk(short er_flag, Hunk *hunk)
{
    if (hunk)
	cerror(er_flag | EILLEGAL_HUNK, hunk, hunk->Node.ln_Type, hunk->Module->Node.ln_Name);
    else
	cerror(er_flag | EILLEGAL_HUNK, NULL, -1, "???");
}

