/* * 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 /* Types and constants */ #include /* Types and constants */ #include /* Relocation definitions */ #include /* Linker I/O definitions */ #include /* New .EXE header definitions */ #include /* Error messages */ #include /* External declarations */ #include #if NOT (WIN_NT OR DOSEXTENDER OR DOSX32) AND NOT WIN_3 #define INCL_BASE #include #include #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 */