|
|
/* SCCSID = %W% %E% */ /*
* Copyright Microsoft Corporation, 1983-1987 * * This Module contains Proprietary Information of Microsoft * Corporation and should be treated as Confidential. */ /****************************************************************
* * * NEWUTL.C * * * * Linker utilities. * * * ****************************************************************/
#include <minlit.h> /* Types, constants */
#include <bndtrn.h> /* More types and constants */
#include <bndrel.h> /* More types and constants */
#include <lnkio.h> /* Linker I/O definitions */
#include <lnkmsg.h> /* Error messages */
#include <newdeb.h> /* CodeView support */
#include <extern.h> /* External declarations */
#include <nmsg.h> /* Near message strings */
#include <string.h>
#include <stdarg.h>
#if EXE386
#include <exe386.h>
#endif
#if NEWIO
#include <errno.h> /* System error codes */
#endif
#if USE_REAL
#if NOT defined( _WIN32 )
#define i386
#include <windows.h>
#endif
// The memory sizes are in paragraphs.
#define TOTAL_CONV_MEM (0xFFFF)
#define CONV_MEM_FOR_TNT (0x800) // 32K of memory
#define MIN_CONV_MEM (0x1900) // 100 K of memory
typedef unsigned short selector_t ; //Define type to hold selectors
static selector_t convMemSelector ; // Selector to conv memory.
static short noOfParagraphs ; // size of the available blocks in paragraphs
static int realModeMemPageable ; // = FALSE
#endif
#if WIN_NT OR DOSX32
unsigned char FCHGDSK(int drive) { return(FALSE); } #endif
#define DISPLAY_ON FALSE
#if DISPLAY_ON
extern int TurnDisplayOn; #endif
APROPCOMDATPTR comdatPrev=NULL; /* Pointer to symbol table entry */ int fSameComdat=FALSE; /* Set if LINSYM to the same COMDAT */
/********************************************************************
* INPUT ROUTINES * ********************************************************************/
/*** GetLineOff - read part of LINNUM record
* * Purpose: * This function reads line/offset pair from LINNUM record. It is here * because we want to keep all the I/O functions near and the LINNUM * processing is performed in NEWDEB.C which resides in another segment. * * Input: * - pLine - pointer to line number * - pRa - pointer to offset * * Output: * Returns line/offset pair from OMF record. * * Exceptions: * None. * * Notes: * None. * *************************************************************************/
void GetLineOff(WORD *pLine, RATYPE *pRa) { *pLine = WGets() + QCLinNumDelta; // Get line number
// Get code segment offset
#if OMF386
if (rect & 1) *pRa = LGets(); else #endif
*pRa = (RATYPE) WGets(); }
/*** GetGsnInfo - read the segment index of the LINNUM
* * Purpose: * This function reads the segemnt index from LINNUM record. It is here * because we want to keep all the I/O functions near and the LINNUM * processing is performed in NEWDEB.C which resides in another segment. * * Input: * - pRa - pointer to offset correction for COMDATs * * Output: * Returns global segment index and for lines in COMDAT record * offset correction. * * Exceptions: * None. * * Notes: * None. * *************************************************************************/
WORD GetGsnInfo(GSNINFO *pInfo) { WORD fSuccess; // TRUE if everything is OK
WORD attr; // COMDAT flags
WORD comdatIdx; // COMDAT symbol index
APROPCOMDATPTR comdat; // Pointer to symbol table entry
fSuccess = TRUE; if (TYPEOF(rect) == LINNUM) { // Read regular LINNUM record
GetIndex((WORD)0,(WORD)(grMac - 1)); // Skip group index
pInfo->gsn = mpsngsn[GetIndex((WORD)1,(WORD)(snMac - 1))]; // Get global SEGDEF number
pInfo->comdatRa = 0L; pInfo->comdatSize = 0L; pInfo->fComdat = FALSE; } else { // Read LINSYM record - line numbers for COMDAT
attr = (WORD) Gets(); comdatIdx = GetIndex(1, (WORD)(lnameMac - 1)); comdat = (APROPCOMDATPTR ) PropRhteLookup(mplnamerhte[comdatIdx], ATTRCOMDAT, FALSE); fSameComdat = FALSE; if (comdat != NULL) { if(comdat == comdatPrev) fSameComdat = 1; else comdatPrev = comdat;
if ((fPackFunctions && !(comdat->ac_flags & REFERENCED_BIT)) || !(comdat->ac_flags & SELECTED_BIT) || comdat->ac_obj != vrpropFile) { SkipBytes((WORD)(cbRec - 1)); fSuccess = FALSE; } else { pInfo->gsn = comdat->ac_gsn; pInfo->comdatRa = comdat->ac_ra; pInfo->comdatSize = comdat->ac_size; pInfo->comdatAlign= comdat->ac_align; pInfo->fComdat = TRUE; } } else { SkipBytes((WORD)(cbRec - 1)); fSuccess = FALSE; } } return(fSuccess); }
/****************************************************************
* * * Gets: * * * * Read a byte of input and return it. * * * ****************************************************************/ #if NOASM
#if !defined( M_I386 ) && !defined( _WIN32 )
WORD NEAR Gets(void) { REGISTER WORD b;
if((b = getc(bsInput)) == EOF) InvalidObject(); /* After reading the byte, decrement the OMF record counter. */ --cbRec; return(b); } #endif
#endif
#if ALIGN_REC
#else
/****************************************************************
* * * WGetsHard: * * * * Read a word of input and return it. * * * ****************************************************************/
WORD NEAR WGetsHard() { REGISTER WORD w;
// handle hard case... easy case already tested in WGets
w = Gets(); /* Get low-order byte */ return(w | (Gets() << BYTELN)); /* Return word */ }
#if OMF386
/****************************************************************
* * * LGets: * * * * Read a long word of input and return it. * * * ****************************************************************/
DWORD NEAR LGets() { DWORD lw; FILE * f = bsInput;
// NOTE: this code will only work on a BigEndian machine
if (f->_cnt >= sizeof(DWORD)) { lw = *(DWORD *)(f->_ptr); f->_ptr += sizeof(DWORD); f->_cnt -= sizeof(DWORD); cbRec -= sizeof(DWORD); return lw; }
lw = WGets(); /* Get low-order word */ return(lw | ((DWORD) WGets() << 16));/* Return long word */ } #endif
#endif
#if 0
/****************************************************************
* * * GetBytes: * * * * Read n bytes from input. * * If n is greater than SBLEN - 1, issue a fatal error. * * * ****************************************************************/
void NEAR GetBytes(pb,n) BYTE *pb; /* Pointer to buffer */ WORD n; /* Number of bytes to read in */ { FILE *f = bsInput;
if(n >= SBLEN) InvalidObject();
if (n <= f->_cnt) { memcpy(pb,f->_ptr, n); f->_cnt -= n; f->_ptr += n; } else fread(pb,1,n,f); /* Ask for n bytes */
cbRec -= n; /* Update byte count */ } #endif
#if 0
/****************************************************************
* * * SkipBytes: * * * * Skip n bytes of input. * * * ****************************************************************/
void NEAR SkipBytes(n) REGISTER WORD n; /* Number of bytes to skip */ { #if WIN_NT
WORD cbRead; SBTYPE skipBuf;
cbRec -= n; // Update byte count
while (n) // While there are bytes to skip
{ cbRead = n < sizeof(SBTYPE) ? n : sizeof(SBTYPE); if (fread(skipBuf, 1, cbRead, bsInput) != cbRead) InvalidObject(); n -= cbRead; } #else
FILE *f = bsInput;
if (f->_cnt >= n) { f->_cnt -= n; f->_ptr += n; } else if(fseek(f,(long) n,1)) InvalidObject(); cbRec -= n; /* Update byte count */ #endif
} #endif
/****************************************************************
* * * GetIndexHard: (GetIndex -- hard case) * * * * This function reads in a variable-length index field from * * the input file. It takes as its arguments two word values * * which represent the minimum and maximum allowable values * * the index. The function returns the value of the index. * * See p. 12 in "8086 Object Module Formats EPS." * * * ****************************************************************/
WORD NEAR GetIndexHard(imin,imax) WORD imin; /* Minimum permissible value */ WORD imax; /* Maximum permissible value */ { REGISTER WORD index;
FILE *f = bsInput;
if (f->_cnt >= sizeof(WORD)) { index = *(BYTE *)(f->_ptr); if (index & 0x80) { index <<= BYTELN; index |= *(BYTE *)(f->_ptr+1); index &= 0x7fff; f->_cnt -= sizeof(WORD); f->_ptr += sizeof(WORD); cbRec -= sizeof(WORD); } else { f->_cnt--; f->_ptr++; cbRec--; } } else { if((index = Gets()) & 0x80) index = ((index & 0x7f) << BYTELN) | Gets(); }
if(index < imin || index > imax) InvalidObject(); return(index); /* Return a good value */ }
/********************************************************************
* STRING ROUTINES * ********************************************************************/
#if OSEGEXE
#if NOASM
/****************************************************************
* * * zcheck: * * * * Determine length of initial nonzero stream in a buffer, and * * return the length. * * * ****************************************************************/
#if defined(M_I386)
#pragma auto_inline(off)
#endif
WORD zcheck(BYTE *pb, WORD cb) { // Loop down from end until a nonzero byte found.
// Return length of remainder of buffer.
#if defined(M_I386)
_asm { push edi ; Save edi movzx ecx, cb ; Number of bytes to check push ds ; Copy ds into es pop es xor eax, eax ; Looking for zeros mov edi, pb ; Start of buffer add edi, ecx ; Just past the end of buffer dec edi ; Last byte in the buffer std ; Decrement pointer repz scasb ; Scan until non-zero byte found jz AllZeros ; Buffer truly empty inc ecx ; Fix count
AllZeros: cld ; Clear flag just to be safe pop edi ; Restore edi mov eax, ecx ; Return count in eax } #endif
for(pb = &pb[cb]; cb != 0; --cb) if(*--pb != '\0') break; return(cb); } #endif
#endif /* OSEGEXE */
#if defined(M_I386)
#pragma auto_inline(on)
#endif
/*** CheckSegmentsMemory - check is all segments have allocated memory
* * Purpose: * Check for not initialized segments. If the segment have a non-zero * size but no initialized data, then we have to allocate for it a * zero filled memory buffer. Normally 'MoveToVm' allocates memory * buffer for segments, but in this case there was no 'moves to VM'. * * Input: * No explicit value is passed. * * Output: * No explicit value is returned. * * Exceptions: * None. * * Notes: * None. * *************************************************************************/
void CheckSegmentsMemory(void) { SEGTYPE seg; SATYPE sa;
if (fNewExe) { for (sa = 1; sa < saMac; sa++) if (mpsaMem[sa] == NULL && mpsacb[sa] > 0) mpsaMem[sa] = (BYTE FAR *) GetMem(mpsacb[sa]); } else { for (seg = 1; seg <= segLast; seg++) if (mpsegMem[seg] == NULL && mpsegcb[seg] > 0) mpsegMem[seg] = (BYTE FAR *) GetMem(mpsegcb[seg] + mpsegraFirst[seg]); } }
/*** WriteExe - write bytes to the executable file
* * Purpose: * Write to the executable file and check for errors. * * Input: * pb - byte buffer to write * cb - buffer size in bytes * * Output: * No explicit value is returned. * * Exceptions: * I/O problems - fatal error and abort * * Notes: * None. * *************************************************************************/
#if !defined( M_I386 ) && !defined( _WIN32 )
#pragma check_stack(on)
void WriteExe(void FAR *pb, unsigned cb) { BYTE localBuf[1024]; WORD count;
while (cb > 0) { count = (WORD) (cb <= sizeof(localBuf) ? cb : sizeof(localBuf)); FMEMCPY((BYTE FAR *) localBuf, pb, count); if (fwrite((char *) localBuf, sizeof(BYTE), count, bsRunfile) != count) { ExitCode = 4; Fatal(ER_spcrun, strerror(errno)); } cb -= count; ((BYTE FAR *) pb) += count; } }
#pragma check_stack(off)
#else
/*** NoRoomForExe - the exe didn't fit
* * Purpose: * emit error message * give fatal error and abort * * Input: * errno must be set * * Output: * No explicit value is returned. * * Notes: * None. * *************************************************************************/
void NoRoomForExe() { ExitCode = 4; Fatal(ER_spcrun, strerror(errno)); }
#endif
/*** WriteZeros - write zero bytes to the executable file
* * Purpose: * Pad executable file with zero bytes. * * Input: * cb - number of bytes to write * * Output: * No explicit value is returned. * * Exceptions: * I/O problems - fatal error and abort * * Notes: * None. * *************************************************************************/
void WriteZeros(unsigned cb) { BYTE buf[512]; unsigned count;
memset(buf, 0, sizeof(buf)); while (cb > 0) { count = cb <= sizeof(buf) ? cb : sizeof(buf); WriteExe(buf, count); cb -= count; } }
/****************************************************************
* * * MoveToVm: * * * * Move a piece of data into a virtual memory area/va. * * * * Input: cb Count of bytes to be moved. * * obData Address of data to be moved. * * seg Logical segment to which data belongs. * * ra Offset at which data belongs. * * * ****************************************************************/
#pragma intrinsic(memcpy)
#if EXE386
void MoveToVm(WORD cb, BYTE *obData, SEGTYPE seg, RATYPE ra) #else
void NEAR MoveToVm(WORD cb, BYTE *obData, SEGTYPE seg, RATYPE ra) #endif
{ long cbtot; /* Count of bytes total */ long cbSeg; /* Segment size */ WORD fError; BYTE FAR *pMemImage; CVINFO FAR *pCVInfo; SATYPE sa;
cbtot = (long) cb + ra;
if (fDebSeg) { pCVInfo = ((APROPFILEPTR ) FetchSym(vrpropFile, FALSE))->af_cvInfo; if (pCVInfo) { if (seg < (SEGTYPE) (segDebFirst + ObjDebTotal)) { cbSeg = pCVInfo->cv_cbTyp; pMemImage = pCVInfo->cv_typ; } else { cbSeg = pCVInfo->cv_cbSym; pMemImage = pCVInfo->cv_sym; }
// Check against segment bounds
fError = cbtot > cbSeg; } else { OutError(ER_badcvseg); return; } } else { if (fNewExe) { cbSeg = ((APROPSNPTR) FetchSym(mpgsnrprop[vgsnCur],FALSE))->as_cbMx; sa = mpsegsa[seg]; if (mpsaMem[sa] == NULL) mpsaMem[sa] = (BYTE FAR *) GetMem(mpsacb[sa]); pMemImage = mpsaMem[sa];
// Check against segment bounds
fError = (long) ((ra - mpgsndra[vgsnCur]) + cb) > cbSeg;
// If data is going up to or past current end of initialized data,
// omit any trailing null bytes and reset mpsacbinit. Mpsacbinit
// will usually go up but may go down if a common segment over-
// writes previous end data with nulls.
if ((DWORD) cbtot >= mpsacbinit[sa]) { if ((DWORD) ra < mpsacbinit[sa] || (cb = zcheck(obData,cb)) != 0) mpsacbinit[sa] = (long) ra + cb; } } else { cbSeg = mpsegcb[seg] + mpsegraFirst[seg]; if (mpsegMem[seg] == NULL) mpsegMem[seg] = (BYTE FAR *) GetMem(cbSeg); pMemImage = mpsegMem[seg];
// Check against segment bounds
fError = cbtot > cbSeg; } }
if (fError) { if (!fDebSeg) OutError(ER_segbnd, 1 + GetFarSb(GetHte(mpgsnrprop[vgsnCur])->cch)); else OutError(ER_badcvseg); } else FMEMCPY(&pMemImage[ra], obData, cb); }
#pragma function(memcpy)
#if (OSEGEXE AND ODOS3EXE) OR EXE386
/*
* Map segment index to memory image address for new-format exes. */ BYTE FAR * NEAR msaNew (SEGTYPE seg) { return(mpsaMem[mpsegsa[seg]]); } #endif
#if (OSEGEXE AND ODOS3EXE) OR EXE386
/*
* Map segment index to memory image address for DOS3 or 286Xenix exes. */ BYTE FAR * NEAR msaOld (SEGTYPE seg) { return(mpsegMem[seg]); } #endif
#if EXE386
/*
* Map segment index to VM area address for 386 exes. */ long NEAR msa386 (seg) SEGTYPE seg; { register long *p; /* Pointer to mpsegcb */ register long *pEnd; /* Pointer to end of mpsegcb */ register long va = AREAFSG; /* Current VM address */
/*
* Segment number-to-VM area mapping is different for 386 segments * because their size limit is so big that allocating a fixed amount * for each segment is impractical, especially when sdb support is * enabled. So segments are allocated contiguously. Each segment * is padded to a VM page boundary for efficiency. * * Implementation: the fastest way would be to allocate a segment * based table of virtual addresses, but this would take more code * and memory. Counting segment sizes is slower but this is not * time-critical routine, and in most cases there will be very few * segments. */ if (fNewExe) { p = &mpsacb[1]; pEnd = &mpsacb[seg]; } #if ODOS3EXE
else { p = &mpsegcb[1]; pEnd = &mpsegcb[seg]; } #endif
for( ; p < pEnd; ++p) va += (*p + (PAGLEN - 1)) & ~(PAGLEN - 1); return(va); } #endif /* EXE386 */
/********************************************************************
* (ERROR) MESSAGE ROUTINES * ********************************************************************/ #pragma auto_inline(off)
/*
* SysFatal : system-level error * * Issue error message and exit with return code 4. */ void cdecl SysFatal (MSGTYPE msg) { ExitCode = 4; Fatal(msg); }
void NEAR InvalidObject(void) { Fatal((MSGTYPE)(fDrivePass ? ER_badobj: ER_eofobj)); }
#pragma auto_inline(on)
/********************************************************************
* MISCELLANEOUS ROUTINES * ********************************************************************/
/*
* Output a word integer. */ void OutWord(x) WORD x; /* A word integer */ { WriteExe(&x, CBWORD); }
/*
* GetLocName : read in a symbol name for L*DEF * * Transform the name by prefixing a space followed by the * module number. Update the length byte. * * Parameters: pointer to a string buffer, 1st byte already * contains length * Returns: nothing */ void NEAR GetLocName (psb) BYTE *psb; /* Name buffer */ { WORD n; BYTE *p;
p = &psb[1]; /* Start after length byte */ *p++ = 0x20; /* Prefix begins with space char */ GetBytes(p,B2W(psb[0])); /* Read in text of symbol */ p += B2W(psb[0]); /* Go to end of string */ *p++ = 0x20; n = modkey; /* Initialize */ /* Convert the module key to ASCII and store backwards */ do { *p++ = (BYTE) ((n % 10) + '0'); n /= 10; } while(n); psb[0] = (BYTE) ((p - (psb + 1))); /* Update length byte */ }
PROPTYPE EnterName(psym,attr,fCreate) BYTE *psym; /* Pointer to length-prefixed string */ ATTRTYPE attr; /* Attribute to look up */ WORD fCreate; /* Create prop cell if not found */ { return(PropSymLookup(psym, attr, fCreate)); /* Hide call to near function */ }
#if CMDMSDOS
#pragma check_stack(on)
/*** ValidateRunFileName - Check if output file has proper extension
* * Purpose: * Check user-specified output file name for valid extension. * Issue warning if extension is invalid and create new file * name with proper extension. * * Input: * ValidExtension - pointer to length prefixed ascii string * representing valid exetension for output * file name. * ForceExtension - TRUE if output file must have new extension, * otherwise user responce takes precedence. * WarnUser - If TRUE than display L4045 if file name changed. * * Output: * rhteRunfile - global virtual pointer to output file * name, changed only if new output name * is created because of invalid original * extension. * warning L4045 - if output file name have to be changed. * *************************************************************************/
void NEAR ValidateRunFileName(BYTE *ValidExtension, WORD ForceExtension, WORD WarnUser) { SBTYPE sb; /* String buffer */ BYTE *psbRunfile; /* Name of runfile */ char oldDrive[_MAX_DRIVE]; char oldDir[_MAX_DIR]; char oldName[_MAX_FNAME]; char oldExt[_MAX_EXT];
/* Get the name of the runfile and check if it has user supplied extension */
psbRunfile = GetFarSb(((AHTEPTR) FetchSym(rhteRunfile,FALSE))->cch); _splitpath(psbRunfile, oldDrive, oldDir, oldName, oldExt);
/* Force extension only when no user defined extension */
if (ForceExtension && oldExt[0] == NULL) { memcpy(sb, ValidExtension, strlen(ValidExtension)); memcpy(bufg, psbRunfile, 1 + B2W(*psbRunfile)); } else { memcpy(bufg, ValidExtension, strlen(ValidExtension)); memcpy(sb, psbRunfile, 1 + B2W(*psbRunfile)); } UpdateFileParts(bufg, sb);
/* If the name has changed, issue a warning and update rhteRunfile. */
if (!SbCompare(bufg, psbRunfile, (FTYPE) TRUE)) { if (WarnUser && !SbCompare(ValidExtension, sbDotExe, (FTYPE) TRUE)) OutWarn(ER_outputname,bufg + 1); PropSymLookup(bufg, ATTRNIL, TRUE); rhteRunfile = vrhte; } }
#pragma check_stack(off)
#endif
/********************************************************************
* PORTABILITY ROUTINES * ********************************************************************/
#if M_BYTESWAP
WORD getword(cp) /* Get a word given a pointer */ REGISTER char *cp; /* Pointer */ { return(B2W(cp[0]) + (B2W(cp[1]) << BYTELN)); /* Return 8086-style word */ }
DWORD getdword(cp)/* Get a double word given a pointer */ REGISTER char *cp; /* Pointer */ { return(getword(cp) + (getword(cp+2) << WORDLN)); /* Return 8086-style double word */ } #endif
#if NOT M_WORDSWAP OR M_BYTESWAP
/*
* Portable structure I/O routines */ #define cget(f) fgetc(f)
static int bswap; /* Byte-swapped mode (1 on; 0 off) */ static int wswap; /* Word-swapped mode (1 on; 0 off) */
static cput(c,f) char c; FILE *f; { #if FALSE AND OEXE
CheckSum(1, &c); #endif
fputc(c, f); }
static pshort(s,f) REGISTER short s; REGISTER FILE *f; { cput(s & 0xFF,f); /* Low byte */ cput(s >> 8,f); /* High byte */ }
static unsigned short gshort(f) REGISTER FILE *f; { REGISTER short s;
s = cget(f); /* Get low byte */ return(s + (cget(f) << 8)); /* Get high byte */ }
static pbshort(s,f) REGISTER short s; REGISTER FILE *f; { cput(s >> 8,f); /* High byte */ cput(s & 0xFF,f); /* Low byte */ }
static unsigned short gbshort(f) REGISTER FILE *f; { REGISTER short s;
s = cget(f) << 8; /* Get high byte */ return(s + cget(f)); /* Get low byte */ }
static int (*fpstab[2])() = { pshort, pbshort }; static unsigned short (*fgstab[2])() = { gshort, gbshort };
static plong(l,f) long l; REGISTER FILE *f; { (*fpstab[bswap])((short)(l >> 16),f); /* High word */ (*fpstab[bswap])((short) l,f); /* Low word */ }
static long glong(f) REGISTER FILE *f; { long l;
l = (long) (*fgstab[bswap])(f) << 16; /* Get high word */ return(l + (unsigned) (*fgstab[bswap])(f)); /* Get low word */ }
static pwlong(l,f) long l; REGISTER FILE *f; { (*fpstab[bswap])((short) l,f); /* Low word */ (*fpstab[bswap])((short)(l >> 16),f); /* High word */ }
static long gwlong(f) REGISTER FILE *f; { long l;
l = (unsigned) (*fgstab[bswap])(f); /* Get low word */ return(l + ((long) (*fgstab[bswap])(f) << 16)); /* Get high word */ }
static int (*fpltab[2])() = { plong, pwlong }; static long (*fgltab[2])() = { glong, gwlong };
/*
* int swrite(cp,dopevec,count,file) * char *cp; * char *dopevec; * int count; * FILE *file; * * Returns number of bytes written. * * Dopevec is a character string with the * following format: * * "[b][w][p]{[<cnt>]<type>}" * * where [...] denotes an optional part, {...} denotes a part * that may be repeated zero or more times, and <...> denotes * a description of a part. * * b bytes are "swapped" (not in PDP-11 order) * w words are swapped * p struct is "packed" (no padding for alignment) * <cnt> count of times to repeat following type * <type> one of the following: * c char * s short * l long * * Example: given the struct * * struct * { * short x; * short y; * char z[16]; * long w; * }; * * and assuming it is to be written so as to use VAX byte- and * word-ordering, its dope vector would be: * * "wss16cl" */
int swrite(cp,dopevec,count,file) char *cp; /* Pointer to struct array */ char *dopevec; /* Dope vector for struct */ int count; /* Number of structs in array */ FILE *file; /* File to write to */ { int pack; /* Packed flag */ int rpt; /* Repeat count */ REGISTER int cc = 0; /* Count of characters written */ REGISTER char *dv; /* Dope vector less flags */ short *sp; /* Pointer to short */ long *lp; /* Pointer to long */
bswap = wswap = pack = 0; /* Initialize flags */ while(*dopevec != '\0') /* Loop to set flags */ { if(*dopevec == 'b') bswap = 1; /* Check for byte-swapped flag */ else if(*dopevec == 'p') pack = 1; /* Check for packed flag */ else if(*dopevec == 'w') wswap = 1; /* Check for word-swapped flag */ else break; ++dopevec; } while(count-- > 0) /* Main loop */ { dv = dopevec; /* Initialize */ for(;;) /* Loop to write struct */ { if(*dv >= '0' && *dv <= '9') { /* If there is a repeat count */ rpt = 0; /* Initialize */ do /* Loop to get repeat count */ { rpt = rpt*10 + *dv++ - '0'; /* Take digit */ } while(*dv >= '0' && *dv <= '9'); /* Loop until non-digit found */ } else rpt = 1; /* Else repeat count defaults to one */ if(*dv == '\0') break; /* break if end of dope vector */ switch(*dv++) /* Switch on type character */ { case 'c': /* Character */ #if FALSE AND OEXE
CheckSum(rpt, cp); #endif
if(fwrite(cp,sizeof(char),rpt,file) != rpt) return(cc); /* Write the characters */ cp += rpt; /* Increment pointer */ cc += rpt; /* Increment count of bytes written */ break;
case 's': /* Short */ if(!pack && (cc & 1)) /* If not packed and misaligned */ { cput(*cp++,file); /* Write padding byte */ ++cc; /* Increment byte count */ } sp = (short *) cp; /* Initialize pointer */ while(rpt-- > 0) /* Loop to write shorts */ { (*fpstab[bswap])(*sp++,file); /* Write the short */ if(feof(file) || ferror(file)) return(cc); /* Check for errors */ cc += sizeof(short); /* Increment byte count */ } cp = (char *) sp; /* Update pointer */ break;
case 'l': /* Long */ if(!pack && (cc & 3)) /* If not packed and misaligned */ { while(cc & 3) /* While not aligned */ { cput(*cp++,file); /* Write padding byte */ ++cc; /* Increment byte count */ } } lp = (long *) cp; /* Initialize pointer */ while(rpt-- > 0) /* Loop to write longs */ { (*fpltab[wswap])(*lp++,file); /* Write the long */ if(feof(file) || ferror(file)) return(cc); /* Check for errors */ cc += sizeof(long); /* Increment byte count */ } cp = (char *) lp; /* Update pointer */ break; } } } return(cc); /* Return count of bytes written */ }
/*
* int sread(cp,dopevec,count,file) * char *cp; * char *dopevec; * int count; * FILE *file; * * Returns number of bytes read. * * Dopevec is a character string whose format is described * with swrite() above. */ int sread(cp,dopevec,count,file) char *cp; /* Pointer to struct array */ char *dopevec; /* Dope vector for struct */ int count; /* Number of structs in array */ FILE *file; /* File to read from */ { int pack; /* Packed flag */ int rpt; /* Repeat count */ REGISTER int cc = 0; /* Count of characters written */ REGISTER char *dv; /* Dope vector less flags */ short *sp; /* Pointer to short */ long *lp; /* Pointer to long */
bswap = wswap = pack = 0; /* Initialize flags */ while(*dopevec != '\0') /* Loop to set flags */ { if(*dopevec == 'b') bswap = 1; /* Check for byte-swapped flag */ else if(*dopevec == 'p') pack = 1; /* Check for packed flag */ else if(*dopevec == 'w') wswap = 1; /* Check for word-swapped flag */ else break; ++dopevec; } while(count-- > 0) /* Main loop */ { dv = dopevec; /* Initialize */ for(;;) /* Loop to write struct */ { if(*dv >= '0' && *dv <= '9') { /* If there is a repeat count */ rpt = 0; /* Initialize */ do /* Loop to get repeat count */ { rpt = rpt*10 + *dv++ - '0'; /* Take digit */ } while(*dv >= '0' && *dv <= '9'); /* Loop until non-digit found */ } else rpt = 1; /* Else repeat count defaults to one */ if(*dv == '\0') break; /* break if end of dope vector */ switch(*dv++) /* Switch on type character */ { case 'c': /* Character */ if(fread(cp,sizeof(char),rpt,file) != rpt) return(cc); /* Read the characters */ cp += rpt; /* Increment pointer */ cc += rpt; /* Increment count of bytes written */ break;
case 's': /* Short */ if(!pack && (cc & 1)) /* If not packed and misaligned */ { *cp ++ = cget(file); /* Read padding byte */ ++cc; /* Increment byte count */ } sp = (short *) cp; /* Initialize pointer */ while(rpt-- > 0) /* Loop to read shorts */ { *sp++ = (*fgstab[bswap])(file); /* Read the short */ if(feof(file) || ferror(file)) return(cc); /* Check for errors */ cc += sizeof(short); /* Increment byte count */ } cp = (char *) sp; /* Update pointer */ break;
case 'l': /* Long */ if(!pack && (cc & 3)) /* If not packed and misaligned */ { while(cc & 3) /* While not aligned */ { *cp++ = cget(file); /* Read padding byte */ ++cc; /* Increment byte count */ } } lp = (long *) cp; /* Initialize pointer */ while(rpt-- > 0) /* Loop to read longs */ { *lp++ = (*fgltab[wswap])(file); /* Read the long */ if(feof(file) || ferror(file)) return(cc); /* Check for errors */ cc += sizeof(long); /* Increment byte count */ } cp = (char *) lp; /* Update pointer */ break; } } } return(cc); /* Return count of bytes written */ } #endif
#define CB_POOL 4096
typedef struct _POOLBLK { struct _POOLBLK * pblkNext; // next pool in list
int cb; // number of bytes in this pool (free+alloc)
char rgb[1]; // data for this pool (variable sized)
} POOLBLK;
typedef struct _POOL { struct _POOLBLK * pblkHead; // start of poolblk list
struct _POOLBLK * pblkCur; // current poolblk we are searching
int cb; // # bytes free in current pool
char * pch; // pointer to free data in current pool
} POOL;
void * PInit() { POOL *ppool;
// create new pool, set size and allocate CB_POOL bytes
ppool = (POOL *)GetMem(sizeof(POOL)); ppool->pblkHead = (POOLBLK *)GetMem(sizeof(POOLBLK) + CB_POOL-1); ppool->pblkHead->cb = CB_POOL; ppool->pblkHead->pblkNext = NULL; ppool->cb = CB_POOL; ppool->pch = &ppool->pblkHead->rgb[0]; ppool->pblkCur = ppool->pblkHead;
return (void *)ppool; }
void * PAlloc(void *pp, int cb) { POOL *ppool = (POOL *)pp; void *pchRet; POOLBLK *pblkCur, *pblkNext;
// if the allocation doesn't fit in the current block
if (cb > ppool->cb) { pblkCur = ppool->pblkCur; pblkNext = pblkCur->pblkNext;
// then check the next block
if (pblkNext && pblkNext->cb >= cb) { // set the master info to reflect the next page...
ppool->pblkCur = pblkNext; ppool->cb = pblkNext->cb; ppool->pch = &pblkNext->rgb[0]; memset(ppool->pch, 0, ppool->cb); } else { POOLBLK *pblkNew; // new pool
// allocate new memory -- at least enough for this allocation
pblkNew = (POOLBLK *)GetMem(sizeof(POOLBLK)+cb+CB_POOL-1); pblkNew->cb = CB_POOL + cb;
// link the current page to the new page
pblkNew->pblkNext = pblkNext; pblkCur->pblkNext = pblkNew;
// set the master info to reflect the new page...
ppool->pblkCur = pblkNew; ppool->cb = CB_POOL + cb; ppool->pch = &pblkNew->rgb[0]; }
}
pchRet = (void *)ppool->pch; ppool->pch += cb; ppool->cb -= cb; return pchRet; }
void PFree(void *pp) { POOL *ppool = (POOL *)pp; POOLBLK *pblk = ppool->pblkHead; POOLBLK *pblkNext;
while (pblk) { pblkNext = pblk->pblkNext; FFREE(pblk); pblk = pblkNext; }
FFREE(ppool); }
void PReinit(void *pp) { POOL *ppool = (POOL *)pp;
ppool->pblkCur = ppool->pblkHead; ppool->cb = ppool->pblkHead->cb; ppool->pch = &ppool->pblkHead->rgb[0];
memset(ppool->pch, 0, ppool->cb); }
#if RGMI_IN_PLACE
/****************************************************************
* * * PchSegAddress: * * * * compute the address that will hold this data so we can read * * it in place... we make sure that we can read in place at * * and give errors as in MoveToVm if we cannot * * * * Input: cb Count of bytes to be moved. * * seg Logical segment to which data belongs. * * ra Offset at which data belongs. * * * ****************************************************************/
BYTE FAR * PchSegAddress(WORD cb, SEGTYPE seg, RATYPE ra) { long cbtot; /* Count of bytes total */ long cbSeg; /* Segment size */ WORD fError; BYTE FAR *pMemImage; CVINFO FAR *pCVInfo; SATYPE sa;
cbtot = (long) cb + ra;
if (fDebSeg) { pCVInfo = ((APROPFILEPTR ) FetchSym(vrpropFile, FALSE))->af_cvInfo; if (pCVInfo) { if (seg < (SEGTYPE) (segDebFirst + ObjDebTotal)) { cbSeg = pCVInfo->cv_cbTyp; pMemImage = pCVInfo->cv_typ;
if (!pMemImage) pCVInfo->cv_typ = pMemImage = GetMem(cbSeg); } else { cbSeg = pCVInfo->cv_cbSym; pMemImage = pCVInfo->cv_sym;
if (!pMemImage) pCVInfo->cv_sym = pMemImage = GetMem(cbSeg); }
// Check against segment bounds
fError = cbtot > cbSeg; } else { OutError(ER_badcvseg); return NULL; } } else { if (fNewExe) { cbSeg = ((APROPSNPTR) FetchSym(mpgsnrprop[vgsnCur],FALSE))->as_cbMx; sa = mpsegsa[seg]; if (mpsaMem[sa] == NULL) mpsaMem[sa] = (BYTE FAR *) GetMem(mpsacb[sa]); pMemImage = mpsaMem[sa];
// Check against segment bounds
fError = (long) ((ra - mpgsndra[vgsnCur]) + cb) > cbSeg; } else { cbSeg = mpsegcb[seg] + mpsegraFirst[seg]; if (mpsegMem[seg] == NULL) mpsegMem[seg] = (BYTE FAR *) GetMem(cbSeg); pMemImage = mpsegMem[seg];
// Check against segment bounds
fError = cbtot > cbSeg; } }
if (fError) { if (!fDebSeg) OutError(ER_segbnd, 1 + GetFarSb(GetHte(mpgsnrprop[vgsnCur])->cch)); else OutError(ER_badcvseg); }
return (pMemImage + ra); }
#endif
#if USE_REAL
// Indicates if you are running under TNT.
// If it returns FALSE today, you are running on NT.
int IsDosxnt ( ) {
#if defined( _WIN32 )
return FALSE; #else
HINSTANCE hLib = GetModuleHandle("kernel32.dll"); if ( hLib != 0 && (GetProcAddress(hLib, "IsTNT") != 0)) { return(TRUE); } else { return(FALSE); } #endif
}
// Are we running on Win31 or greater.
// Note that we know if we are running under Windows we are running in enhanced mode.
int IsWin31() { #if defined( _WIN32 )
return FALSE; #else
__asm { mov ax,1600h ; Is Win31 or greater running int 2fh cmp al,03h ; Is major version number 3.0 jb NotWin31 ; Major version less than 3.0 ja ItIsWin31 cmp ah,0ah ; Is minor version atleast .10 jb NotWin31 ; Must be Win3.0 } ItIsWin31: return (TRUE); NotWin31: return (FALSE); #endif // NOT _WIN32
} int MakeConvMemPageable ( ) { #if defined( _WIN32 )
return TRUE; #else
if ( realModeMemPageable ) { return ( TRUE ); // Somebody already freed the real mode mem.
} __asm { mov ax,0100h ; function to get DOS memory. mov bx,TOTAL_CONV_MEM ; Ask for 1 M to get max memory count int 31h
jnc errOut ; allocated 1 M - something must be wrong.
cmp ax,08h ; Did we fail because of not enough memory jne errOut ; No we failed because of some other reason. cmp bx,MIN_CONV_MEM ; See if we can allocate atleast the min
// We could fail for two reasons here .
// 1) we really didn't have sufficient memory.
// 2) Some TNT app that spawned us already unlocked this memory. For ex:
// cl might have already freed up the memory when it calls link.exe.
jb errOut ; Too little mem available don't bother.
sub bx,CONV_MEM_FOR_TNT ; Leave real mode mem for TNT. mov ax,0100h ; Try again with new amount of memory int 31h ; Ask for the real mode memory from DPMI. jc errOut ; didn't succeed again, give up.
mov convMemSelector,dx ; Save the value of the selector for allocated block mov noOfParagraphs,bx ; amount of memory we were able to allocate.
mov ax,0006h ; function to get base addr of a selector mov bx,dx ; move the selector to bx int 31h ; Get Segment Base Address jc errOut ;
mov bx,cx ; mov lin addr from cx:dx to bx:cx mov cx,dx ;
movzx eax,noOfParagraphs shl eax,4 ; Multiply by 16 to get count in bytes. mov di,ax ; transfer size to si:di from eax shr eax,16 ; mov si,ax ; mov ax,602h ; Make real mode memory pageable int 31h
jc errOut ; Didn't work.
mov ax,703h ; Indicate data in these pages is discardable. int 31h // Even if we fail this call, we will still assume we are succesful,
// because it is just a performance enhancement
// Also for correctness we should relock the memory once it is free.
} realModeMemPageable = TRUE ; errOut: return(realModeMemPageable); #endif // NOT _WIN32
}
/* Relock the real mode memory now */
int RelockConvMem ( void ) { #if defined( _WIN32 )
return TRUE; #else
if ( !realModeMemPageable ) { return ( TRUE ); // We were never able to free the mem anyway.
} __asm { mov bx, convMemSelector mov ax, 0006h int 31h ; Get Segment Base Address. jc errOut ;
mov bx,cx ; Mov lin addr from cx:dx to bx:cx mov cx,dx
movzx eax,noOfParagraphs shl eax,4 ; Mul paragraphs by 16 to get count in bytes. mov di,ax ;Transfer size to si:di from eax. shr eax,16 mov si,ax
mov ax,603h ; Relock real mode region int 31h jc errOut
mov dx,convMemSelector mov ax,101h ; Free the real mode memory int 31h jc errOut } realModeMemPageable = FALSE ; return ( TRUE ); errOut: return ( FALSE ); #endif // NOT _WIN32
}
void RealMemExit(void) { if(fUseReal) { if(!RelockConvMem()) OutError(ER_membad); fUseReal = FALSE; } } #endif
|