1260 lines
53 KiB
C
1260 lines
53 KiB
C
/*
|
|
* TITLE
|
|
* newent.c
|
|
* Pete Stewart
|
|
* (C) Copyright Microsoft Corp 1984-1989
|
|
* 1 October 1984
|
|
*
|
|
* DESCRIPTION
|
|
* This file contains routines for the DOS 4.0 linker
|
|
* that manage per-segment entry point information.
|
|
*
|
|
* It also contains routines that manage per-segment
|
|
* relocation information.
|
|
*
|
|
* Modifications:
|
|
*
|
|
* 09-Feb-1989 RB Fix Insert().
|
|
*/
|
|
|
|
#include <minlit.h> /* Basic type definitions */
|
|
#include <bndtrn.h> /* Constants and compound types */
|
|
#include <bndrel.h> /* Types and constants */
|
|
#include <lnkio.h> /* I/O definitions */
|
|
#include <newexe.h> /* DOS & 286 .EXE format data structures */
|
|
#if EXE386
|
|
#include <exe386.h> /* 386 .EXE format data structures */
|
|
#endif
|
|
#include <lnkmsg.h> /* Error messages */
|
|
#include <extern.h> /* External declarations */
|
|
#include <impexp.h>
|
|
|
|
#define hashra(ra) (WORD) ((ra) % HEPLEN)
|
|
/* Function to hash offset */
|
|
#if NOT EXE386
|
|
#define hashrlc(r) (((NR_SEGNO(*r) << NR_STYPE(*r)) + NR_ENTRY(*r)) & HASH_SIZE - 1)
|
|
/* Hash relocation item */
|
|
#define EOC ((RATYPE) 0xFFFF)
|
|
/* End-of-chain marker */
|
|
#endif
|
|
|
|
#define IsInSet(x) ((pOrdinalSet[(x) >> 3] & BitMask[(x) & 0x07]) != 0)
|
|
#define NotInSet(x) ((pOrdinalSet[(x) >> 3] & BitMask[(x) & 0x07]) == 0)
|
|
#define SetBit(x) (pOrdinalSet[(x) >> 3] |= BitMask[(x) & 0x07])
|
|
#define MaxIndex 8192
|
|
#define ET_END 0xffff
|
|
|
|
/*
|
|
* FUNCTION PROTOTYPES
|
|
*/
|
|
|
|
|
|
LOCAL void NEAR NewBundle(unsigned short type);
|
|
LOCAL WORD NEAR MatchRlc(RLCPTR rlcp0,
|
|
RLCPTR rlcp1);
|
|
#if NOT QCLINK
|
|
LOCAL void NEAR NewEntry(unsigned short sa,
|
|
RATYPE ra,
|
|
unsigned char flags,
|
|
unsigned short hi,
|
|
unsigned short ord);
|
|
LOCAL void SavExp1(APROPNAMEPTR apropexp,
|
|
RBTYPE rhte,
|
|
RBTYPE rprop,
|
|
WORD fNewHte);
|
|
LOCAL void SavExp2(APROPNAMEPTR apropexp,
|
|
RBTYPE rhte,
|
|
RBTYPE rprop,
|
|
WORD fNewHte);
|
|
LOCAL WORD NEAR BuildList(WORD NewOrd, RBTYPE NewProp);
|
|
LOCAL WORD NEAR FindFreeRange(void);
|
|
LOCAL WORD NEAR Insert(RBTYPE NewProp);
|
|
#endif
|
|
|
|
|
|
/*
|
|
* LOCAL DATA
|
|
*/
|
|
|
|
#if NOT QCLINK
|
|
#pragma pack(1)
|
|
|
|
typedef struct _BUNDLE
|
|
{
|
|
BYTE count;
|
|
BYTE type;
|
|
}
|
|
BUNDLE;
|
|
|
|
#pragma pack()
|
|
|
|
LOCAL WORD ceCurBnd; /* No. of entries in current bundle */
|
|
LOCAL WORD offCurBnd; /* Offset of current bundle header */
|
|
LOCAL WORD tyCurBnd; /* Type of current bundle */
|
|
|
|
LOCAL WORD ordMac; /* Highest entry ordinal number */
|
|
LOCAL BYTE *pOrdinalSet;
|
|
#if EXE386
|
|
LOCAL APROPEXPPTR pExport; /* Pointer to export property cell */
|
|
#endif
|
|
LOCAL struct {
|
|
WORD ord; /* Current available ordinal */
|
|
WORD count; /* Number of free ordinals in range */
|
|
}
|
|
FreeRange;
|
|
|
|
LOCAL BYTE BitMask[] = { /* Bit mask used in set operations */
|
|
0x01,
|
|
0x02,
|
|
0x04,
|
|
0x08,
|
|
0x10,
|
|
0x20,
|
|
0x40,
|
|
0x80 };
|
|
|
|
LOCAL WORD MinOrd = 0; /* Min ordinal number see so far */
|
|
LOCAL WORD MaxOrd = 0; /* Max ordinal number see so far */
|
|
RBTYPE pMinOrd = NULL; /* Pointer to property cell with MinOrd */
|
|
LOCAL RBTYPE pMaxOrd = NULL; /* Pointer to property cell with MaxOrd */
|
|
LOCAL RBTYPE pStart;
|
|
|
|
#ifndef UNPOOLED_RELOCS
|
|
LOCAL void * pPoolRlc; /* memory pool for relocations */
|
|
#endif
|
|
|
|
|
|
|
|
#if NOT EXE386
|
|
LOCAL void NEAR NewBundle(type) /* Make a new bundle */
|
|
WORD type; /* Type of new bundle */
|
|
{
|
|
BUNDLE FAR *pBnd; /* Ptr to start of bundle or entry */
|
|
BUNDLE bnd;
|
|
|
|
if (EntryTable.byteMac != 0)
|
|
{
|
|
// If there is a previous bundle patch the count filed
|
|
|
|
pBnd = (BUNDLE FAR *) &(EntryTable.rgByte[offCurBnd]);
|
|
pBnd->count = (BYTE) ceCurBnd;
|
|
}
|
|
|
|
bnd.count = 0;
|
|
bnd.type = (BYTE) type;
|
|
offCurBnd = AddEntry((BYTE *) &bnd, sizeof(BUNDLE));
|
|
ceCurBnd = 0;
|
|
tyCurBnd = type;
|
|
|
|
if (type == ET_END)
|
|
EntryTable.byteMac--;
|
|
}
|
|
#endif
|
|
|
|
|
|
/****************************************************************
|
|
* *
|
|
* NAME: NewEntry *
|
|
* *
|
|
* DESCRIPTION: *
|
|
* *
|
|
* This function makes an entry in the Entry Table for a *
|
|
* given file segment number, offset, and flag set. It also *
|
|
* makes an entry in the entry address hash table on the *
|
|
* given hash chain for the new entry point. N.B.: this *
|
|
* function assumes the static variable ordMac is set to the *
|
|
* desired ordinal value for the entry being added. *
|
|
* *
|
|
* ARGUMENTS: *
|
|
* *
|
|
* SATYPE sa File segment number *
|
|
* RATYPE ra Offset *
|
|
* FTYPE flags Entry point flags *
|
|
* WORD hi Hash table index *
|
|
* WORD ord New ordinal *
|
|
* *
|
|
* RETURNS: *
|
|
* *
|
|
* WORD Offset in Entry Table *
|
|
* *
|
|
* SIDE EFFECTS: *
|
|
* *
|
|
* Maintains a hash table hashing file segment/offset pairs *
|
|
* to entry table offsets. Builds in virtual memory the *
|
|
* Entry Table. Updates the following variables: *
|
|
* *
|
|
* WORD offCurBnd Offset of start of current *
|
|
* bundle of entries. *
|
|
* WORD ceCurBnd Count of entries in cur- *
|
|
* rent bundle. *
|
|
* WORD tyCurBnd Type of current bundle. *
|
|
* WORD cbEntTab Size of Entry Table in *
|
|
* bytes. *
|
|
* *
|
|
* NOTE: THIS FUNCTION CALLS THE VIRTUAL MEMORY MANAGER. *
|
|
* *
|
|
****************************************************************/
|
|
|
|
LOCAL void NEAR NewEntry(sa,ra,flags,hi,ord)
|
|
SATYPE sa; /* File segment number */
|
|
RATYPE ra; /* Segment offset */
|
|
FTYPE flags; /* Entry point flags */
|
|
WORD hi; /* Hash table index */
|
|
WORD ord; /* New ordinal */
|
|
{
|
|
EPTYPE FAR *ep; /* Entry point node */
|
|
#if NOT EXE386
|
|
WORD tyEntry; /* Entry type */
|
|
WORD cbEntry; /* Length of entry in bytes */
|
|
BYTE entry[6]; /* The entry itself - NE version */
|
|
#endif
|
|
#if EXE386
|
|
static WORD prevEntryOrd; // Previous export ordinal
|
|
DWORD eatEntry; /* The entry itself - LE version */
|
|
#endif
|
|
|
|
#if NOT EXE386
|
|
if(sa == SANIL) /* If absolute symbol */
|
|
tyEntry = BNDABS; /* use fake segment # */
|
|
else if (TargetOs == NE_OS2)
|
|
tyEntry = NonConfIOPL(mpsaflags[sa]) ? BNDMOV: sa;
|
|
else
|
|
tyEntry = (mpsaflags[sa] & NSMOVE)? BNDMOV: sa;
|
|
/* Get the entry type */
|
|
/* If not library, or realmode and not solo data, clear local data bit. */
|
|
if(!(vFlags & NENOTP) || (!(vFlags & NEPROT) && !(vFlags & NESOLO)))
|
|
flags &= ~2;
|
|
entry[0] = (BYTE) flags; /* Set the entry flags */
|
|
if(tyEntry == BNDMOV /* If entry is in movable segment */
|
|
#if O68K
|
|
&& iMacType == MAC_NONE
|
|
#endif
|
|
)
|
|
{
|
|
++cMovableEntries; /* Increment movable entries count */
|
|
cbEntry = 6; /* Entry is six bytes long */
|
|
entry[1] = 0xCD; /* INT... */
|
|
entry[2] = 0x3F; /* ...3FH */
|
|
entry[3] = (BYTE) sa; /* File segment number */
|
|
entry[4] = (BYTE) ra; /* Lo-byte of offset */
|
|
entry[5] = (BYTE)(ra >> BYTELN);/* Hi-byte of offset */
|
|
}
|
|
else /* Else if fixed entry */
|
|
{
|
|
cbEntry = 3; /* Entry is three bytes long */
|
|
entry[1] = (BYTE) ra; /* Lo-byte of offset */
|
|
entry[2] = (BYTE)(ra >> BYTELN);/* Hi-byte of offset */
|
|
}
|
|
#endif
|
|
|
|
#if EXE386
|
|
/*
|
|
* This function creates one entry in the Export Address Table.
|
|
* The EAT table is stored in linker's VM in area AREAEAT. The
|
|
* global variable cbEntTab always points to free space in the
|
|
* AREAEAT.
|
|
*/
|
|
|
|
|
|
eatEntry = 0L;
|
|
if ((prevEntryOrd != 0) && (ord > prevEntryOrd + 1))
|
|
{
|
|
// Write unused entries in the Export Address Table
|
|
|
|
for (; prevEntryOrd < ord - 1; prevEntryOrd++)
|
|
{
|
|
if (cbEntTab + sizeof(DWORD) > MEGABYTE)
|
|
Fatal(ER_eatovf, MEGABYTE);
|
|
vmmove(sizeof(DWORD), &eatEntry, (long)(AREAEAT + cbEntTab), TRUE);
|
|
cbEntTab += sizeof(DWORD);
|
|
}
|
|
}
|
|
prevEntryOrd = ord;
|
|
|
|
// FLAT address
|
|
|
|
eatEntry = mpsaBase[sa] + ra;
|
|
|
|
/* Check for Entry Table overflow */
|
|
|
|
if (cbEntTab + sizeof(DWORD) > MEGABYTE)
|
|
Fatal(ER_eatovf, MEGABYTE);
|
|
#endif
|
|
|
|
/* Insert the new entry */
|
|
|
|
#if NOT EXE386
|
|
if (tyCurBnd != tyEntry || ceCurBnd == BNDMAX)
|
|
NewBundle(tyEntry); /* Make a new bundle if needed */
|
|
|
|
++ceCurBnd; /* Increment counter */
|
|
#endif
|
|
|
|
/* Save entry in virtual memory */
|
|
|
|
#if EXE386
|
|
vmmove(sizeof(DWORD), &eatEntry, (long)(AREAEAT + cbEntTab), TRUE);
|
|
#else
|
|
AddEntry(entry, cbEntry);
|
|
#endif
|
|
ep = (EPTYPE FAR *) GetMem(sizeof(EPTYPE));
|
|
ep->ep_next = htsaraep[hi]; /* Link old chain to new node */
|
|
ep->ep_sa = sa; /* Save the file segment number */
|
|
ep->ep_ra = ra; /* Save offset */
|
|
ep->ep_ord = ord; /* Save Entry Table ordinal */
|
|
htsaraep[hi] = ep; /* Make new node head of chain */
|
|
}
|
|
|
|
/****************************************************************
|
|
* *
|
|
* NAME: MpSaRaEto *
|
|
* *
|
|
* DESCRIPTION: *
|
|
* *
|
|
* This function returns an Entry Table ordinal given a *
|
|
* file segment number (sa) for a segment and an offset in *
|
|
* that segment. *
|
|
* *
|
|
* ARGUMENTS: *
|
|
* *
|
|
* SATYPE sa File segment number *
|
|
* RATYPE ra Offset *
|
|
* *
|
|
* RETURNS: *
|
|
* *
|
|
* WORD Entry Table ordinal *
|
|
* *
|
|
* SIDE EFFECTS: *
|
|
* *
|
|
* Calls NewEntry(). Increments ordMac. *
|
|
* *
|
|
* NOTE: THIS FUNCTION CALLS THE VIRTUAL MEMORY MANAGER. *
|
|
* *
|
|
****************************************************************/
|
|
|
|
WORD NEAR MpSaRaEto(sa,ra)
|
|
SATYPE sa; /* File segment number */
|
|
RATYPE ra; /* Segment offset */
|
|
{
|
|
WORD hi; /* Hash table index */
|
|
EPTYPE FAR *ep; /* Entry point node */
|
|
|
|
hi = hashra(ra); /* Hash the offset */
|
|
for (ep = htsaraep[hi]; ep != NULL; ep = ep->ep_next)
|
|
{ /* Loop through hash chain */
|
|
if (ep->ep_sa == sa && ep->ep_ra == ra)
|
|
return(ep->ep_ord);
|
|
/* If match found, return number */
|
|
}
|
|
|
|
// At this point, we know a new entry must be created.
|
|
|
|
NewEntry(sa, ra, 0, hi, ++ordMac); /* Add a new entry */
|
|
return(ordMac); /* Return Entry Table ordinal */
|
|
}
|
|
|
|
|
|
/****************************************************************
|
|
* *
|
|
* NAME: BuildList *
|
|
* *
|
|
* DESCRIPTION: *
|
|
* *
|
|
* This function links the property cells of exports with *
|
|
* preassigned ordinals into list. Global pointers pMinOrd *
|
|
* and pMaxOrd points to the begin and end of this list. The *
|
|
* preassigned ordinals are stored in the set pointed by the *
|
|
* global pointer pOrdinalSet. *
|
|
* *
|
|
* ARGUMENTS: *
|
|
* *
|
|
* WORD NewOrd New preassigned ordinal *
|
|
* RBTYPE NewProp Addr of property cell *
|
|
* *
|
|
* RETURNS: *
|
|
* *
|
|
* TRUE if ordinal seen for the first time, otherwise FALSE. *
|
|
* *
|
|
* SIDE EFFECTS: *
|
|
* *
|
|
* Changes pMinOrd and pMaxOrd pointers, sets bits in ordinal *
|
|
* set and sets MinOrd, MaxOrd seen so far. *
|
|
* *
|
|
****************************************************************/
|
|
|
|
|
|
LOCAL WORD NEAR BuildList(WORD NewOrd, RBTYPE NewProp)
|
|
|
|
{
|
|
RBTYPE pTemp; /* Temporary pointer to property cell */
|
|
APROPEXPPTR pExpCurr; /* Export record pointer */
|
|
APROPEXPPTR pExpPrev; /* Export record pointer */
|
|
|
|
|
|
if (!MinOrd && !MaxOrd)
|
|
{ /* First time call */
|
|
MinOrd = MaxOrd = NewOrd;
|
|
pMinOrd = pMaxOrd = NewProp;
|
|
SetBit(NewOrd);
|
|
return TRUE;
|
|
}
|
|
|
|
if (IsInSet(NewOrd))
|
|
return FALSE; /* Ordinal all ready used */
|
|
|
|
SetBit(NewOrd); /* Set bit in ordinal set */
|
|
|
|
if (NewOrd > MaxOrd)
|
|
{ /* Add new at the list end */
|
|
pExpCurr = (APROPEXPPTR ) FetchSym(pMaxOrd,TRUE);
|
|
pExpCurr->ax_NextOrd = NewProp;
|
|
MARKVP();
|
|
pMaxOrd = NewProp;
|
|
MaxOrd = NewOrd;
|
|
pExpCurr = (APROPEXPPTR ) FetchSym(NewProp,TRUE);
|
|
pExpCurr->ax_NextOrd = NULL;
|
|
MARKVP();
|
|
}
|
|
else if (NewOrd < MinOrd)
|
|
{ /* Add new at list begin */
|
|
pExpCurr = (APROPEXPPTR ) FetchSym(NewProp,TRUE);
|
|
pExpCurr->ax_NextOrd = pMinOrd;
|
|
MARKVP();
|
|
pMinOrd = NewProp;
|
|
MinOrd = NewOrd;
|
|
}
|
|
else
|
|
{ /* Add new in the middle of list */
|
|
pTemp = pMinOrd;
|
|
do
|
|
{
|
|
pExpPrev = (APROPEXPPTR ) FetchSym(pTemp,TRUE);
|
|
pExpCurr = (APROPEXPPTR ) FetchSym(pExpPrev->ax_NextOrd,TRUE);
|
|
if (NewOrd < pExpCurr->ax_ord)
|
|
{
|
|
pTemp = pExpPrev->ax_NextOrd;
|
|
pExpPrev->ax_NextOrd = NewProp;
|
|
MARKVP();
|
|
pExpCurr = (APROPEXPPTR ) FetchSym(NewProp,TRUE);
|
|
pExpCurr->ax_NextOrd = pTemp;
|
|
MARKVP();
|
|
break;
|
|
}
|
|
pTemp = pExpPrev->ax_NextOrd;
|
|
} while (pTemp);
|
|
}
|
|
if(NewOrd > ordMac) ordMac = NewOrd; /* Remember largest ordinal */
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/****************************************************************
|
|
* *
|
|
* NAME: FindFreeRange *
|
|
* *
|
|
* DESCRIPTION: *
|
|
* *
|
|
* This function finds in the ordinal set first available *
|
|
* free range of ordinals. *
|
|
* *
|
|
* ARGUMENTS: *
|
|
* *
|
|
* Nothing. *
|
|
* *
|
|
* RETURNS: *
|
|
* *
|
|
* TRUE if free range found, otherwise FALSE. *
|
|
* *
|
|
* SIDE EFFECTS: *
|
|
* *
|
|
* Changes FreeRange descriptor by setting first free ordinal *
|
|
* and the lenght of range. *
|
|
* *
|
|
****************************************************************/
|
|
|
|
|
|
|
|
LOCAL WORD NEAR FindFreeRange(void)
|
|
|
|
{
|
|
int ByteIndex;
|
|
int BitIndex;
|
|
|
|
|
|
ByteIndex = FreeRange.ord >> 3;
|
|
BitIndex = FreeRange.ord & 0x07;
|
|
|
|
while ((pOrdinalSet[ByteIndex] & BitMask[BitIndex]) &&
|
|
ByteIndex < MaxIndex)
|
|
{ /* Skip all used ordinals */
|
|
FreeRange.ord++;
|
|
BitIndex = (BitIndex + 1) & 0x07;
|
|
if (!BitIndex)
|
|
ByteIndex++;
|
|
}
|
|
|
|
if (ByteIndex < MaxIndex)
|
|
{
|
|
if (FreeRange.ord > MaxOrd)
|
|
{
|
|
FreeRange.count = 0xffff - MaxOrd;
|
|
return TRUE;
|
|
}
|
|
|
|
do
|
|
{ /* Count all unused ordinals */
|
|
FreeRange.count++;
|
|
BitIndex = (BitIndex + 1) & 0x07;
|
|
if (!BitIndex)
|
|
ByteIndex++;
|
|
} while (!(pOrdinalSet[ByteIndex] & BitMask[BitIndex]) &&
|
|
ByteIndex < MaxIndex);
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
|
|
/****************************************************************
|
|
* *
|
|
* NAME: Insert *
|
|
* *
|
|
* DESCRIPTION: *
|
|
* *
|
|
* This function inserts into the exports list new property *
|
|
* cell without preassigned ordinal. It assigns new ordinal. *
|
|
* *
|
|
* ARGUMENTS: *
|
|
* *
|
|
* RBTYPE NewProp New property cell to insert *
|
|
* *
|
|
* RETURNS: *
|
|
* *
|
|
* New assigned ordinal. *
|
|
* *
|
|
* SIDE EFFECTS: *
|
|
* *
|
|
* Changes FreeRange descriptor and MaxOrd assigned so far. *
|
|
* *
|
|
****************************************************************/
|
|
|
|
|
|
|
|
LOCAL WORD NEAR Insert(RBTYPE NewProp)
|
|
|
|
{
|
|
APROPEXPPTR pExpCurr; /* Export record pointer */
|
|
APROPEXPPTR pExpPrev; /* Export record pointer */
|
|
WORD NewOrd;
|
|
RBTYPE pTemp, rbPrev, rbCur;
|
|
/*
|
|
* On entry, pStart points to the place in the export list where
|
|
* NewProp should be inserted. If NULL, the list is empty.
|
|
*/
|
|
if (!FreeRange.count)
|
|
{
|
|
/* No more space left in current free range; find the next one. */
|
|
if (!FindFreeRange())
|
|
Fatal(ER_expmax);
|
|
/*
|
|
* Update pStart (the insertion point) by walking down the list and
|
|
* finding the first element whose ordinal is greater than the new
|
|
* ordinal, or the end of the list if none is found.
|
|
*/
|
|
rbPrev = RHTENIL;
|
|
for (rbCur = pStart; rbCur != RHTENIL; rbCur = pExpCurr->ax_NextOrd)
|
|
{
|
|
pExpCurr = (APROPEXPPTR) FetchSym(rbCur, FALSE);
|
|
if (pExpCurr->ax_ord > FreeRange.ord)
|
|
break;
|
|
rbPrev = rbCur;
|
|
}
|
|
/* Set pStart to the insertion point. */
|
|
pStart = rbPrev;
|
|
}
|
|
|
|
/* Insert new property cell */
|
|
|
|
NewOrd = FreeRange.ord++;
|
|
FreeRange.count--;
|
|
SetBit(NewOrd);
|
|
pExpCurr = (APROPEXPPTR ) FetchSym(NewProp,TRUE);
|
|
pExpCurr->ax_ord = NewOrd;
|
|
MARKVP();
|
|
if (pStart != NULL)
|
|
{
|
|
// We're not inserting at head of list. Append new cell to previous
|
|
// cell.
|
|
pExpPrev = (APROPEXPPTR ) FetchSym(pStart,TRUE);
|
|
pTemp = pExpPrev->ax_NextOrd;
|
|
pExpPrev->ax_NextOrd = NewProp;
|
|
MARKVP();
|
|
}
|
|
else
|
|
{
|
|
// We're inserting at head of list. Set head list pointer to new
|
|
// cell.
|
|
pTemp = pMinOrd;
|
|
pMinOrd = NewProp;
|
|
}
|
|
/*
|
|
* Set the next pointer to the following element in the list.
|
|
*/
|
|
pExpCurr = (APROPEXPPTR ) FetchSym(NewProp,TRUE);
|
|
pExpCurr->ax_NextOrd = pTemp;
|
|
MARKVP();
|
|
/*
|
|
* Update MaxOrd and pStart.
|
|
*/
|
|
if (NewOrd > MaxOrd)
|
|
MaxOrd++;
|
|
pStart = NewProp;
|
|
return NewOrd;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/****************************************************************
|
|
* *
|
|
* NAME: SavExp1 *
|
|
* *
|
|
* DESCRIPTION: *
|
|
* *
|
|
* This function places the virtual addresses of property *
|
|
* cells for exports with preassigned ordinals into a table *
|
|
* which will later be used to create the first part of the *
|
|
* entry table. It also verifies the validity of the ex- *
|
|
* ports. *
|
|
* *
|
|
* ARGUMENTS: *
|
|
* *
|
|
* APROPEXPPTR apropexp Export record pointer *
|
|
* RBTYPE rhte Addr of hash table entry *
|
|
* RBTYPE rprop Address of export record *
|
|
* FTYPE fNewHte New hash table entry flag *
|
|
* *
|
|
* RETURNS: *
|
|
* *
|
|
* Nothing. *
|
|
* *
|
|
* SIDE EFFECTS: *
|
|
* *
|
|
* Entries are made in a table on the stack to which the *
|
|
* local static variable prb points. The global variable *
|
|
* ordMac is set to the highest ordinal value found. Pro- *
|
|
* perty cells for exports are updated to contain the file *
|
|
* segment number and offset of the entry point. *
|
|
* *
|
|
* NOTE: THIS FUNCTION CALLS THE VIRTUAL MEMORY MANAGER. *
|
|
* *
|
|
****************************************************************/
|
|
|
|
LOCAL void SavExp1(APROPNAMEPTR apropexp,
|
|
RBTYPE rhte,
|
|
RBTYPE rprop,
|
|
WORD fNewHte)
|
|
{
|
|
AHTEPTR ahte; /* Pointer to hash table entry */
|
|
LOCAL APROPNAMEPTR apropnam; /* Public definition record pointer */
|
|
LOCAL APROPPTR aprop; /* temp. pointer */
|
|
WORD ord; /* Entry ordinal */
|
|
SATYPE sa; /* File segment number */
|
|
RATYPE ra; /* Offset in segment */
|
|
WORD fStartSeen=0; /* Have we seen the start of the list */
|
|
APROPEXPPTR pExport;
|
|
char *p;
|
|
|
|
ASSERT(fNewHte); /* Only once per customer */
|
|
pExport = (APROPEXPPTR ) apropexp;
|
|
if((ord = pExport->ax_ord) >= EXPMAX)
|
|
{ /* If ordinal too big */
|
|
pExport->ax_ord = 0; /* Treat as unspecified */
|
|
ord = 0;
|
|
MARKVP(); /* Page has changed */
|
|
/* Issue error message */
|
|
ahte = (AHTEPTR ) FetchSym(rhte,FALSE);
|
|
OutError(ER_ordmax,1 + GetFarSb(ahte->cch));
|
|
}
|
|
apropnam = (APROPNAMEPTR ) FetchSym(pExport->ax_symdef,FALSE);
|
|
/* Fetch the public symbol def. */
|
|
|
|
|
|
for (aprop = (APROPPTR) apropnam; aprop->a_attr != ATTRPNM;)
|
|
{
|
|
|
|
if(aprop->a_attr == ATTRALIAS) /* If an alias */
|
|
{
|
|
aprop = (APROPPTR) FetchSym(
|
|
((APROPALIASPTR)aprop)->al_sym, FALSE );
|
|
if (aprop->a_attr == ATTRPNM) /* The substitute is a public-OK */
|
|
break;
|
|
}
|
|
|
|
aprop = (APROPPTR) FetchSym (aprop->a_next, FALSE);
|
|
if (aprop->a_next == NULL && aprop->a_attr == ATTRNIL) /* Beginning of list */
|
|
{
|
|
aprop = (APROPPTR) FetchSym ( ((AHTEPTR)aprop)->rprop, FALSE);
|
|
fStartSeen ++;
|
|
}
|
|
|
|
if ((aprop != (APROPPTR) apropnam) && (fStartSeen<2))
|
|
continue; /* Find an ALIAS or the starting point */
|
|
|
|
/* Issue error message */
|
|
if(SbCompare(GetPropName(FetchSym(rhte,FALSE)), GetPropName(FetchSym(pExport->ax_symdef,FALSE)), 0))
|
|
{
|
|
/* skip the (alias %s) part */
|
|
OutError(ER_expund,1 + GetPropName(FetchSym(rhte,FALSE)), " ");
|
|
}
|
|
else
|
|
{
|
|
if(p = GetMem(SBLEN + 20))
|
|
sprintf(p, " (alias %s) ", 1 + GetPropName(FetchSym(pExport->ax_symdef,FALSE)));
|
|
OutError(ER_expund,1 + GetPropName(FetchSym(rhte,FALSE)),p);
|
|
if(p) FreeMem(p);
|
|
}
|
|
/* Flag export as undefined */
|
|
pExport = (APROPEXPPTR ) FetchSym(rprop,TRUE);
|
|
pExport->ax_symdef = RHTENIL;
|
|
return;
|
|
}
|
|
|
|
apropnam = (APROPNAMEPTR) aprop;
|
|
sa = mpsegsa[mpgsnseg[apropnam->an_gsn]];
|
|
/* Get the file segment number */
|
|
ra = apropnam->an_ra; /* Get the offset in the segment */
|
|
#if NOT EXE386
|
|
if(apropnam->an_flags & FIMPORT) /* If public is an import */
|
|
{
|
|
/* Issue error message */
|
|
OutError(ER_expimp,1 + GetPropName(FetchSym(rhte,FALSE)),
|
|
1 + GetPropName(FetchSym(pExport->ax_symdef,FALSE)));
|
|
/* Flag export as undefined */
|
|
pExport = (APROPEXPPTR ) FetchSym(rprop,TRUE);
|
|
pExport->ax_symdef = RHTENIL;
|
|
return;
|
|
}
|
|
if (!IsIOPL(mpsaflags[sa])) /* If not I/O privileg segment */
|
|
pExport->ax_flags &= 0x07; /* force parameter words to 0 */
|
|
#endif
|
|
pExport = (APROPEXPPTR ) FetchSym(rprop,TRUE);
|
|
/* Fetch the export property cell */
|
|
pExport->ax_sa = sa; /* Set the file segment number */
|
|
pExport->ax_ra = ra; /* Set the offset in the segment */
|
|
MARKVP();
|
|
if(ord == 0) return; /* Skip unspecified ordinals for now */
|
|
if(!BuildList(ord, rprop)) /* If ordinal conflict found */
|
|
{
|
|
/*
|
|
* Issue error message for ordinal conflict
|
|
*/
|
|
OutError(ER_ordmul,ord,1 + GetPropName(FetchSym(rhte,FALSE)));
|
|
return;
|
|
}
|
|
}
|
|
|
|
/****************************************************************
|
|
* *
|
|
* NAME: SavExp2 *
|
|
* *
|
|
* DESCRIPTION: *
|
|
* *
|
|
* This function enters those exports without preassigned *
|
|
* ordinal numbers into the table to which prb refers. It *
|
|
* also builds the resident and non-resident name tables. *
|
|
* *
|
|
* ARGUMENTS: *
|
|
* *
|
|
* APROPEXPPTR apropexp Export record pointer *
|
|
* RBTYPE rhte Addr of hash table entry *
|
|
* RBTYPE rprop Address of export record *
|
|
* FTYPE fNewHte New hash table entry flag *
|
|
* *
|
|
* RETURNS: *
|
|
* *
|
|
* Nothing. *
|
|
* *
|
|
* SIDE EFFECTS: *
|
|
* *
|
|
* Entries are made in a table in virtual memory. A global *
|
|
* variable is set to contain the highest ordinal value seen. *
|
|
* *
|
|
* NOTE: THIS FUNCTION CALLS THE VIRTUAL MEMORY MANAGER. *
|
|
* *
|
|
****************************************************************/
|
|
|
|
LOCAL void SavExp2(APROPNAMEPTR apropexp,
|
|
RBTYPE rhte,
|
|
RBTYPE rprop,
|
|
WORD fNewHte)
|
|
{
|
|
AHTEPTR ahte; /* Pointer to hash table entry */
|
|
APROPNAMEPTR apropnam; /* Public definition record pointer */
|
|
WORD ord; /* Ordinal number */
|
|
WORD cb; /* # of bytes in name table entry */
|
|
SATYPE sa; /* File segment number */
|
|
FTYPE fResNam; /* True if name is resident */
|
|
FTYPE fNoName; /* True if discard name */
|
|
APROPEXPPTR pExport;
|
|
SBTYPE sbName;
|
|
|
|
|
|
pExport = (APROPEXPPTR ) apropexp;
|
|
if (pExport->ax_symdef == RHTENIL) return;
|
|
/* Skip undefined exports */
|
|
apropnam = (APROPNAMEPTR ) FetchSym(pExport->ax_symdef,FALSE);
|
|
/* Fetch the public symbol def. */
|
|
sa = mpsegsa[mpgsnseg[apropnam->an_gsn]];
|
|
/* Get the file segment number */
|
|
#if NOT EXE386
|
|
if (!IsIOPL(mpsaflags[sa])) /* If not I/O privileg segment */
|
|
pExport->ax_flags &= 0x07; /* force parameter words to 0 */
|
|
#endif
|
|
if ((ord = pExport->ax_ord) == 0) /* If unassigned export found */
|
|
{
|
|
ord = Insert(rprop); /* Add new export to the list */
|
|
fResNam = (FTYPE) TRUE; /* Name is resident */
|
|
}
|
|
else
|
|
fResNam = (FTYPE) ((pExport->ax_nameflags & RES_NAME) != 0);
|
|
/* Else set resident name flag */
|
|
fNoName = (FTYPE) ((pExport->ax_nameflags & NO_NAME) != 0);
|
|
ahte = (AHTEPTR ) FetchSym(rhte,FALSE);
|
|
/* Get external name */
|
|
cb = B2W(ahte->cch[0]) + 1; /* Number of bytes incl. length byte */
|
|
#if EXE386
|
|
/*
|
|
* For linear-executable build the Export Name Pointers Table and
|
|
* Export Name Table. For linear-executable all exported names
|
|
* are put in one Exported Name Table; there is no distiction
|
|
* between resident and non-resident tables. We still support
|
|
* the NONAME keyword by removing the exported name
|
|
* from the Export Name Table.
|
|
*/
|
|
|
|
if (!fNoName)
|
|
{
|
|
if (cb > sizeof(sbName) - sizeof(BYTE))
|
|
cb = sizeof(sbName) - sizeof(BYTE);
|
|
memcpy(sbName, GetFarSb(ahte->cch), cb + 1);
|
|
/* Copy the name to local buffer */
|
|
if (fIgnoreCase)
|
|
SbUcase(sbName); /* Make upper case if ignoring case */
|
|
|
|
// Store the pointer to the name; for now it is an offset from
|
|
// the begin of Export Name Table (be sure that name doesn't
|
|
// cross VM page boundary). Later when the size of the
|
|
// Export Directory Table plus the size of Export Address Table
|
|
// becomes known we update the entries in the Export Name Pointer
|
|
// Table to become a relative virtual address from the Export
|
|
// Directory Table.
|
|
|
|
if ((cbExpName & (PAGLEN - 1)) + cb > PAGLEN)
|
|
cbExpName = (cbExpName + PAGLEN - 1) & ~(PAGLEN - 1);
|
|
|
|
vmmove(sizeof(DWORD), &cbExpName, AREANAMEPTR + cbNamePtr, TRUE);
|
|
cbNamePtr += sizeof(DWORD);
|
|
if (cbNamePtr > NAMEPTRSIZE)
|
|
Fatal(ER_nameptrovf, NAMEPTRSIZE);
|
|
|
|
// Store exported name
|
|
|
|
vmmove(cb, &sbName[1], AREAEXPNAME + cbExpName, TRUE);
|
|
cbExpName += cb;
|
|
if (cbExpName > EXPNAMESIZE)
|
|
Fatal(ER_expnameovf, EXPNAMESIZE);
|
|
}
|
|
#else
|
|
/* Add exported name to segmented-executable name tables */
|
|
|
|
if (fResNam || !fNoName)
|
|
{
|
|
if (cb > sizeof(sbName) - sizeof(BYTE))
|
|
cb = sizeof(sbName) - sizeof(BYTE);
|
|
memcpy(sbName, GetFarSb(ahte->cch), cb + 1);
|
|
/* Copy the name to local buffer */
|
|
if (fIgnoreCase
|
|
#if NOT OUT_EXP
|
|
|| TargetOs == NE_WINDOWS
|
|
#endif
|
|
)
|
|
SbUcase(sbName); /* Make upper case if ignoring case */
|
|
|
|
AddName(fResNam ? &ResidentName : &NonResidentName,
|
|
sbName, ord);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
#pragma check_stack(on)
|
|
|
|
void NEAR InitEntTab()
|
|
{
|
|
BYTE OrdinalSet[MaxIndex];
|
|
/* Ordinal numbers set */
|
|
#if NOT EXE386
|
|
APROPEXPPTR exp; /* Pointer to export property cell */
|
|
#endif
|
|
WORD i; /* Index */
|
|
|
|
tyCurBnd = 0xFFFF; /* Won't match any legal types */
|
|
ceCurBnd = 0; /* No entries yet */
|
|
offCurBnd = 0; /* First bundle at beginning */
|
|
ordMac = 0; /* Assume no exported entries */
|
|
pOrdinalSet = OrdinalSet; /* Set global pointer */
|
|
memset(OrdinalSet,0,MaxIndex*sizeof(BYTE));
|
|
/* Initialize set to empty */
|
|
EnSyms(SavExp1,ATTREXP); /* Enumerate exports with ordinals */
|
|
FreeRange.ord = 1; /* Initialize free range of ordinals */
|
|
FreeRange.count = 0;
|
|
pStart = pMinOrd;
|
|
EnSyms(SavExp2,ATTREXP); /* Enumerate exports without ordinals */
|
|
if (MaxOrd > ordMac)
|
|
ordMac = MaxOrd;
|
|
pStart = pMinOrd;
|
|
for(i = 1; i <= ordMac && pStart != NULL; ++i)
|
|
{ /* Loop to start Entry Table */
|
|
#if EXE386
|
|
pExport = (APROPEXPPTR ) FetchSym(pStart,FALSE);
|
|
/* Fetch symbol from virtual memory */
|
|
pStart = pExport->ax_NextOrd; /* Go down on list */
|
|
NewEntry(pExport->ax_sa, pExport->ax_ra, pExport->ax_flags,
|
|
hashra(pExport->ax_ra), pExport->ax_ord);
|
|
#else
|
|
if(NotInSet(i)) /* If a hole found */
|
|
{
|
|
if (tyCurBnd != BNDNIL || ceCurBnd == BNDMAX)
|
|
NewBundle(BNDNIL);
|
|
/* Make a new bundle if needed */
|
|
++ceCurBnd; /* Increment counter */
|
|
continue; /* Next iteration */
|
|
}
|
|
exp = (APROPEXPPTR ) FetchSym(pStart,FALSE);
|
|
/* Fetch symbol from virtual memory */
|
|
pStart = exp->ax_NextOrd; /* Go down on list */
|
|
NewEntry(exp->ax_sa,exp->ax_ra,exp->ax_flags,hashra(exp->ax_ra),i);
|
|
#endif
|
|
/* Create Entry Table entry */
|
|
}
|
|
#if EXE386
|
|
SortPtrTable();
|
|
pExport = NULL;
|
|
#endif
|
|
}
|
|
|
|
#pragma check_stack(off)
|
|
|
|
|
|
#if NOT EXE386
|
|
|
|
/****************************************************************
|
|
* *
|
|
* NAME: OutEntTab *
|
|
* *
|
|
* DESCRIPTION: *
|
|
* *
|
|
* This function writes the Entry Table to the executable *
|
|
* file. First it writes an empty bundle to mark the end of *
|
|
* the table. *
|
|
* *
|
|
* ARGUMENTS: *
|
|
* *
|
|
* None *
|
|
* *
|
|
* RETURNS: *
|
|
* *
|
|
* Nothing. *
|
|
* *
|
|
* SIDE EFFECTS: *
|
|
* *
|
|
* A table is written to the file specified by the global *
|
|
* file pointer, bsRunfile. This function calls OutVm(), *
|
|
* which CALLS THE VIRTUAL MEMORY MANAGER. *
|
|
* *
|
|
****************************************************************/
|
|
|
|
void NEAR OutEntTab()
|
|
{
|
|
NewBundle(ET_END); /* Append an empty bundle */
|
|
WriteByteArray(&EntryTable); /* Write the table */
|
|
}
|
|
#endif
|
|
|
|
#endif /* NOT QCLINK */
|
|
|
|
#if NOT EXE386
|
|
|
|
|
|
/****************************************************************
|
|
* *
|
|
* NAME: MatchRlc *
|
|
* *
|
|
* DESCRIPTION: *
|
|
* *
|
|
* This function compares two relocation records and returns *
|
|
* TRUE if they match. Two records are said to match if they *
|
|
* agree on the fixup type and the target specification. The *
|
|
* location being fixed up does not have to match. *
|
|
* *
|
|
* ARGUMENTS: *
|
|
* *
|
|
* struct new_rlc *rlcp0 Ptr to relocation record *
|
|
* struct new_rlc *rlcp1 Ptr to relocation record *
|
|
* *
|
|
* RETURNS: *
|
|
* *
|
|
* FTYPE *
|
|
* *
|
|
* SIDE EFFECTS: *
|
|
* *
|
|
* None. *
|
|
* *
|
|
****************************************************************/
|
|
|
|
LOCAL WORD NEAR MatchRlc(rlcp0,rlcp1)
|
|
RLCPTR rlcp0; /* Ptr to struct new_rlc record */
|
|
RLCPTR rlcp1; /* Ptr to struct new_rlc record */
|
|
{
|
|
|
|
if(NR_STYPE(*rlcp0) != NR_STYPE(*rlcp1) ||
|
|
NR_FLAGS(*rlcp0) != NR_FLAGS(*rlcp1)) return(FALSE);
|
|
/* Check flags and type */
|
|
if((NR_FLAGS(*rlcp0) & NRRTYP) == NRRINT)
|
|
{ /* If internal reference */
|
|
return((NR_SEGNO(*rlcp0) == NR_SEGNO(*rlcp1)) &&
|
|
(NR_ENTRY(*rlcp0) == NR_ENTRY(*rlcp1)));
|
|
/* Check internal references */
|
|
}
|
|
return((NR_MOD(*rlcp0) == NR_MOD(*rlcp1)) &&
|
|
(NR_PROC(*rlcp0) == NR_PROC(*rlcp1)));
|
|
/* Check imports */
|
|
}
|
|
|
|
|
|
|
|
/****************************************************************
|
|
* *
|
|
* NAME: SaveFixup *
|
|
* *
|
|
* DESCRIPTION: *
|
|
* *
|
|
* This function saves a fixup record for emission later. In *
|
|
* addition, if the fixup is not additive, it builds chains. *
|
|
* *
|
|
* ARGUMENTS: *
|
|
* *
|
|
* SATYPE saLoc Segment of location to fix *
|
|
* relocation *rlcp Ptr to relocation record *
|
|
* *
|
|
* RETURNS: *
|
|
* *
|
|
* RATYPE *
|
|
* Returns the previous head of the fixup chain so that it *
|
|
* can be stuffed into the location being fixed up. If the *
|
|
* fixup is additive, however, it always returns EOC. *
|
|
* *
|
|
****************************************************************/
|
|
|
|
RATYPE NEAR SaveFixup(SATYPE saLoc, RLCPTR rlcp)
|
|
{
|
|
WORD hi; // Hash index
|
|
RLCHASH FAR *pHt; // Hash table
|
|
RLCBUCKET FAR *pBucket; // Relocation bucket
|
|
WORD fi; // fixup bucket index
|
|
RLCPTR pRlc; // Pointer to relocation record
|
|
WORD tmp;
|
|
RATYPE ra;
|
|
void FAR *pTmp;
|
|
|
|
#ifndef UNPOOLED_RELOCS
|
|
if (pPoolRlc == NULL)
|
|
pPoolRlc = PInit();
|
|
#endif
|
|
|
|
if (mpsaRlc[saLoc] == NULL)
|
|
{
|
|
// Allocate hash vector for physical segment saLoc
|
|
|
|
#ifndef UNPOOLED_RELOCS
|
|
mpsaRlc[saLoc] = (RLCHASH FAR *) PAlloc(pPoolRlc, sizeof(RLCHASH));
|
|
#else
|
|
mpsaRlc[saLoc] = (RLCHASH FAR *) GetMem(sizeof(RLCHASH));
|
|
#endif
|
|
}
|
|
pHt = mpsaRlc[saLoc];
|
|
tmp = hashrlc(rlcp);
|
|
hi = (WORD) tmp;
|
|
pBucket = pHt->hash[hi];
|
|
|
|
#if FALSE
|
|
if (saLoc == 2 && hi == 8)
|
|
{
|
|
fprintf(stdout, "Storing fixup for segment: %d\r\n", saLoc);
|
|
fprintf(stdout, " Source offset: %x; type: %x\r\n", NR_SOFF(*rlcp), NR_STYPE(*rlcp));
|
|
fprintf(stdout, " Hash index: %d\r\n", hi);
|
|
}
|
|
#endif
|
|
if (pBucket && !(NR_FLAGS(*rlcp) & NRADD))
|
|
{
|
|
// For non-additive fixups search the bucket for
|
|
// matching relocation records
|
|
|
|
for(fi = 0; fi < pBucket->count; fi++)
|
|
{
|
|
pRlc = &(pBucket->rgRlc[fi]);
|
|
if (MatchRlc(pRlc, rlcp))
|
|
{
|
|
// Relocation records match - chain them
|
|
|
|
ra = (WORD) NR_SOFF(*pRlc);
|
|
// Save previous head of chain
|
|
NR_SOFF(*pRlc) = NR_SOFF(*rlcp);
|
|
// Insert new head of chain
|
|
#if FALSE
|
|
if (saLoc == 2 && hi == 8)
|
|
fprintf(stdout, " Match found with fixup @%x\r\n", ra);
|
|
#endif
|
|
return(ra); // Return previous head of chain
|
|
}
|
|
}
|
|
}
|
|
|
|
// At this point, we know we have to add a new entry
|
|
// to the bucket we are examining.
|
|
|
|
pHt->count++; // Increment count of fixups per segment
|
|
|
|
#if FALSE
|
|
if (saLoc == 2 && hi == 8)
|
|
fprintf(stdout, " New entry; Count: %d\r\n", pHt->count);
|
|
#endif
|
|
// Check space in the bucket
|
|
|
|
if (pBucket == NULL)
|
|
{
|
|
// Allocate new fixup bucket
|
|
|
|
#ifndef UNPOOLED_RELOCS
|
|
pBucket = (RLCBUCKET FAR *) PAlloc(pPoolRlc, sizeof(RLCBUCKET));
|
|
pBucket->rgRlc = (RLCPTR) PAlloc(pPoolRlc, BUCKET_DEF * sizeof(RELOCATION));
|
|
#else
|
|
pBucket = (RLCBUCKET FAR *) GetMem(sizeof(RLCBUCKET));
|
|
pBucket->rgRlc = (RLCPTR) GetMem(BUCKET_DEF * sizeof(RELOCATION));
|
|
#endif
|
|
pBucket->countMax = BUCKET_DEF;
|
|
pHt->hash[hi] = pBucket;
|
|
}
|
|
else if (pBucket->count >= pBucket->countMax)
|
|
{
|
|
// Realloc fixup bucket
|
|
|
|
#ifndef UNPOOLED_RELOCS
|
|
// REVIEW: for now we just throw away the old memory, we'll free
|
|
// REVIEW: it later, we do this infrequently anyways...
|
|
|
|
pTmp = PAlloc(pPoolRlc, (pBucket->countMax << 1) * sizeof(RELOCATION));
|
|
FMEMCPY(pTmp, pBucket->rgRlc, pBucket->countMax * sizeof(RELOCATION));
|
|
// FFREE(pBucket->rgRlc); NOT MUCH MEMORY WASTED HERE
|
|
#else
|
|
pTmp = GetMem((pBucket->countMax << 1) * sizeof(RELOCATION));
|
|
FMEMCPY(pTmp, pBucket->rgRlc, pBucket->countMax * sizeof(RELOCATION));
|
|
FFREE(pBucket->rgRlc);
|
|
#endif
|
|
pBucket->rgRlc = pTmp;
|
|
pBucket->countMax <<= 1;
|
|
}
|
|
|
|
// Add new relocation record at the end of bucket
|
|
|
|
NR_RES(*rlcp) = '\0'; // Zero the reserved field
|
|
pBucket->rgRlc[pBucket->count] = *rlcp;
|
|
++pBucket->count; // Increment count of fixups
|
|
return(EOC); // Return end-of-chain marker
|
|
}
|
|
|
|
/****************************************************************
|
|
* *
|
|
* NAME: OutFixTab *
|
|
* *
|
|
* DESCRIPTION: *
|
|
* *
|
|
* This fuction writes the load-time relocation (fixup) table *
|
|
* for a given file segment to the execuatble file. *
|
|
* *
|
|
* ARGUMENTS: *
|
|
* *
|
|
* SATYPE sa File segment number *
|
|
* *
|
|
* RETURNS: *
|
|
* *
|
|
* Nothing. *
|
|
* *
|
|
* SIDE EFFECTS: *
|
|
* *
|
|
* A table is written to the file specified by the global *
|
|
* file pointer, bsRunfile. *
|
|
* *
|
|
****************************************************************/
|
|
|
|
void NEAR OutFixTab(SATYPE sa)
|
|
{
|
|
WORD hi; // Hash table index
|
|
RLCHASH FAR *pHt;
|
|
RLCBUCKET FAR *pBucket;
|
|
|
|
|
|
|
|
pHt = mpsaRlc[sa];
|
|
WriteExe(&(pHt->count), CBWORD); // Write the number of relocations
|
|
for (hi = 0; hi < HASH_SIZE; hi++)
|
|
{
|
|
pBucket = pHt->hash[hi];
|
|
if (pBucket != NULL)
|
|
{
|
|
WriteExe(pBucket->rgRlc, pBucket->count * sizeof(RELOCATION));
|
|
#ifdef UNPOOLED_RELOCS
|
|
FFREE(pBucket->rgRlc);
|
|
#endif
|
|
}
|
|
}
|
|
#ifdef UNPOOLED_RELOCS
|
|
FFREE(pHt);
|
|
#endif
|
|
}
|
|
|
|
/****************************************************************
|
|
* *
|
|
* NAME: ReleaseRlcMemory *
|
|
* *
|
|
* DESCRIPTION: *
|
|
* *
|
|
* This function releases the pool(s) of memory that held the *
|
|
* segment relocations *
|
|
* *
|
|
* RETURNS: *
|
|
* *
|
|
* Nothing. *
|
|
* *
|
|
* SIDE EFFECTS: *
|
|
* *
|
|
* pPoolRlc is set to NULL so that we will fail if we should *
|
|
* ever try to allocate more relocations after this point *
|
|
* *
|
|
****************************************************************/
|
|
|
|
void NEAR ReleaseRlcMemory()
|
|
{
|
|
#ifndef UNPOOLED_RELOCS
|
|
// free all the memory associated with the saved relocation
|
|
if (pPoolRlc) {
|
|
PFree(pPoolRlc);
|
|
pPoolRlc = NULL;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
#endif /* NOT EXE386 */
|