windows-nt/Source/XPSP1/NT/sdktools/link16/newout5.c
2020-09-26 16:20:57 +08:00

757 lines
28 KiB
C

/*
* Copyright Microsoft Corporation, 1983-1989
*
* This Module contains Proprietary Information of Microsoft
* Corporation and should be treated as Confidential.
*/
/*
* Segmented-Executable Format Output Module
*
* Modifications:
*
* 23-Feb-1989 RB Fix stack allocation when DGROUP is only stack.
*/
#include <minlit.h> /* Types and constants */
#include <bndtrn.h> /* Types and constants */
#include <bndrel.h> /* Relocation definitions */
#include <lnkio.h> /* Linker I/O definitions */
#include <newexe.h> /* New .EXE header definitions */
#include <lnkmsg.h> /* Error messages */
#include <extern.h> /* External declarations */
#include <impexp.h>
#if NOT (WIN_NT OR DOSEXTENDER OR DOSX32) AND NOT WIN_3
#define INCL_BASE
#include <os2.h>
#include <basemid.h>
#if defined(M_I86LM)
#undef NEAR
#define NEAR
#endif
#endif
#define CBSTUBSTK 0x80 /* # bytes in stack of default stub */
extern unsigned char LINKREV; /* Release number */
extern unsigned char LINKVER; /* Version number */
LOCAL long cbOldExe; /* Size of image of old .EXE file */
/*
* LOCAL FUNCTION PROTOTYPES
*/
LOCAL void NEAR CopyBytes(long cb);
LOCAL void NEAR OutSegTable(unsigned short *mpsasec);
LOCAL long NEAR PutName(long lfaimage, struct exe_hdr *hdr);
#define NAMESIZE 9 /* size of signature array */
void NEAR PadToPage(align)
WORD align; /* Alignment factor */
{
REGISTER WORD cb; /* Number of bytes to write */
align = 1 << align; /* Calculate page size */
cb = align - ((WORD) ftell(bsRunfile) & (align - 1));
/* Calculate needed padding */
if (cb != align) /* If padding needed */
WriteZeros(cb);
}
long NEAR MakeHole(cb)
long cb;
{
long lfaStart; /* Starting file address */
lfaStart = ftell(bsRunfile); /* Save starting address */
#if OSXENIX
fseek(bsRunfile,cb,1); /* Leave a hole */
#else
WriteZeros(cb);
#endif
return(lfaStart); /* Return starting file address */
}
LOCAL void NEAR CopyBytes(cb) /* Copy from bsInput to bsRunfile */
long cb; /* Number of bytes to copy */
{
BYTE buffer[PAGLEN]; /* Buffer */
#if FALSE
raChksum = (WORD) ftell(bsRunfile); /* Determine checksum relative offset */
#endif
while(cb >= (long) PAGLEN) /* While full buffers remain */
{
fread(buffer,sizeof(BYTE),PAGLEN,bsInput);
/* Read */
WriteExe(buffer, PAGLEN); /* Write */
cb -= (long) PAGLEN; /* Decrement count of bytes */
}
if(cb != 0L) /* If bytes remain */
{
fread(buffer,sizeof(BYTE),(WORD) cb,bsInput);
/* Read */
WriteExe(buffer, (WORD) cb); /* Write */
}
}
#pragma check_stack(on)
BSTYPE LinkOpenExe(sbExe)
BYTE *sbExe; /* .EXE file name */
{
SBTYPE sbPath; /* Path */
SBTYPE sbFile; /* File name */
SBTYPE sbDefault; /* Default file name */
char FAR *lpch; /* Pointer to buffer */
REGISTER BYTE *sb; /* Pointer to string */
BSTYPE bsFile; /* File handle */
#if OSMSDOS
#if WIN_NT
memcpy(sbFile, sbExe, sbExe[0] + 1);
sbFile[sbFile[0]+1] = '\0';
#else
memcpy(sbFile,"\006A:.EXE",7); /* Initialize file name */
sbFile[1] += DskCur; /* Use current drive */
UpdateFileParts(sbFile,sbExe); /* Use parts of name given */
#endif
#endif
memcpy(sbDefault,sbFile,sbFile[0]+2);
/* Set default file name */
if((bsFile = fopen(&sbFile[1],RDBIN)) != NULL) return(bsFile);
/* If file found, return handle */
#if OSMSDOS
if (lpszPath != NULL) /* If variable set */
{
lpch = lpszPath;
sb = sbPath; /* Initialize */
do /* Loop through environment value */
{
if(*lpch == ';' || *lpch == '\0')
{ /* If end of path specification */
if(sb > sbPath) /* If specification not empty */
{
if (!fPathChr(*sb) && *sb != ':') *++sb = CHPATH;
/* Add path char if none */
sbPath[0] = (BYTE)(sb - sbPath);
/* Set length of path string */
UpdateFileParts(sbFile,sbPath);
/* Use the new path spec */
if((bsFile = fopen(&sbFile[1],RDBIN)) != NULL)
return(bsFile); /* If file found, return handle */
sb = sbPath; /* Reset pointer */
memcpy(sbFile,sbDefault,sbDefault[0]+2);
/* Initialize file name */
}
}
else *++sb = *lpch; /* Else copy character to path */
}
while(*lpch++ != '\0'); /* Loop until end of string */
}
#endif
return(NULL); /* File not found */
}
/*
* Default realmode DOS program stub.
*/
LOCAL BYTE DefStub[] =
{
0x0e, 0x1f, 0xba, 0x0e, 0x00, 0xb4, 0x09, 0xcd, 0x21, 0xb8,
0x01, 0x4c, 0xcd, 0x21
};
void NEAR EmitStub(void) /* Emit old .EXE header */
{
struct exe_hdr exe; /* Stub .EXE header */
AHTEPTR ahteStub; /* File hash table entry */
long lfaimage; /* File offset of old .EXE image */
#if MSGMOD
unsigned MsgLen;
SBTYPE Msg; /* Message text */
unsigned MsgStatus;
char *pMsg; /* Pointer to message text */
#endif
SBTYPE StubFileName;
#if OSMSDOS
char buf[512]; /* File buffer */
#endif
/*
* Emit stub .EXE header
*/
if (rhteStub != RHTENIL
#if ILINK
|| fQCIncremental
#endif
)
{
/* If a stub has been supplied or QC incremental link */
#if ILINK
if (fQCIncremental)
{
strcpy(StubFileName, "\014ilinkstb.ovl");
}
else
{
#endif
ahteStub = (AHTEPTR ) FetchSym(rhteStub,FALSE);
/* Get the stub file name */
strcpy(StubFileName, GetFarSb(ahteStub->cch));
#if ILINK
}
#endif
StubFileName[StubFileName[0] + 1] = '\0';
if((bsInput = LinkOpenExe(StubFileName)) == NULL)
/* If file not found, quit */
Fatal(ER_nostub, &StubFileName[1]);
#if OSMSDOS
setvbuf(bsInput,buf,_IOFBF,sizeof(buf));
#endif
xread(&exe,CBEXEHDR,1,bsInput); /* Read the header */
if(E_MAGIC(exe) != EMAGIC) /* If stub is not an .EXE file */
{
fclose(bsInput); /* Close stub file */
Fatal(ER_badstub);
/* Print error message and die */
}
fseek(bsInput,(long) E_LFARLC(exe),0);
/* Seek to relocation table */
E_LFARLC(exe) = sizeof(struct exe_hdr);
/* Change to new .EXE value */
lfaimage = (long) E_CPARHDR(exe) << 4;
/* Save offset of image */
cbOldExe = ((long) E_CP(exe) << LG2PAG) - lfaimage;
/* Calculate in-file image size */
if(E_CBLP(exe) != 0) cbOldExe -= (long) PAGLEN - E_CBLP(exe);
/* Diddle size for last page */
E_CPARHDR(exe) = (WORD)((((long) E_CRLC(exe)*CBRLC +
sizeof(struct exe_hdr) + PAGLEN - 1) >> LG2PAG) << 5);
/* Calculate header size in para.s */
E_RES(exe) = 0; /* Clear reserved word */
E_CBLP(exe) = E_CP(exe) = E_MINALLOC(exe) = 0;
E_LFANEW(exe) = 0L; /* Clear words which will be patched */
raChksum = 0; /* Set checksum offset */
WriteExe(&exe, CBEXEHDR); /* Write now, patch later */
CopyBytes((long) E_CRLC(exe)*CBRLC);
/* Copy relocations */
PadToPage(LG2PAG); /* Pad to page boundary */
fseek(bsInput,lfaimage,0); /* Seek to start of image */
#if ILINK
if (fQCIncremental)
cbOldExe -= PutName(lfaimage, &exe);
/* Imbed .EXE file name into QC stubloader */
#endif
CopyBytes(cbOldExe); /* Copy the image */
fclose(bsInput); /* Close input file */
#if ILINK
if (!fQCIncremental)
#endif
PadToPage(LG2PAG); /* Pad to page boundary */
cbOldExe += ((long) E_MINALLOC(exe) << 4) +
((long) E_CPARHDR(exe) << 4); /* Add unitialized space and header */
return; /* And return */
}
memset(&exe,0,sizeof(struct exe_hdr));/* Initialize to zeroes */
#if CPU286
if(TargetOs==NE_WINDOWS) /* Provide standard windows stub */
{
pMsg = GetMsg(P_stubmsgwin);
MsgLen = strlen(pMsg);
strcpy(Msg, pMsg);
}
else
{
MsgStatus = DosGetMessage((char far * far *) 0, 0,
(char far *) Msg, SBLEN,
MSG_PROT_MODE_ONLY,
(char far *) "OSO001.MSG",
(unsigned far *) &MsgLen);
if (MsgStatus == 0)
{
/* Message retrieved from system file */
Msg[MsgLen-1] = 0xd; /* Append CR */
Msg[MsgLen] = 0xa; /* Append LF */
Msg[MsgLen+1] = '$';
MsgLen += 2;
}
else
{
/* System message file is not present - use standard message */
#endif
#if MSGMOD
if(TargetOs==NE_WINDOWS) /* Provide standard windows stub */
{
pMsg = GetMsg(P_stubmsgwin);
}
else
{
pMsg = GetMsg(P_stubmsg);
}
MsgLen = strlen(pMsg);
strcpy(Msg, pMsg);
#endif
#if CPU286
}
}
#endif
E_MAGIC(exe) = EMAGIC; /* Set magic number */
E_MAXALLOC(exe) = 0xffff; /* Default is all available mem */
/* SS will be same as CS, SP will be end of image + stack */
#if MSGMOD
cbOldExe = sizeof(DefStub) + MsgLen + CBSTUBSTK + ENEWEXE;
#else
cbOldExe = sizeof(DefStub) + strlen(P_stubmsg) + CBSTUBSTK + ENEWEXE;
#endif
E_SP(exe) = (WORD) ((cbOldExe - ENEWEXE) & ~1);
E_LFARLC(exe) = ENEWEXE;
E_CPARHDR(exe) = ENEWEXE >> 4;
raChksum = 0; /* Set checksum offset */
WriteExe(&exe, CBEXEHDR); /* Write the stub header */
WriteExe(DefStub, sizeof(DefStub));
#if MSGMOD
WriteExe(Msg, MsgLen);
#else
WriteExe(P_stubmsg, strlen(P_stubmsg));
#endif
PadToPage(4); /* Pad to paragraph boundary */
}
#pragma check_stack(off)
void NEAR PatchStub(lfahdr, lfaseg)
long lfahdr; /* File address of new header */
long lfaseg; /* File address of first segment */
{
long cbTotal; /* Total file size */
WORD cpTotal; /* Pages total */
WORD cbLast; /* Bytes on last page */
WORD cparMin; /* Extra paragraphs needed */
if (TargetOs == NE_WINDOWS
#if ILINK
|| fQCIncremental
#endif
)
cbTotal = lfaseg; /* QC incremental linking or Windows app */
else
cbTotal = ftell(bsRunfile); /* Get the size of the file */
cpTotal = (WORD)((cbTotal + PAGLEN - 1) >> LG2PAG);
/* Get the total number of pages */
cbLast = (WORD) (cbTotal & (PAGLEN - 1));
/* Get no. of bytes on last page */
cbTotal = (cbTotal + 0x000F) & ~(1L << LG2PAG);
/* Round to paragraph boundary */
cbOldExe = (cbOldExe + 0x000F) & ~(1L << LG2PAG);
/* Round to paragraph boundary */
cbOldExe -= cbTotal; /* Subtract new size from old */
fseek(bsRunfile,(long) ECBLP,0); /* Seek into header */
raChksum = ECBLP; /* Set checksum offset */
WriteExe(&cbLast, CBWORD); /* Write no. of bytes on last page */
WriteExe(&cpTotal, CBWORD); /* Write number of pages */
fseek(bsRunfile,(long) EMINALLOC,0);/* Seek into header */
cparMin = (cbOldExe < 0L)? 0: (WORD)(cbOldExe >> 4);
/* Min. no. of extra paragraphs */
raChksum = EMINALLOC; /* Set checksum offset */
WriteExe(&cparMin, CBWORD); /* Write no. of extra para.s needed */
fseek(bsRunfile,(long) ENEWHDR,0); /* Seek into header */
raChksum = ENEWHDR; /* Set checksum offset */
WriteExe(&lfahdr, CBLONG); /* Write offset to new header */
}
#if NOT EXE386
/****************************************************************
* *
* OutSas: *
* *
* This function moves a segment from virtual memory to the *
* run file. *
* *
****************************************************************/
LOCAL void NEAR OutSas(WORD *mpsasec)
{
SATYPE sa; /* File segment number */
DWORD lfaseg; /* File segment offset */
if (saMac == 1)
{
OutWarn(ER_nosegdef); /* No code or initialized data in .EXE */
return;
}
for(sa = 1; sa < saMac; ++sa) /* Loop through file segments */
{
if (mpsaRlc[sa] && mpsacbinit[sa] == 0L)
mpsacbinit[sa] = 1L; /* If relocs, must be bytes in file */
if (mpsacbinit[sa] != 0L) /* If bytes to write in file */
{
PadToPage(fileAlign); /* Pad to page boundary */
lfaseg = (ftell(bsRunfile) >> fileAlign);
WriteExe(mpsaMem[sa], mpsacbinit[sa]);
/* Output the segment */
FFREE(mpsaMem[sa]); // Free segment's memory
}
else
lfaseg = 0L; /* Else no bytes in file */
if (mpsaRlc[sa])
OutFixTab(sa); /* Output fixups if any */
if (lfaseg > 0xffffL)
Fatal(ER_filesec);
else
mpsasec[sa] = (WORD)lfaseg;
}
ReleaseRlcMemory();
}
#pragma check_stack(on)
/*** PutName - put .EXE file name into QC stubloader
*
* Purpose:
* PutName will imbed the outfile name (.EXE) into the stubloader
* so that programs can load in DOS 2.x
*
* Input:
* hdr - pointer to stub loader .EXE header
* lfaimage - start of the stub loader code in file
* Output:
* Number of bytes copied to .EXE file
*
*************************************************************************/
LOCAL long NEAR PutName(long lfaimage, struct exe_hdr *hdr)
{
long offset_to_filename;
char newname[NAMESIZE];
char oldname[NAMESIZE];
SBTYPE sbRun; /* Executable file name */
AHTEPTR hte; /* Hash table entry address */
long BytesCopied;
WORD i, oldlen;
/* Calculate the offset to the filename data patch */
offset_to_filename = (E_CPARHDR(*hdr) << 4) + /* paragraphs in header */
(E_CS(*hdr) << 4) + /* start of cs adjusted */
E_IP(*hdr) - /* offset into cs of ip */
NAMESIZE; /* back up to filename */
/* Copy begin of stubloader */
BytesCopied = offset_to_filename - lfaimage - 2;
CopyBytes(BytesCopied);
/* Read in the lenght and file name template and validate it */
xread(&oldlen, sizeof(unsigned int), 1, bsInput);
xread(oldname, sizeof(char), NAMESIZE, bsInput);
/* Does the name read match the signature */
if (!(strcmp(oldname, "filename")))
{
hte = (AHTEPTR ) FetchSym(rhteRunfile,FALSE);
/* Get run file name */
memcpy(sbRun, &GetFarSb(hte->cch)[1], B2W(hte->cch[0]));
/* Get name from hash table */
sbRun[B2W(hte->cch[0])] = '\0'; /* Null-terminate name */
memset(newname, 0, NAMESIZE); /* Initialize to zeroes */
/* Copy only the proper number of characters */
for (i = 0; (i < NAMESIZE-1 && sbRun[i] && sbRun[i] != '.'); i++)
newname[i] = sbRun[i];
/* Write the length of name */
WriteExe(&i, sizeof(WORD));
/* Write the new name over the signature */
WriteExe(newname, NAMESIZE);
return(BytesCopied + NAMESIZE + 2);
}
WriteExe(&oldlen, sizeof(WORD));
return(BytesCopied + 2);
}
#pragma check_stack(off)
/****************************************************************
* *
* OutSegTable: *
* *
* This function outputs the segment table. *
* *
****************************************************************/
LOCAL void NEAR OutSegTable(mpsasec)
WORD *mpsasec; /* File segment to sector address */
{
struct new_seg ste; /* Segment table entry */
SATYPE sa; /* Counter */
for(sa = 1; sa < saMac; ++sa) /* Loop through file segments */
{
NS_SECTOR(ste) = mpsasec[sa]; /* Set the sector number */
NS_CBSEG(ste) = (WORD) mpsacbinit[sa];
/* Save the "in-file" length */
NS_MINALLOC(ste) = (WORD) mpsacb[sa];
/* Save total size */
NS_FLAGS(ste) = mpsaflags[sa]; /* Set the segment attribute flags */
if (mpsaRlc[sa])
NS_FLAGS(ste) |= NSRELOC; /* Set reloc bit if there are relocs */
WriteExe(&ste, CBNEWSEG); /* Write it to the executable file */
}
}
/*
* OutSegExe:
*
* Outputs a segmented-executable format file. This format is used
* by DOS 4.0 and later, and Windows.
* Called by OutRunfile.
*/
void NEAR OutSegExe(void)
{
WORD sasec[SAMAX]; /* File segment to sector table */
struct new_exe hdr; /* Executable header */
SEGTYPE segStack; /* Stack segment */
WORD i; /* Counter */
long lfahdr; /* File address of new header */
long lfaseg; /* File address of first segment */
if (fStub
#if ILINK
|| fQCIncremental
#endif
)
EmitStub();
/* Emit stub old .EXE header */
/*
* Emit the new portion of the .EXE
*/
memset(&hdr,0,sizeof(struct new_exe));/* Set to zeroes */
NE_MAGIC(hdr) = NEMAGIC; /* Set the magic number */
NE_VER(hdr) = LINKVER; /* Set linker version number */
NE_REV(hdr) = LINKREV; /* Set linker revision number */
NE_CMOVENT(hdr) = cMovableEntries; /* Set count of movable entries */
NE_ALIGN(hdr) = fileAlign; /* Set segment alignment field */
NE_CRC(hdr) = 0; /* Assume CRC = 0 when calculating */
if (((TargetOs == NE_OS2) || (TargetOs == NE_WINDOWS)) &&
#if O68K
iMacType == MAC_NONE &&
#endif
!(vFlags & NENOTP) && !(vFlags & NEAPPTYP))
{
if (TargetOs == NE_OS2)
vFlags |= NEWINCOMPAT;
else
vFlags |= NEWINAPI;
}
if (gsnAppLoader)
vFlags |= NEAPPLOADER;
NE_FLAGS(hdr) = vFlags; /* Set header flags */
NE_EXETYP(hdr) = TargetOs; /* Set target operating system */
if (TargetOs == NE_WINDOWS)
NE_EXPVER(hdr) = (((WORD) ExeMajorVer) << 8) | ExeMinorVer;
NE_FLAGSOTHERS(hdr) = vFlagsOthers; /* Set other module flags */
/*
* If SINGLE or MULTIPLE DATA, then set the automatic data segment
* from DGROUP. If DGROUP has not been declared and we're not a
* dynlink library then issue a warning.
*/
if((NE_FLAGS(hdr) & NEINST) || (NE_FLAGS(hdr) & NESOLO))
{
if(mpggrgsn[ggrDGroup] == SNNIL)
{
if(!(vFlags & NENOTP))
OutWarn(ER_noautod);
NE_AUTODATA(hdr) = SANIL;
}
else NE_AUTODATA(hdr) = mpsegsa[mpgsnseg[mpggrgsn[ggrDGroup]]];
}
else NE_AUTODATA(hdr) = SANIL; /* Else no auto data segment */
if (fHeapMax)
{
if (NE_AUTODATA(hdr) != SANIL)
NE_HEAP(hdr) = (WORD) (LXIVK - mpsacb[NE_AUTODATA(hdr)]-16);
else /* Heap size = 64k - size of DGROUP - 16 */
NE_HEAP(hdr) = 0xffff-16;
}
else
NE_HEAP(hdr) = cbHeap; /* Set heap allocation */
NE_STACK(hdr) = 0; /* Assume no stack in DGROUP */
if (vFlags & NENOTP)
NE_SSSP(hdr) = 0L; /* Libraries have no stack at all */
else if (gsnStack != SNNIL)
{
/* If there is a stack segment definition */
segStack = mpgsnseg[gsnStack]; /* Get stack segment number */
/*
* If stack segment is in DGROUP, adjust size of DGROUP down and
* move stack allocation to ne_stack field, so it can be modified
* after linking. Only do this if DGROUP has more than just the
* stack segment.
*/
if (fSegOrder &&
NE_AUTODATA(hdr) == mpsegsa[segStack] &&
mpsacb[NE_AUTODATA(hdr)] > cbStack)
{
mpsacb[NE_AUTODATA(hdr)] -= cbStack;
NE_STACK(hdr) = (WORD) cbStack;
NE_SSSP(hdr) = (long) (NE_AUTODATA(hdr)) << WORDLN;
/* SS:SP = DS:0 */
if (fHeapMax)
{
/* If max heap - adjust heap size */
if (NE_HEAP(hdr) >= (WORD) cbStack)
NE_HEAP(hdr) -= cbStack;
}
}
else
NE_SSSP(hdr) = cbStack + mpsegraFirst[segStack] +
((long) mpsegsa[segStack] << WORDLN);
/* Set initial SS:SP */
}
else /* Else assume stack is in DGROUP */
{
NE_SSSP(hdr) = (long) NE_AUTODATA(hdr) << WORDLN;
/* SS:SP = DS:0 */
NE_STACK(hdr) = (WORD) cbStack; /* Set stack allocation */
if (fHeapMax)
{
/* If max heap - adjust heap size */
if (NE_HEAP(hdr) >= (WORD) cbStack)
NE_HEAP(hdr) -= cbStack;
}
}
/* Check that auto data + heapsize <= 64K */
if(NE_AUTODATA(hdr) != SNNIL)
if(mpsacb[NE_AUTODATA(hdr)] +
(long) NE_HEAP(hdr) +
(long) NE_STACK(hdr) > LXIVK)
OutError(ER_datamax);
if (!(vFlags & NENOTP) && (segStart == 0))
Fatal(ER_nostartaddr);
NE_CSIP(hdr) = raStart + ((long) mpsegsa[segStart] << WORDLN);
/* Set starting point */
NE_CSEG(hdr) = saMac - 1; /* Number of file segments */
NE_CMOD(hdr) = ModuleRefTable.wordMac;
/* Number of modules imported */
lfahdr = MakeHole((long) sizeof(struct new_exe));
/* Leave space for header */
i = NE_CSEG(hdr)*sizeof(struct new_seg);
/* Calc. size of Segment Table */
NE_SEGTAB(hdr) = (WORD)(MakeHole((long) i) - lfahdr);
/* Leave hole for segment table */
NE_RSRCTAB(hdr) = NE_SEGTAB(hdr) + i;
/* Offset of Resource Table */
NE_RESTAB(hdr) = NE_RSRCTAB(hdr); /* Offset of Resident Name Table */
NE_MODTAB(hdr) = NE_RESTAB(hdr);
#if OUT_EXP
/* Convert Res and Non Resident Name Tables to uppercase
and write export file */
ProcesNTables(bufExportsFileName);
#endif
if (ResidentName.byteMac)
{
ByteArrayPut(&ResidentName, sizeof(BYTE), "\0");
WriteByteArray(&ResidentName); /* If we have Resident Name Table */
/* Output table with null at end */
NE_MODTAB(hdr) += ResidentName.byteMac;
FreeByteArray(&ResidentName);
}
/* Calc. offset of Module Ref Table */
WriteWordArray(&ModuleRefTable);
/* Output the Module Reference Table */
NE_IMPTAB(hdr) = NE_MODTAB(hdr) + ModuleRefTable.wordMac * sizeof(WORD);
FreeWordArray(&ModuleRefTable);
/* Calc offset of Imported Names Tab */
NE_ENTTAB(hdr) = NE_IMPTAB(hdr); /* Minimum offset of Entry Table */
if (ImportedName.byteMac > 1) /* If Imported Names Table not empty */
{
WriteByteArray(&ImportedName); /* Output the Imported Names Table */
NE_ENTTAB(hdr) += ImportedName.byteMac;
FreeByteArray(&ImportedName);
/* Add in length of table */
}
#if NOT QCLINK
#if ILINK
if (!fQCIncremental)
#endif
OutEntTab(); /* Output the Entry Table */
#endif
NE_CBENTTAB(hdr) = EntryTable.byteMac;
/* Set size of Entry Table */
FreeByteArray(&EntryTable);
NE_NRESTAB(hdr) = ftell(bsRunfile);
ByteArrayPut(&NonResidentName, sizeof(BYTE), "\0");
WriteByteArray(&NonResidentName); /* Output table with null at end */
NE_CBNRESTAB(hdr) = NonResidentName.byteMac;
FreeByteArray(&NonResidentName);
/* Size of non-resident name table */
lfaseg = ftell(bsRunfile); /* Remember where segment data starts in file */
OutSas(sasec); /* Output the file segments */
PatchStub(lfahdr, lfaseg); /* Patch stub header */
if(cErrors || fUndefinedExterns) NE_FLAGS(hdr) |= NEIERR;
/* If errors, set error bit */
fseek(bsRunfile,lfahdr,0); /* Seek to beginning of header */
raChksum = (WORD) lfahdr; /* Set checksum offset */
WriteExe(&hdr, CBNEWEXE); /* Write the header */
OutSegTable(sasec); /* Write the segment table */
fseek(bsRunfile,lfahdr+NECRC,0); /* Seek into new header */
NE_CRC(hdr) = chksum32; /* Must copy, else chksum32 trashed */
WriteExe((BYTE FAR *) &NE_CRC(hdr), CBLONG);
/* Write the checksum */
fseek(bsRunfile, 0L, 2); /* Go to end of file */
if (fExeStrSeen)
WriteExe(ExeStrBuf, ExeStrLen);
#if SYMDEB
if (fSymdeb)
{
#if ILINK
if (fIncremental)
PadToPage(fileAlign); /* Pad to page boundary for ILINK */
#endif
OutDebSections(); /* Generate ISLAND sections */
}
#endif
}
#endif /* NOT EXE386 */