windows-nt/Source/XPSP1/NT/sdktools/link16/newutl.c

1680 lines
51 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
/* 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