/*static char *SCCSID = "%W% %E%";*/ /* * Copyright Microsoft Corporation 1983-1987 * * This Module contains Proprietary Information of Microsoft * Corporation and should be treated as Confidential. */ /* Exepack */ /**************************************************************** * * * PACK.C * * * ****************************************************************/ #include /* Types, constants */ #include /* More types and constants */ #include /* More types and constants */ #include /* Linker I/O definitions */ #include /* Error messages */ #include /* External declarations */ #if FEXEPACK AND ODOS3EXE /* Whole file is conditional */ typedef struct _RUNTYPE { WORD wSignature; WORD cbLastp; WORD cpnRes; WORD irleMax; WORD cparDirectory; WORD cparMinAlloc; WORD cparMaxAlloc; WORD saStack; WORD raStackInit; WORD wchksum; WORD raStart; WORD saStart; WORD rbrgrle; WORD iovMax; WORD doslev; } RUNTYPE; /* States of automaton */ #define STARTSTATE 0 #define FINDREPEAT 1 #define FINDENDRPT 2 #define EMITRECORD 3 /* * LOCAL FUNCTION PROTOTYPES */ LOCAL void NEAR EmitRecords(void); LOCAL unsigned char NEAR GetFromVM(void); LOCAL unsigned short NEAR ScanWhileSame(void); LOCAL unsigned short NEAR ScanWhileDifferent(void); LOCAL WORD NEAR AfterScanning(unsigned short l); LOCAL void NEAR OutEnum(void); LOCAL void NEAR OutIter(SATYPE sa, WORD length); /* * DATA DEFINED IN UNPACK MODULE: unpack.asm */ #if NOT defined( _WIN32 ) extern char * FAR cdecl UnpackModule; /* Unpacker/Relocator module */ extern char FAR cdecl SegStart; // Start of unpacker extern WORD FAR cdecl cbUnpack; /* Length of UnpackModule */ extern WORD FAR cdecl ipsave; /* Original IP */ extern WORD FAR cdecl cssave; /* Original CS */ extern WORD FAR cdecl spsave; /* Original SP */ extern WORD FAR cdecl sssave; /* Original SS */ extern WORD FAR cdecl cparExp; /* # para. in expanded image */ extern WORD FAR cdecl raStartUnpack; /* Offset of code start in unpacker */ extern WORD FAR cdecl raMoveUnpack; /* Offset of self-moving in unpacker */ extern WORD FAR cdecl raBadStack; /* Bottom of bad stack range */ extern WORD FAR cdecl szBadStack; /* Bad stack range */ #else // _WIN32 // // For the portable NTGroup version of this linker, we can't use unpack32.asm // directly because we need to run on RISC platforms. But we still need this // code, which is real-mode x86 code tacked onto the DOS binary which unpacks // the packed EXE and then calls the real entrypoint. So it's defined as a // byte array, and the interesting offsets are hard-coded here. I came across // these values and the code debugging link3216.exe built as the languages group // did. // #define SegStart unpack #define cbUnpack (*(WORD *) &unpack[6]) #define ipsave (*(WORD *) &unpack[0]) #define cssave (*(WORD *) &unpack[2]) #define spsave (*(WORD *) &unpack[8]) #define sssave (*(WORD *) &unpack[0xa]) #define cparExp (*(WORD *) &unpack[0xc]) #define raStartUnpack 0x10 #define raMoveUnpack 0x33 #define raBadStack 0 #define szBadStack 0x35 unsigned char unpack[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x32, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x52, 0x42, 0x8b, 0xe8, 0x8c, 0xc0, 0x05, 0x10, 0x00, 0x0e, 0x1f, 0xa3, 0x04, 0x00, 0x03, 0x06, 0x0c, 0x00, 0x8e, 0xc0, 0x8b, 0x0e, 0x06, 0x00, 0x8b, 0xf9, 0x4f, 0x8b, 0xf7, 0xfd, 0xf3, 0xa4, 0x50, 0xb8, 0x34, 0x00, 0x50, 0xcb, 0x8c, 0xc3, 0x8c, 0xd8, 0x48, 0x8e, 0xd8, 0x8e, 0xc0, 0xbf, 0x0f, 0x00, 0xb9, 0x10, 0x00, 0xb0, 0xff, 0xf3, 0xae, 0x47, 0x8b, 0xf7, 0x8b, 0xc3, 0x48, 0x8e, 0xc0, 0xbf, 0x0f, 0x00, 0xb1, 0x04, 0x8b, 0xc6, 0xf7, 0xd0, 0xd3, 0xe8, 0x8c, 0xda, 0x2b, 0xd0, 0x73, 0x04, 0x8c, 0xd8, 0x2b, 0xd2, 0xd3, 0xe0, 0x03, 0xf0, 0x8e, 0xda, 0x8b, 0xc7, 0xf7, 0xd0, 0xd3, 0xe8, 0x8c, 0xc2, 0x2b, 0xd0, 0x73, 0x04, 0x8c, 0xc0, 0x2b, 0xd2, 0xd3, 0xe0, 0x03, 0xf8, 0x8e, 0xc2, 0xac, 0x8a, 0xd0, 0x4e, 0xad, 0x8b, 0xc8, 0x46, 0x8a, 0xc2, 0x24, 0xfe, 0x3c, 0xb0, 0x75, 0x05, 0xac, 0xf3, 0xaa, 0xeb, 0x06, 0x3c, 0xb2, 0x75, 0x6d, 0xf3, 0xa4, 0x8a, 0xc2, 0xa8, 0x01, 0x74, 0xb1, 0xbe, 0x32, 0x01, 0x0e, 0x1f, 0x8b, 0x1e, 0x04, 0x00, 0xfc, 0x33, 0xd2, 0xad, 0x8b, 0xc8, 0xe3, 0x13, 0x8b, 0xc2, 0x03, 0xc3, 0x8e, 0xc0, 0xad, 0x8b, 0xf8, 0x83, 0xff, 0xff, 0x74, 0x11, 0x26, 0x01, 0x1d, 0xe2, 0xf3, 0x81, 0xfa, 0x00, 0xf0, 0x74, 0x16, 0x81, 0xc2, 0x00, 0x10, 0xeb, 0xdc, 0x8c, 0xc0, 0x40, 0x8e, 0xc0, 0x83, 0xef, 0x10, 0x26, 0x01, 0x1d, 0x48, 0x8e, 0xc0, 0xeb, 0xe2, 0x8b, 0xc3, 0x8b, 0x3e, 0x08, 0x00, 0x8b, 0x36, 0x0a, 0x00, 0x03, 0xf0, 0x01, 0x06, 0x02, 0x00, 0x2d, 0x10, 0x00, 0x8e, 0xd8, 0x8e, 0xc0, 0xbb, 0x00, 0x00, 0xfa, 0x8e, 0xd6, 0x8b, 0xe7, 0xfb, 0x8b, 0xc5, 0x2e, 0xff, 0x2f, 0xb4, 0x40, 0xbb, 0x02, 0x00, 0xb9, 0x16, 0x00, 0x8c, 0xca, 0x8e, 0xda, 0xba, 0x1c, 0x01, 0xcd, 0x21, 0xb8, 0xff, 0x4c, 0xcd, 0x21, 0x50, 0x61, 0x63, 0x6b, 0x65, 0x64, 0x20, 0x66, 0x69, 0x6c, 0x65, 0x20, 0x69, 0x73, 0x20, 0x63, 0x6f, 0x72, 0x72, 0x75, 0x70, 0x74 }; #endif // _WIN32 LOCAL WORD lastc; /* last character */ LOCAL WORD c; /* current or next character */ LOCAL WORD State = STARTSTATE; /* current state */ LOCAL FTYPE fEnumOK; /* OK to emit enumerated records */ LOCAL WORD cbRepeat; /* length of repeated stream */ LOCAL WORD cbEnum; /* length of enumerated stream */ #define EHLEN 3 /* 2 for length + 1 for type */ #define MAXRPT 0xfff0 /* Maximum length to compress */ #define MAXENM (0xfff0-(EHLEN+1)) /* Maximum length of enum. stream */ #define MINEXP (2*EHLEN+2) /* Minimum length of repeated stream, * after the first repeate record */ #define toAdr20(seg, off) (((long)seg << 4) + off) LOCAL WORD minRpt = (18 * EHLEN) + 1; /* Minimum for rpt rec begins larger */ /* Type values for expansion record headers */ #define RPTREC 0xb0 /* Repeat record */ #define ENMREC 0xb2 /* Enumerated record */ /* * OutPack - Run a buffer through the compactor. Return value is * undefined. */ void OutPack (pb, cb) REGISTER BYTE *pb; /* Pointer to buffer */ unsigned cb; /* Number of bytes to compress */ { REGISTER BYTE *endp; /* Pointer to end of buffer */ endp = &pb[cb]; while (pb < endp) switch (State) { case STARTSTATE: lastc = *pb++; State = FINDREPEAT; break; case FINDREPEAT: if (cbEnum >= MAXENM) { EmitRecords(); State = FINDREPEAT; break; } c = *pb++; if (c == lastc) { cbRepeat = 2; State = FINDENDRPT; break; } /* At this point c != lastc */ fputc(lastc, bsRunfile); cbEnum++; lastc = c; break; case FINDENDRPT: c = *pb++; if (c == lastc && cbRepeat < MAXRPT) { cbRepeat++; break; } if (cbRepeat < minRpt) { /* * Not long enough. Enum record swallows * repeated chars. */ while (cbEnum <= MAXENM && cbRepeat > 0) { fputc(lastc, bsRunfile); cbEnum++; cbRepeat--; } if (cbRepeat > 0) EmitRecords(); } else EmitRecords(); lastc = c; /* Prepare for next stream */ State = FINDREPEAT; } } /* * EmitRecords - Emits 1 or 2 expansion records. Return value is * undefined. */ LOCAL void NEAR EmitRecords () { /* We have 1 or 2 records to output */ if (cbEnum > 0) { #if MDEBUG AND FDEBUG if (fDebug) fprintf(stdout, "E%8x\n", cbEnum); #endif if (fEnumOK) { /* Output an enumerated record header */ OutWord(cbEnum); fputc(ENMREC, bsRunfile); } cbEnum = 0; } if (cbRepeat >= minRpt) { #if MDEBUG AND FDEBUG if (fDebug) fprintf(stdout, "R%8x\n", cbRepeat); #endif /* Output a repeat record */ fputc(lastc, bsRunfile); OutWord(cbRepeat); if (!fEnumOK) { /* 1st record header generated */ fputc(RPTREC|1, bsRunfile); fEnumOK = 1; } else fputc(RPTREC, bsRunfile); cbRepeat = 0; minRpt = MINEXP; /* 1st is out, so reset minRpt */ } else if (cbRepeat > 0) { cbEnum = cbRepeat; while (cbRepeat-- > 0) fputc(lastc, bsRunfile); } } /* * EndPack - End the packing procedure: add the relocator module. */ void EndPack (prun) RUNTYPE *prun; /* Pointer to runfile header */ { long fpos; /* File position */ WORD cparPacked; /* # paras in packed image */ WORD cparUnpack; /* Size of Unpack module (paras) */ int i; int crle; /* Count of relocs for a frame */ long cTmp; /* Temporary count */ long us; /* User's stack in minalloc */ long los; /* Low end of forbidden stack */ fseek(bsRunfile, 0L, 2); /* Go to end of file */ cTmp = (((((long)prun->cpnRes-1)<<5) - prun->cparDirectory) << 4) + (prun->cbLastp ? prun->cbLastp : 512); /* Get # bytes in expanded image */ cbRepeat += (WORD) (0xf & (0x10 - (0xf & cTmp))); /* Make it look like image ends on * paragraph boundary */ if (State == FINDREPEAT) { fputc(lastc, bsRunfile); /* Update last enum record */ cbEnum++; } minRpt = 1; /* Force final repeat rec. out */ EmitRecords(); /* Output the final record(s) */ cparExp = (short) ((cTmp + 0xf) >> 4);/* Save # paras in image */ /* * Append the unpacking module (relocator-expander) */ fpos = ftell(bsRunfile); /* Save where Unpack begins */ /* Align Unpack on paragraph boundary */ while (fpos & 0x0f) { fpos++; fputc(0xff, bsRunfile); } /* Make sure User stack won't stomp on unpack code */ us = toAdr20(prun->saStack, prun->raStackInit); los = toAdr20((cTmp >> 4), raBadStack); while ( us > los && us - los < szBadStack ) { for (i = 0; i < 16; i++) { us--; fpos++; fputc(0xff, bsRunfile); } } fflush(bsRunfile); cparPacked = (WORD) ((fpos >> 4) - prun->cparDirectory); if (cTmp < ((long)cparPacked << 4) + raMoveUnpack) Fatal(ER_badpack); /* Append relocator module (Unpack). This code depends * closely on the structure of unpack.asm. */ /* Get length of relocation area */ for (crle = 0, i = 0; i < 16; i++) crle += (mpframeRlc[i].count + 1) << 1; /* Initialize stack of relocator module */ ipsave = prun->raStart; cssave = prun->saStart; #if defined( _WIN32 ) if (cbUnpack != sizeof(unpack)) Fatal(ER_badpack); #endif i = cbUnpack; cbUnpack += (WORD)crle; spsave = prun->raStackInit; sssave = prun->saStack; #ifdef M_BYTESWAP bswap(Unpack, 7); #endif #if DOSX32 WriteExe(&SegStart, i); #else fwrite(UnpackModule, 1, i, bsRunfile); #endif /* Append optimized relocation records */ for (i = 0; i < 16; i++) { crle = mpframeRlc[i].count; OutWord((WORD) crle); WriteExe(mpframeRlc[i].rgRlc, crle * sizeof(WORD)); } /* Correct header values */ fpos += cbUnpack; prun->cbLastp = (WORD) (fpos & 0x1ff); prun->cpnRes = (WORD) ((fpos + 511) >> 9); prun->irleMax = 0; cparUnpack = (cbUnpack + 0xf) >> 4; prun->cparMinAlloc = (cparExp + max(prun->cparMinAlloc,(cparUnpack+8))) - (cparPacked + cparUnpack); if (prun->cparMaxAlloc < prun->cparMinAlloc) prun->cparMaxAlloc = prun->cparMinAlloc; prun->saStack = cparExp + cparUnpack; prun->raStackInit = 128; prun->raStart = raStartUnpack; prun->saStart = cparPacked; fseek(bsRunfile, 0L, 0); OutHeader((struct exe_hdr *) prun); fseek(bsRunfile, fpos, 0); } #ifdef M_BYTESWAP /* * Swap bytes for 1st n words in buffer. */ LOCAL bswap (buf, n) REGISTER char *buf; REGISTER int n; { REGISTER char swapb; for ( ; n-- > 0 ; buf += 2) { swapb = buf[0]; buf[0] = buf[1]; buf[1] = swapb; } } #endif #endif /*FEXEPACK AND ODOS3EXE*/ /* * The following routines concern packing segmented-executable format * files. */ #if FALSE #define MINREPEAT 32 /* Min length of iteration cousing compression */ LOCAL long vaLast; /* Virtual address */ LOCAL long vaStart; /* Virtual scanning start address */ LOCAL long BufEnd; /* Virtual address of buffer end */ #if EXE386 LOCAL long ra; /* Offset within packed segment */ #endif LOCAL BYTE LastB; LOCAL BYTE CurrentB; LOCAL long VPageAddress; /* Virtual address of current page */ LOCAL WORD VPageOffset; /* Current position within virtual page */ LOCAL BYTE *PageBuffer; /* Virtual page buffer */ LOCAL BYTE NEAR GetFromVM() { if (VPageOffset == PAGLEN) { PageBuffer = mapva(VPageAddress, FALSE); /* Fetch page */ VPageAddress += PAGLEN; /* Update page virtual address */ VPageOffset = 0; /* Init page offset */ } return(PageBuffer[VPageOffset++]); } LOCAL WORD NEAR ScanWhileSame() { long l; l = 2L; /* We are looking at two bytes in buffer */ while (CurrentB == LastB) { if (vaStart + l >= BufEnd) return((WORD) l); /* We bump the buffer end */ CurrentB = GetFromVM(); l++; } return(l == 2L ? 0 : (WORD) (l - 1)); /* We went one byte too far to detect they are different */ } LOCAL WORD NEAR ScanWhileDifferent() { long l; l = 2L; /* We are looking at two bytes in buffer */ while (CurrentB != LastB) { if (vaStart + l >= BufEnd) return((WORD) l); /* We bump the buffer end */ LastB = CurrentB; CurrentB = GetFromVM(); l++; } return((WORD) (l - 2)); /* We went two bytes too far to detect they are the same */ } LOCAL WORD NEAR AfterScanning(l) WORD l; /* Length of scanned bytes */ { vaStart += l; /* Update scan start address */ #if EXE386 ra += l; /* Update offset in segment */ #endif if (vaStart + 2 >= BufEnd) { /* We need at least to bytes remaining */ return(FALSE); /* Buffer end */ } else { if (LastB != CurrentB) { /* We stop at iterated and enumerated */ LastB = CurrentB; /* byte sequence, so we have move */ CurrentB = GetFromVM(); /* one byte forward */ } return((WORD) TRUE); } } LOCAL void NEAR OutEnum(void) { #if EXE386 if (ExeFormat == Exe386) OutVm(vaLast, vaStart - vaLast); else { #endif OutWord(1); OutWord((WORD) (vaStart - vaLast)); OutVm(vaLast, vaStart - vaLast); #if EXE386 } #endif PageBuffer = mapva((VPageAddress - PAGLEN), FALSE); /* Refetch page */ } LOCAL void NEAR OutIter(SATYPE sa, WORD length) { #if EXE386 ITER idata; /* Iterated data description for range */ if (ExeFormat == Exe386) { idata.iterations = (DWORD) length; idata.length = (DWORD) 1; idata.data = (DWORD) LastB; UpdateRanges(ShortIterData, sa, ra, &idata); } else { #endif OutWord(length); OutWord(1); OutByte(bsRunfile, LastB); #if EXE386 } #endif } /* * Out5Pack - Run a buffer through the compactor. Return value is * starting position where data was written to output file. */ long Out5Pack (sa, packed) SATYPE sa; /* File segment to be packed */ WORD *packed; /* TRUE if iterated records written */ { WORD proceed; /* True if there are bytes to scan */ WORD length; /* Scanned bytes length */ long lfaStart; /* Starting file address */ lfaStart = ftell(bsRunfile); /* Get the starting address */ VPageAddress = AREASA(sa); VPageOffset = PAGLEN; *packed = FALSE; if (mpsacbinit[sa] > 1L) { /* If buffer is big enough */ #if EXE386 ra = 0L; /* Offset within segment */ #endif vaStart = VPageAddress; vaLast = vaStart; BufEnd = vaStart + mpsacbinit[sa]; LastB = GetFromVM(); CurrentB = GetFromVM(); proceed = (WORD) TRUE; /* Initialize */ while (proceed) { length = ScanWhileDifferent(); if (!(proceed = AfterScanning(length))) break; if ((length = ScanWhileSame()) > MINREPEAT) { /* If there are enough same bytes */ if (vaLast != vaStart) OutEnum(); /* First write out preceeding diff. bytes */ OutIter(sa, length); /* Now write out iterated record */ proceed = AfterScanning(length); *packed = (WORD) TRUE; vaLast = vaStart; } else proceed = AfterScanning(length); /* Otherwise enumerated record swallow this */ } /* small repeated record */ } if (*packed) { if (vaLast != BufEnd) { vaStart = BufEnd; OutEnum(); /* Write out any remainig bytes */ } mpsacbinit[sa] = ftell(bsRunfile) - lfaStart; /* Return number of written bytes */ return(lfaStart); } else return(OutVm(AREASA(sa),mpsacbinit[sa])); } #endif /*FEXEPACK AND OSEGEXE*/