|
|
/*
* 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 <minlit.h> /* Types and constants */
#include <bndtrn.h> /* Types and constants */
#include <bndrel.h> /* Types and constants */
#include <lnkio.h> /* Linker I/O definitions */
#include <lnkmsg.h> /* Error messages */
#include <extern.h> /* External declarations */
#include <sys\types.h>
#include <sys\stat.h>
#include <newexe.h>
#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 <dos.h>
/*
* 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 = 0; /* 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
if (fseek(bsRunfile,lfaPrev + IBWCSIP,0)) Fatal(ER_badobj); 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. */
if (fseek(bsRunfile,0L,2)) Fatal(ER_badobj); 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]; if (fseek(bsRunfile, ovlRootBeg + ((long) mpsegsa[seg] << 4), 0)) Fatal(ER_badobj); WriteExe(mpsegMem[seg] + mpsegraFirst[seg], mpsegcb[seg]); FFREE(mpsegMem[seg]); } if (fExeStrSeen) { if (fseek(bsRunfile,0L,2)) Fatal(ER_badobj); 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
}
|