|
|
/*
* 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 */
|