/* * Copyright Microsoft Corporation 1986,1987 * * This Module contains Proprietary Information of Microsoft * Corporation and should be treated as Confidential. */ /* * NEWOUT3.C * * Functions to output DOS3 exe. */ #include /* Types and constants */ #include /* Types and constants */ #include /* Types and constants */ #include /* Linker I/O definitions */ #include /* Error messages */ #include /* External declarations */ #include #include #include #define E_VERNO(x) (x).e_sym_tab #define IBWCHKSUM 18L #define IBWCSIP 20L #define CBRUN sizeof(struct exe_hdr) #define CBRUN_OLD 0x1e /* Size of header for DOS 1, 2 & 3 */ #define EMAGIC 0x5A4D /* Old magic number */ FTYPE parity; /* For DOS3 checksum */ SEGTYPE segAdjCom = SEGNIL; /* Segment moved by 0x100 in .com programs */ /* * LOCAL FUNCTION PROTOTYPES */ #if OVERLAYS LOCAL void NEAR OutRlc(IOVTYPE iov); #endif #if QBLIB LOCAL unsigned short NEAR SkipLead0(unsigned short seg); LOCAL void NEAR FixQStart(long cbFix,struct exe_hdr *prun); #endif /**************************************************************** * * * OutRlc: * * * * This function writes the reloc table to the run file. * * NOTE: relocation table entries must be a factor of the * * virtual memory page length. * * * ****************************************************************/ #if OVERLAYS LOCAL void NEAR OutRlc(IOVTYPE iov) { RUNRLC FAR *pRlc; pRlc = &mpiovRlc[iov]; WriteExe(pRlc->rgRlc, CBRLC*pRlc->count); } #endif void OutHeader (prun) struct exe_hdr *prun; { WriteExe(prun, E_LFARLC(*prun)); } #if INMEM #if CPU8086 OR CPU286 #include /* * WriteExe : write() with a far buffer * * Emulate write() except use a far buffer. Call the system * directly. * * Returns: * 0 if error, else number of bytes written. */ LOCAL int WriteExe (fh, buf, n) int fh; /* File handle */ char FAR *buf; /* Buffer to store bytes in */ int n; /* # bytes to write */ { #if OSMSDOS #if CPU8086 union REGS regs; /* Non-segment registers */ struct SREGS sregs; /* Segment registers */ regs.x.ax = 0x4000; regs.x.bx = fh; regs.x.cx = n; sregs.ds = FP_SEG(buf); sregs.es = sregs.ds; regs.x.dx = FP_OFF(buf); intdosx(®s,®s,&sregs); if(regs.x.cflag) return(0); return(regs.x.ax); #else ERROR #endif #endif /* OSMSDOS */ #if OSXENIX char mybuf[PAGLEN]; int cppage; char *p; while(n > 0) { cppage = n > PAGLEN ? PAGLEN : n; for(p = mybuf; p < mybuf[cppage]; *p++ = *buf++); if(write(fh,mybuf,cppage) != cppage) return(0); n -= cppage; } #endif } #else #define readfar read #endif extern WORD saExe; LOCAL void OutExeBlock (seg1, segEnd) { long cb; unsigned cbWrite; WORD sa; FTYPE parity; /* 1 odd, 0 even */ fflush(bsRunfile); parity = 0; cb = ((long)(mpsegsa[segEnd] - mpsegsa[seg1]) << 4) + mpsegcb[segEnd] + mpsegraFirst[segEnd]; sa = saExe; while(cb) { if(cb > 0xfff0) cbWrite = 0xfff0; else cbWrite = cb; ChkSum(cbWrite,(BYTE FAR *)((long) sa << 16),parity); parity = parity ^ (cbWrite & 1); if(WriteExe(fileno(bsRunfile),(long)sa << 16,cbWrite) != cbWrite) { ExitCode = 4; Fatal(ER_spcrun); /* Fatal error */ } cb -= cbWrite; sa += 0xfff; } } #endif /* INMEM */ #if QBLIB /* * SkipLead0 : Output a segment, skipping leading zeroes * * Count the number of leading 0s in the segment and write * a word holding the count. Then write the segment starting * with the first nonzero byte. Return number of leading 0s. * * Parameters: * seg Segment number * Returns: * Number of leading 0s */ WORD NEAR SkipLead0 (SEGTYPE seg) { BYTE FAR *pSegImage; // Segment memory image long cZero; // Number of zero bytes at the begin of the segment WORD cbSkip; /* # bytes of leading 0s */ DWORD cbRemain; // no-zero bytes // Initialize starting address pSegImage = mpsegMem[seg] + mpsegraFirst[seg]; // Count zero bytes at segment start for (cZero = 0; cZero < mpsegcb[seg] && *pSegImage == 0; cZero++, pSegImage++) ; // If segment is 64K and entirely 0s, write 0 and 64k of zeros. if (cZero == mpsegcb[seg] && cZero == LXIVK) { cbSkip = 0; pSegImage = mpsegMem[seg] + mpsegraFirst[seg]; cbRemain = LXIVK; } else { cbSkip = (WORD) cZero; cbRemain = mpsegcb[seg] - cZero; } WriteExe((char FAR *)&cbSkip, CBWORD); WriteExe(pSegImage, cbRemain); return(cbSkip); } /* * FixQStart : Fix up (patch) .QLB starting address * * Parameters: * cbFix Number of bytes skipped (may be negative) * prun Pointer to DOS3 exe header * ASSUMES: * File pointer is at CS:IP. */ void NEAR FixQStart (cbFix,prun) long cbFix; struct exe_hdr *prun; { /* * WARNNG: dra must be long since it holds numbers in the range * -4 to 0x10000, inclusive. */ long dra; /* Delta for raStart adjustment */ SATYPE saStart; /* Initial CS */ saStart = prun->e_cs; /* Initialize */ /* * Adjust initial CS:IP for .QLB's since it is used by loader * to point to symbol table, and all addresses are off by the * amount of leading 0s skipped. Luckily CS:IP comes right after * checksum so we don't have to seek. * First, normalize CS:IP downward if underflow will occur. */ if((dra = cbFix - raStart) > 0) { raStart += (dra + 0xf) & ~0xf; saStart -= (SATYPE) ((dra + 0xf) >> 4); } /* Patch the header */ OutWord((WORD) (raStart -= cbFix)); OutWord(saStart); } #endif /*QBLIB*/ /* * OutDos3Exe: * * Output DOS3-format executable file. * Called by OutRunfile. */ void NEAR OutDos3Exe() { SEGTYPE seg; /* Current segment */ struct exe_hdr run; /* Executable header */ WORD cbPadding; /* # bytes of padding */ WORD cb; /* # bytes on last page */ WORD pn; /* # pages */ long lfaPrev; /* Previous file offset */ RATYPE ra; /* Current address offset */ SATYPE sa; /* Current address base */ SEGTYPE segIovFirst; /* First segment in overlay */ SEGTYPE segFinaliov; /* Last seg in overlay to output */ SEGTYPE segIovLast; /* Last segment in overlay */ long cbDirectory; /* # bytes in entire header */ WORD cparDirectory; /* # para. in entire header */ SEGTYPE segStack; /* Segment index of stack segment */ #if OVERLAYS IOVTYPE iov; /* Current overlay number */ #endif #if FEXEPACK FTYPE fSave; /* Scratch var. */ #endif SATYPE saStart; /* Start of current segment */ WORD segcbDelta = 0; /* For /TINY segment size adjustment */ WORD fOrgStriped = FALSE; /* TRUE when 0x100 bytes striped */ WORD tmp; #if OVERLAYS DWORD ovlLfa; /* Seek offset for overlay */ DWORD imageSize; /* Overlay memory image size */ DWORD ovlRootBeg; /* Seek offset to the begin of root memory image */ WORD ovlDataOffset; #endif #if QBLIB /* Count of bytes skipped in the load image must be a long since * it can be negative (if there were less than 4 leading 0s) * or greater than 0x8000. */ long cbSkip = 0; /* # bytes skipped */ extern SEGTYPE segQCode; /* .QLB code segment */ #endif if (fBinary) { #if OVERLAYS if (fOverlays) Fatal(ER_swbadovl, "/TINY"); /* Overlays not allowed in .COM */ #endif if (mpiovRlc[0].count) Fatal(ER_binary); /* Run time relocations not allowed in .COM */ } memset(&run,0,sizeof(run)); /* Clear everything in fixed header */ E_MAGIC(run) = EMAGIC; /* Magic number */ if (vFlagsOthers & NENEWFILES || fDOSExtended) { /* DOS header is 0x40 bytes long */ E_LFARLC(run) = CBRUN; /* Offset of loadtime relocations */ if (vFlagsOthers & NENEWFILES) E_FLAGS(run) |= EKNOWEAS; if (fDOSExtended) E_FLAGS(run) |= EDOSEXTENDED; } else { /* DOS header is 0x1e bytes long */ E_LFARLC(run) = CBRUN_OLD; /* Offset of loadtime relocations */ } E_VERNO(run) = 1; /* DOS ver. for compatibility only */ lfaPrev = 0L; #if OVERLAYS for(iov = 0; iov < (IOVTYPE) iovMac; ++iov) /* Loop thru overlays */ { #endif /* Get size of overlay */ cb = 0; pn = 0; #if OVERLAYS /* Find lowest seg in overlay */ for(seg = 1; seg <= segLast && mpsegiov[seg] != iov; ++seg) #else seg = 1; #endif /* If no overlay to output, we're done with this one. */ if(seg > segLast) #if OVERLAYS continue; #else return; #endif /* Get starting address of lowest segment */ segIovFirst = seg; ra = mpsegraFirst[seg]; sa = mpsegsa[seg]; /* Find the last segment in the overlay */ segIovLast = SEGNIL; for(seg = segLast; seg; --seg) { #if OVERLAYS if(mpsegiov[seg] == iov) { #endif if(segIovLast == SEGNIL) segIovLast = seg; if(!cparMaxAlloc) break; if((mpsegFlags[seg] & FNOTEMPTY) == FNOTEMPTY) break; #if OVERLAYS } #endif } /* If no data in overlay, we're done with it. */ if(!seg) #if OVERLAYS continue; #else return; #endif /* Get size in between 1st, last segs in this overlay */ segFinaliov = seg; sa = mpsegsa[seg] - sa - 1; ra = mpsegraFirst[seg] - ra + 16; /* Normalize */ sa += (SATYPE) (ra >> 4); ra &= 0xF; /* Take into account size of last segment */ if(mpsegcb[seg] + ra < LXIVK) ra += (WORD) mpsegcb[seg]; else { ra -= LXIVK - mpsegcb[seg]; sa += 0x1000; } /* Normalize again */ sa += (SATYPE) (ra >> 4); ra &= 0xF; /* Determine # pages, bytes on last page */ pn = sa >> 5; cb = (WORD) (((sa << 4) + ra) & MASKRB); E_CBLP(run) = cb; if(cb) { cb = 0x200 - cb; ++pn; } /* If empty overlay, skip it */ #if OVERLAYS if(iov && !pn) continue; #else if(!pn) return; #endif vchksum = parity = 0; /* Initialize check sum */ if (segStart == SEGNIL) { if (fBinary) OutWarn(ER_comstart); #if 0 else OutWarn(ER_nostartaddr); #endif } else if (mpsegiov[segStart] != IOVROOT) Fatal(ER_ovlstart); /* Starting address can't be in overlay */ E_CS(run) = mpsegsa[segStart]; /* Base of starting segment */ E_IP(run) = (WORD) raStart; /* Offset of starting procedure */ #if QBLIB /* * For .QLB, set minalloc field to an impossible amount to force * DOS3 loader to abort. */ if(fQlib) E_MINALLOC(run) = 0xffff; else #endif /* If no uninitialized segments, minalloc = 0 */ if (segFinaliov == segIovLast) E_MINALLOC(run) = 0; else { /* Otherwise determine the minalloc value: */ /* sa:ra is end of overlay being output. Find empty area size */ sa = mpsegsa[segIovLast] - sa - 1; ra = mpsegraFirst[segIovLast] - ra + 0x10; /* Add in last segment size */ if(mpsegcb[segIovLast] + ra < LXIVK) ra += mpsegcb[segIovLast]; else { ra -= LXIVK - mpsegcb[segIovLast]; sa += 0x1000; } /* Normalize */ sa += (SATYPE) (ra >> 4); ra &= 0xF; /* Set field with min. no of para.s above image */ E_MINALLOC(run) = (WORD) (sa + ((ra + 0xF) >> 4)); /* If /HIGH not given, then cparmaxAlloc = max(maxalloc,minalloc) */ if(cparMaxAlloc && E_MINALLOC(run) > cparMaxAlloc) cparMaxAlloc = E_MINALLOC(run); } E_MAXALLOC(run) = cparMaxAlloc; #if OVERLAYS E_CRLC(run) = mpiovRlc[iov].count; #else E_CRLC(run) = mpiovRlc[0].count; #endif segStack = mpgsnseg[gsnStack]; E_SS(run) = mpsegsa[segStack]; E_SP(run) = (WORD) (cbStack + mpsegraFirst[segStack]); E_CSUM(run) = 0; E_CP(run) = pn; /* Get true size of header */ #if OVERLAYS cbDirectory = (long) E_LFARLC(run) + ((long) mpiovRlc[iov].count << 2); #else cbDirectory = (long) E_LFARLC(run) + ((long) mpiovRlc[0].count << 2); #endif /* Get padding needed for header */ if (fBinary) cbPadding = 0; else cbPadding = (0x200 - ((WORD) cbDirectory & 0x1FF)) & 0x1FF; /* Pages in header */ pn = (WORD)((cbDirectory + 0x1FF) >> 9); cparDirectory = pn << SHPNTOPAR; /* Paragraphs in header */ E_CPARHDR(run) = cparDirectory; /* Store in header */ E_CP(run) += pn; /* Add header pages to file size */ #if OVERLAYS E_OVNO(run) = iov; #else E_OVNO(run) = 0; #endif ovlLfa = ftell(bsRunfile); if (fBinary) { if (E_IP(run) != 0 && E_IP(run) != 0x100) OutWarn(ER_comstart); } else OutHeader(&run); /* Output relocation table. Turn exepack off first. */ #if FEXEPACK fSave = fExePack; fExePack = FALSE; #endif #if OVERLAYS if (!fBinary) OutRlc(iov); #else if (!fBinary) OutRlc(); #endif /* Restore exepack */ #if FEXEPACK fExePack = fSave; #endif /* Output padding */ WriteZeros(cbPadding); ra = mpsegraFirst[segIovFirst]; /* Offset of first segment */ sa = mpsegsa[segIovFirst]; /* Base of first segment */ #if INMEM if(saExe) OutExeBlock(segIovFirst,segFinaliov); else #endif /* Loop through segs in overlay */ if (!iov) ovlRootBeg = ftell(bsRunfile); for(seg = segIovFirst; seg <= segFinaliov; ++seg) { #if OVERLAYS if(mpsegiov[seg] == iov) { #endif /* * Pad up to start of segment. First determine destination * segment address. We could just use mpsegsa[seg] were it * not for packcode. */ saStart = (SATYPE) (mpsegsa[seg] + (mpsegraFirst[seg] >> 4)); tmp = 0; while(ra != (mpsegraFirst[seg] & 0xf) || sa < saStart) { #if FEXEPACK if (fExePack) OutPack("\0", 1); else #endif tmp++; if(++ra > 0xF) { ra &= 0xF; ++sa; } parity ^= 1; } if (!fExePack && tmp) WriteZeros(tmp); /* Output the segment and update the address */ #if QBLIB /* * If /QUICKLIB and segment is 1st in DGROUP or 1st code, * skip leading 0s and adjust the count, less 2 for the * count word. */ if(fQlib && (seg == mpgsnseg[mpggrgsn[ggrDGroup]] || seg == segQCode)) cbSkip += (long) SkipLead0(seg) - 2; else #endif { if (fBinary && !fOrgStriped && mpsegcb[seg] > 0x100) { /* * For .Com files strip first 0x100 bytes * from the first non-empyt segment */ mpsegraFirst[seg] += E_IP(run); mpsegcb[seg] -= E_IP(run); segcbDelta = E_IP(run); fOrgStriped = TRUE; segAdjCom = seg; } if (mpsegMem[seg]) { #if FEXEPACK if (fExePack) OutPack(mpsegMem[seg] + mpsegraFirst[seg], mpsegcb[seg]); else #endif WriteExe(mpsegMem[seg] + mpsegraFirst[seg], mpsegcb[seg]); if (seg != mpgsnseg[gsnOvlData]) FFREE(mpsegMem[seg]); } } mpsegcb[seg] += segcbDelta; segcbDelta = 0; sa += (WORD)(mpsegcb[seg] >> 4); ra += (WORD)(mpsegcb[seg] & 0xF); if(ra > 0xF) { ra &= 0xF; ++sa; } #if OVERLAYS } #endif } #if FALSE if (!fBinary) { /* Complement checksum, go to checksum field and output it. */ vchksum = (~vchksum) & ~(~0 << WORDLN); fseek(bsRunfile,lfaPrev + IBWCHKSUM,0); OutWord(vchksum); } #endif #if QBLIB /* * If /QUICKLIB, patch the starting address which has been * invalidated by processing leading 0s. */ if(fQlib) { // Seek to the CS:IP field fseek(bsRunfile,lfaPrev + IBWCSIP,0); FixQStart(cbSkip,&run); } #endif #if FEXEPACK /* Finish up with exepack stuff if necessary */ if (fExePack) { EndPack(&run); cb = 0x1ff & (0x200 - E_CBLP(run)); /* Correct cb */ fExePack = FALSE; /* In case of overlays */ } #endif #if OVERLAYS /* * If not last overlay: return to end of file, pad to page boundary, * and get length of file. */ fseek(bsRunfile,0L,2); if (iov != (IOVTYPE) (iovMac - 1)) { while(cb--) OutByte(bsRunfile,'\0'); } lfaPrev = ftell(bsRunfile); if (fDynamic) { // Update $$MPOVLLFA and $$MPOVLSIZE tables // // OVERLAY_DATA --> +-------------+ // | DW $$CGSN | // +-------------+ // | DW $$COVL | // +-------------+ // | $$MPGSNBASE | // | osnMac * DW | // +-------------+ // | $$MPGSNOVL | // | osnMac * DW | // +-------------+ // | $$MPOVLLFA | // | iovMac * DD | // +-------------+ // | $$MPOVLSIZE | // | iovMac * DD | // +-------------+ vgsnCur = gsnOvlData; ovlDataOffset = 4 + (osnMac << 2) + iov * sizeof(DWORD); MoveToVm(sizeof(DWORD), (BYTE *) &ovlLfa, mpgsnseg[gsnOvlData], ovlDataOffset); ovlDataOffset += iovMac << 2; // Exclude the header size imageSize = ((DWORD) (E_CP(run) - (E_CPARHDR(run) >> SHPNTOPAR) - 1) << 9) + (E_CBLP(run) ? E_CBLP(run) : 512); imageSize = ((imageSize + 0xf) & ~0xf) >> 4; imageSize += E_MINALLOC(run); if ((imageSize<<4) > LXIVK && iov) Fatal(ER_ovl64k, iov); MoveToVm(sizeof(DWORD), (BYTE *) &imageSize, mpgsnseg[gsnOvlData], ovlDataOffset); } } #endif if (E_MINALLOC(run) == 0 && E_MAXALLOC(run) == 0) OutError(ER_nosegdef); /* No code or initialized data in .EXE */ if (fDynamic) { // Patch $$MPOVLLFA and $$MPOVLSIZE table in the .EXE file seg = mpgsnseg[gsnOvlData]; fseek(bsRunfile, ovlRootBeg + ((long) mpsegsa[seg] << 4), 0); WriteExe(mpsegMem[seg] + mpsegraFirst[seg], mpsegcb[seg]); FFREE(mpsegMem[seg]); } if (fExeStrSeen) { fseek(bsRunfile,0L,2); WriteExe(ExeStrBuf, ExeStrLen); } #if SYMDEB if (fSymdeb) { if (fBinary) { /* * For .COM files CV info goes into separate file. */ SBTYPE sbDbg; /* .DBG file name */ AHTEPTR hte; /* Hash table entry address */ struct _stat fileInfo; _fstat(fileno(bsRunfile), &fileInfo); CloseFile(bsRunfile); hte = (AHTEPTR ) FetchSym(rhteRunfile,FALSE); /* Get run file name */ #if OSMSDOS if(hte->cch[2] != ':') { /* If no drive spec */ sbDbg[1] = chRunFile; /* Use saved drive letter */ sbDbg[2] = ':'; /* Put in colon */ sbDbg[0] = '\002'; /* Set length */ } else sbDbg[0] = '\0'; /* Length is zero */ memcpy(&sbDbg[B2W(sbDbg[0]) + 1],&GetFarSb(hte->cch)[1],B2W(hte->cch[0])); /* Get name from hash table */ sbDbg[0] += (BYTE)hte->cch[0]; /* Fix length */ #else memcpy(sbDbg,GetFarSb(hte->cch),B2W(hte->cch[0]) + 1); /* Get name from hash table */ #endif UpdateFileParts(sbDbg, sbDotDbg); /* Force extension to .DBG */ sbDbg[B2W(sbDbg[0]) + 1] = '\0'; /* Null-terminate name */ if((bsRunfile = fopen(&sbDbg[1], WRBIN)) == NULL) Fatal(ER_openw, &sbDbg[1]); #if OSMSDOS setvbuf(bsRunfile,bigbuf,_IOFBF,sizeof(bigbuf)); #endif /* Write time stamp into .DBG file */ WriteExe(&fileInfo.st_atime, sizeof(time_t)); } OutDebSections(); /* Generate ISLAND sections */ } #endif }