_ARRAY BOUNDS CHECKING WITH TURBO C_ by Glenn Pearson [LISTING ONE] void *p; void *q; /* Make life easier when dealing with segment and offset pointers. */ static union {void *A; struct {unsigned Offset,Segment;} Word; } LrgPtr; p = malloc(bytes); if (p EQ NULL) error("pmalloc: Out of memory space on heap"); LrgPtr.A = q = createBoundedWindow(p,bytes); /* Lower 3 bits are ALWAYS ON; so we can restore them later: */ /* Shift to make array smaller, and its length <= LDTSIZE */ /* Remember the p-q correspondence: */ malloclist[(LrgPtr.Word.Segment & ~7) >> 3] = p; return(q); /* q's Offset is zero */ } /*=====================================*/ void pfree(void *q) {unsigned i; /* Make life easier when dealing with segment and offset pointers. */ static union {void *A; struct {unsigned Offset,Segment;} Word; } LrgPtr; if (q EQ NULL) return; LrgPtr.A = q; if (LrgPtr.Word.Offset != 0) error("pfree: Attempt to free improper selector"); /* Lower 3 bits of a selector are ALWAYS ON: */ i = (LrgPtr.Word.Segment & ~7) >> 3; if (malloclist[i] == NULL) error("pfree: Attempt to free unknown window"); deleteSegOrWin(q); /* First remove bounds-checking window */ free(malloclist[i]); /* Then free memory */ malloclist[i] = NULL; /* Prevent screw ups */ } [LISTING TWO] /* * Hardware-assisted Bounds Checking of Dynamic Arrays and Structures: * Dynamically allocated arrays and structures do not automatically get a private * sel with Turboc C 2.0/Ergo (or Microsoft C/Ergo). To guarantee a private * sel, this routine sets what Ergo calls a "window" over the array. The private * sel is then used for subsequent array accesses, so that bounds checking * is performed with no run-time overhead (because the chip hardware does it). By * building this functionality into the "pmalloc" (and calloc, etc.) and "pfree" * and "pfarfree" routines, all non-static arrays get this ability. * * NOTE 1: Code shown is for the far memory model, so all pointers are far; * Additional explicit casting will be needed for smaller models. * NOTE 2: If using Turbo C huge model with osx86, any function call that * passes the address of an automatic (stack) variable must have that * address explicitly normalized first. Recommendation: use Microsoft C instead. * ******************************************************************************* * --- The following bounds-related utility functions are currently private to * --- this file, but could be made public as needed: * errorstrcat() * deleteSegOrWin() * createDataWindow() * allocateMultipleWindows() * deleteMultipleWindows() * createBoundedWindow() * checksize() * markPtoQ() * --- The following routines provide runtime bounds checking for * --- protected mode heap memory allocation. Each routine has the * --- same syntax as the corresponding Turbo C 2.0 call (except "p" prefix)... * pmalloc() * pcalloc() * prealloc() * pfarmalloc() * pfarcalloc() * pfarrealloc() * pfree() * pfarfree() ******************************************************************************/ #include #include #include /**********************FILE GLOBALS********************************************/ #define private static #define forward extern #define import extern #define export #define uint16 unsigned short #define uint32 unsigned long int #define int16 short #define int32 long int #define EQ == #define ERRORSTRCAT #define ERROR(A,B) errorstrcat(A,B) #define ERROUT(A) {printf("Fatal Error %s.\n",A);exit(-1);} /* Compile time debug tracing */ /* #define TRACE(A) A */ #define TRACE(A) #define LDTSIZE 1024 /* Reserve arbitary slot space at extreme of LDT table for Ergo work space, such as extra windows sometimes needed for malloc */ #define MAXLDTNUM (LDTSIZE-25) /* Assert: MAXLDTNUM <= LDTSIZE */ #define MAXSEGNUM ((MAXLDTNUM << 3) & 7) /* Eclipse's default LDT size (in units of number of entries) is 1024. The low end of the LDT will already be taken up by code and data sels, before any dynamic data allocation occurs. If the program doesn't do much mallocing, the default may be sufficient. Otherwise, you take a generous guess as to the maximum number of memory requests active at any time, round up to the next power of 2, and set LDTSIZE to that value. However, a 286 machine can have at most 8192 entries in its Local Descriptor Table. Thus, a program that has, say, 10,000 simultaneously active dynamic arrays would have to use a more selective method of assigning private sels than simply universal use of "pmalloc" and "pfree". LDTSIZE should match the value stored in the os286 kernel. A larger LDT is requested by modifying the kernel (the developer's on-disk .EXE file) by specifying, for example: 286setup ldtsize 2048 Valid ldtsize values range from 128 to 8192. This kernel can be subsequently bound in with the distributed program. The current value of ldtsize (and all other settable parameters) can be viewed by: 286setup -help Alternatively, for unbound applications, one may specify the ldtsize on the command line during loading of the kernel TSR: os286 ldtsize 2048 This would typically be part of a batch file. Here, the load image of the kernel is altered, not the disk file, so the change is not "sticky". */ void *malloclist[LDTSIZE]; /* K&R promises all values are initially zero (i.e., NULL) */ /* For createBoundedWindow funtion: */ #define SEGMENT 0 #define WINDOW 1 #define REALSEGMENT 2 #define REALWINDOW 3 /***************************************************************************/ #ifdef ERRORSTRCAT /*======================================================================== */ void errorstrcat(char *stringA, char *stringB) {char sss[90]; strcpy(sss,"In "); strcat(sss,stringA); strcat(sss,", "); strcat(sss,stringB); gxerror(sss); } #endif /*======================================================================= */ void private deleteSegOrWin(void *selector) /*--------------------------------------------------------------------------- * Call to DOS delete-segment service, extended by Ergo to include * memory "windows" as well. (Note: Turbo C's free & farfree DO NOT * use this call). Memory must have been allocated by DOS interrrupts * 0x48, 0xe7, or 0xe8; 0xe8 is used by createDataWindow routine here. * Deleting a window also deletes any child windows. *------------------------------------------------------------------------- */ { /* regs, sregs, and LrgPtr are declared static to put them into common DATA segment */ static union REGS regs; static struct SREGS sregs; /* Make life easier when dealing with segment and offset pointers. */ static union {void *A; struct {uint16 Offset,Segment;} Word; } LrgPtr; LrgPtr.A = selector; sregs.es = LrgPtr.Word.Segment; /* Offset doesn't matter */ regs.h.ah = 0x49; /* Delete segment or window */ intdosx(®s, ®s, &sregs); if (regs.h.al EQ 7) ERROR("deleteSegOrWin","Delete Segment; Bad Memory Map"); if (regs.h.al EQ 9) ERROR("deleteSegOrWin","Delete Segment; Bad Selector"); } /* ==================================================================== */ void private *createDataWindow(char *base, uint32 length) /*--------------------------------------------------------------------------- "createDataWindow" is a call to an Eclipse "extended service" routine, accessed through software interrupt E8, Function 01. ----------------------------------------------------------------------------*/ { /* regs, sregs, and LrgPtr are declared static to put them into common DATA segment */ static union REGS regs; static struct SREGS sregs; /* Make life easier when dealing with segment and offset pointers. */ static union {void *A; struct {uint16 Offset,Segment;} Word; } LrgPtr; regs.h.ah = 0xe8; regs.h.al=1; /* Create Data window */ /* si:bx=base; ds=parent selector */ LrgPtr.A = base; sregs.ds = LrgPtr.Word.Segment; regs.x.si = 0L; /* parent selector takes care of high-order base */ regs.x.bx = LrgPtr.Word.Offset; regs.x.cx = (uint16)((length >> 16) & 0x0000ffff); /* cx:dx=length in bytes */ regs.x.dx = (uint16)(length & 0x0000ffff); intdosx(®s,®s,&sregs); LrgPtr.Word.Segment = regs.x.ax; /* Selector or error */ LrgPtr.Word.Offset = 0; if (LrgPtr.Word.Segment > MAXSEGNUM) {/* We reserve a little work space at extreme of LDT table; if we've encrouched upon it, back off: */ deleteSegOrWin(LrgPtr.A); /* Table is "full" as far as we're concerned */ LrgPtr.Word.Segment = regs.x.ax = 21U; } if (regs.x.ax > 26U || regs.x.ax == 21U) return (LrgPtr.A); /* Let caller handle "Descriptor Table Full" error */ switch (regs.x.ax) { case 9U: ERROR("createDataWindow","Memory allocation; Bad Selector"); break; case 20U: ERROR("createDataWindow","Memory allocation; Bad Type"); break; /* case 21U: Handled by caller... ERROR("createDataWindow","Memory allocation; Descriptor Table Full"); break; */ case 23U: ERROR("createDataWindow","Memory allocation; Need Local Descriptor"); break; case 25U: ERROR("createDataWindow","Memory allocation; Bad Base"); break; case 26U: ERROR("createDataWindow","Memory allocation; Bad Size"); break; default: ERROR("createDataWindow","Memory allocation; Unknown Error");break; } return(NULL); /* should never get here; quiet compiler warning */ } /* =====================================================================*/ void private deleteMultipleWindows(char *base, uint16 count) /*----------------------------------------------------------------------- * Called with the pointer representing the first (lowest LDT) selector * in a consecutive set of selectors to be deleted, and the count * of the number of selectors in the set. * See deleteSegOrWin for possible errors. *----------------------------------------------------------------------*/ { int i; /* Make life easier when dealing with segment and offset pointers. */ static union {void *A; struct {uint16 Offset,Segment;} Word; } LrgPtr; if (count == 0) ERROR("deleteMultipleWindows","zero count not valid"); LrgPtr.A = base; /* Undocumented Ergo recommendation for tiled windows: delete from highest to lowest. 8 is increment from one LDT entry to next: */ LrgPtr.Word.Segment += (count - 1) * 8; for (i=0; i < count; i++) { deleteSegOrWin(LrgPtr.A); LrgPtr.Word.Segment -= 8; } } /* ==================================================================== */ void private *allocateMultipleWindows(char *base, uint32 length) /*--------------------------------------------------------------------------- "allocateMultipleWindows" is a call to an Eclipse "extended service" routine, accessed through software interrupt 0xEA. It creates tiled windows, using 32K tiles. The pointer for the first tile is returned. (The total number of tiles created is also known, but not returned to the caller at this time.) ----------------------------------------------------------------------------*/ { uint16 numSelectors; /* regs, sregs, and LrgPtr are declared static to put them into common DATA segment */ static union REGS regs; static struct SREGS sregs; /* Make life easier when dealing with segment and offset pointers. */ static union {void *A; struct {uint16 Offset,Segment;} Word; } LrgPtr; regs.h.ah = 0xea; /* Allocate multiple windows */ /* si:bx and cx:dx are 32-bit offsets, not a paragraph address & offset */ /* si:bx = stride in bytes = 32K (32K makes for fast math, 64K has problems with Turbo C) */ /* Maximum legal value is 64K (si = 1, bx = 0) */ regs.x.si = 0; regs.x.bx = 0x8000; /* 32K */ /* ds is parent selector */ LrgPtr.A = base; sregs.ds = LrgPtr.Word.Segment; /*(uint16)((((uint32)base) >> 16) & 0x0000ffff);*/ /* cx:dx=length in bytes: */ regs.x.cx = (uint16)((length >> 16) & 0x0000ffff); regs.x.dx = (uint16)(length & 0x0000ffff); intdosx(®s,®s,&sregs); LrgPtr.Word.Segment = regs.x.ax; /* Selector or error */ LrgPtr.Word.Offset = 0; if (regs.x.ax == 21U) return (LrgPtr.A); /* Let caller handle "Descriptor Table Full" error */ numSelectors = regs.x.bx; /* 8 is increment between successive LDT entries: */ if ((LrgPtr.Word.Segment + ((numSelectors-1)*8) ) > MAXSEGNUM) {/* We reserve a little work space at extreme of LDT table; if we've encrouched upon it, back off: */ deleteMultipleWindows(LrgPtr.A,numSelectors); /* Table is "full" as far as we're concerned */ LrgPtr.Word.Segment = 21U; return (LrgPtr.A); /* Let caller handle "Descriptor Table Full" error */ } if (regs.x.ax > 26U) return (LrgPtr.A); /* Everything OK */ switch (regs.x.ax) { case 9U: ERROR("allocateMultipleWindows","Memory allocation; Bad Selector"); break; /* case 21U: Handled by caller... ERROR("allocateMultipleWindows","Memory allocation; Descriptor Table Full"); break; */ case 23U: ERROR("allocateMultipleWindows","Memory allocation; Need Local Descriptor"); break; case 27U: ERROR("allocateMultipleWindows","Memory allocation; Bad Stride"); break; } return(NULL); /* should never get here; quiet compiler warning */ } /* ==================================================================== */ void private *createBoundedWindow(char *base, uint32 length) /*--------------------------------------------------------------------------- "createBoundedWindow" is used with the "pmalloc" and "pfree" routines for adding bounds-checking to dynamically-allocated objects. It features an embedded loop of calls to Ergo's "extended DOS" function 0xED, "Get Segment or Window Information". Besided dynamic allocation checking, this function can also be used for bounds-checking of static arrays and structures, such as constant strings. There is no centralized mechanism (see article). ----------------------------------------------------------------------------*/ { /* regs, sregs, and LrgPtr are declared static to put them into common DATA segment */ static union REGS regs; /* Make life easier when dealing with segment and offset pointers. */ static union {void *A; struct {uint16 Offset,Segment;} Word; } LrgPtr; uint16 type; uint32 offset = 0; /* Offset will accumulate total offset */ TRACE(printf("Call to createBoundedWindow\n");) /* Start with initial selector */ LrgPtr.A = base; regs.x.bx = LrgPtr.Word.Segment; /* Work up the inheritence tree until a non-WINDOW is found: */ while (TRUE) { /* Next line must be within this loop, since ah is not preserved across the subsequent intdos call: */ regs.h.ah = 0xed; /* Call to Get segment or window information */ intdos(®s,®s); switch (regs.h.al) { case 9U: ERROR("createBoundedWindow","Memory allocation; Bad Selector");break; case 23U: ERROR("createBoundedWindow","Memory allocation; Need Local Descriptor"); break; } type = regs.h.al; if (type == REALSEGMENT OR type == REALWINDOW) ERROR("createBoundedWindow","Real segment or window found"); /* if ERROR, we will not continue here */ /* Assert: type is either SEGMENT or WINDOW */ /* Length in bytes = cx:dx */ length = (((uint32)regs.x.cx) << 16) + (uint32)regs.x.dx; if (type != WINDOW) break; /* from while */ /* For WINDOW type, di has it's parent selector: */ regs.x.bx = regs.x.di; /* For WINDOW type, si:bx is the 32 bit offset within the parent: */ offset += (((uint32)regs.x.si) << 16) + (uint32)regs.x.bx; } /* For SEGMENT type, si:bx is the 32-bit linear address of the base of the segment. */ /* Add accumulated offset as well */ base = (char *)((uint32)regs.x.si << 16) + (uint32)regs.x.bx + offset; /* Now we have the underlying selector:offset location; next, build a new window of just the right size on top of it. */ if (length > 0x0000ffff) /* If length is greater than 64K, use tiling with 32K windows */ return(allocateMultipleWindows(base,length)); else return(createDataWindow(base,length)); } /*===========================================================================*/ void private checksize(uint32 size) /*---------------------------------------------------------------------------- * Unfortunately, Turbo C/Ergo's version of malloc ( & calloc & realloc) * delivers a block with a header of 8 bytes (i.e., returns with an offset of * 0x0008), so the full 64K is not available. If we don't check for this, * block descriptor header could be overwritten... disaster! -----------------------------------------------------------------------------*/ { if (size > 0x0000fff7) ERROR("checksize","Attempt to allocate more than 64K - 8 bytes"); } /*========================================================================*/ void private markPtoQ (void *p, uint32 bytes, void *q) /*-------------------------------------------------------------------------- * This utility routine is used by the allocators to mark the * correspondence between p and q *-------------------------------------------------------------------------*/ { /* Make life easier when dealing with segment and offset pointers. */ static union {void *A; struct {uint16 Offset,Segment;} Word; } LrgPtr; void *ptile; uint32 qspot; /* 32K: */ #define TILESIZE 0x00010000 LrgPtr.A = q; /* Lower 3 bits are ALWAYS ON; so we can restore them later: */ /* Shift to make array smaller, and its length <= LDTSIZE */ qspot = (LrgPtr.Word.Segment & ~7) >> 3; if (bytes > 0x0000ffff) { /* Tiling is indicated by multiple adjacent entries with the same value of p: */ while (bytes > TILESIZE) { malloclist[qspot++] = p; /* Remember the p-q correspondence*/ bytes = bytes - TILESIZE; } /* Conclude with last partial tile below */ } malloclist[qspot] = p; /* Remember the p-q correspondence*/ TRACE(printf("index: %u ",(LrgPtr.Word.Segment & ~7) >> 3);) TRACE(printf("returns 0x%lx\n",q);) } /* =========================================================================*/ void *pmalloc(uint16 bytes) { void *p; void *p2; void *q; /* Make life easier when dealing with segment and offset pointers. */ static union {void *A; struct {uint16 Offset,Segment;} Word; } LrgPtr; TRACE(printf("[pmalloc] want: %u, ",bytes);) checksize((uint32)bytes); p = malloc(bytes); if (p EQ NULL) ERROR("pmalloc","Out of memory space on heap"); LrgPtr.A = q = createBoundedWindow(p,bytes); if (LrgPtr.Word.Segment == 21U) {/* The local descriptor table is full; "fail soft" by foregoing the pleasure of bounds checking. When freeing occurs, the decision about whether the pointer refers to a bounds-checking data window like q or just a direct alloc like p is made by viewing the offset. A data window always has a zero offset. The odds are 1 in 64K that p has a zero offset. */ if (((uint32)p & 0x0000ffff) == 0) {/* We have to make sure that p doesn't have a zero offset! */ p2 = malloc((uint16)(bytes & 0x0000ffff)); /* If we don't succeed next time, periodicity suggests we won't succeed n times. Just complain and fail */ if (((uint32)p2 & 0x0000ffff) == 0) ERROR("pmalloc","Unlikely memory error"); free(p); p = p2; } TRACE(printf("returns 0x%lx\n",p);) return(p); } markPtoQ(p, (uint32)bytes, q); return(q); } /* =========================================================================*/ void *pcalloc(uint16 nitems, uint16 bytes) { void *p; void *p2; void *q; /* Make life easier when dealing with segment and offset pointers. */ static union {void *A; struct {uint16 Offset,Segment;} Word; } LrgPtr; TRACE(printf("[pcalloc] want: %u * %u = %lu, ", nitems,bytes,(uint32)nitems*(uint32)bytes);) checksize((uint32)nitems*(uint32)bytes); /*Could be way over 64K - 8 bytes */ p = calloc(nitems,bytes); if (p EQ NULL) ERROR("pcalloc","Out of memory space on heap"); LrgPtr.A = q = createBoundedWindow(p,(uint32)nitems*(uint32)bytes); if (LrgPtr.Word.Segment == 21U) {/* The local descriptor table is full; "fail soft" by foregoing the pleasure of bounds checking. When freeing occurs, the decision about whether the pointer refers to a bounds-checking data window like q or just a direct alloc like p is made by viewing the offset. A data window always has a zero offset. The odds are 1 in 64K that p has a zero offset. */ if (((uint32)p & 0x0000ffff) == 0) {/* We have to make sure that p doesn't have a zero offset! */ p2 = calloc(nitems,bytes); /* If we don't succeed next time, periodicity suggests we won't succeed n times. Just complain and fail */ if (((uint32)p2 & 0x0000ffff) == 0) ERROR("pcalloc","Unlikely memory error"); free(p); p = p2; } TRACE(printf("returns 0x%lx\n",p);) return(p); } markPtoQ(p, (uint32)bytes, q); return(q); } /*=========================================================================*/ void *prealloc(void *block, uint16 newsize) { void *p; void *p2; void *q; int16 i; /* Make life easier when dealing with segment and offset pointers. */ static union {void *A; struct {uint16 Offset,Segment;} Word; } LrgPtr; TRACE(printf(" [prealloc] with: 0xlx, want: %u, ",block,newsize);) if (block == NULL) return(NULL); checksize((uint32)newsize); LrgPtr.A = block; if (LrgPtr.Word.Offset != 0) /* Assume no bounds protection for this allocation */ return(realloc(block,newsize)); /* Lower 3 bits of a selector are ALWAYS ON: */ i = (LrgPtr.Word.Segment & ~7) >> 3; TRACE(printf("index: %u ",i);) if (malloclist[i] == NULL) ERROR("prealloc","Attempt to reallocate unknown window"); if (newsize == 0) {deleteSegOrWin(block); /* First remove bounds-checking window */ realloc(malloclist[i],newsize); /* Should return NULL */ malloclist[i] = NULL; /* Prevent screw ups */ TRACE(printf("returns NULL\n");) return(NULL); } p = realloc(malloclist[i],newsize); /* adjust memory */ if (p == NULL) {TRACE(printf("returns NULL\n");) return(NULL); /* Couldn't do it */ } malloclist[i] = NULL; /* Prevent screw ups */ /* Since realloc may change pointer location as well as size, we'll just throw away the old bounds window and get a new one */ deleteSegOrWin(block); LrgPtr.A = q = createBoundedWindow(p,newsize); if (LrgPtr.Word.Segment == 21U) {/* The local descriptor table is full; "fail soft" by foregoing the pleasure of bounds checking. When freeing occurs, the decision about whether the pointer refers to a bounds-checking data window like q or just a direct alloc like p is made by viewing the offset. A data window always has a zero offset. The odds are 1 in 64K that p has a zero offset. */ if (((uint32)p & 0x0000ffff) == 0) {/* We have to make sure that p doesn't have a zero offset! */ p2 = malloc(newsize); /* If we don't succeed next time, periodicity suggests we won't succeed n times. Just complain and fail */ if (((uint32)p2 & 0x0000ffff) == 0) ERROR("prealloc","Unlikely memory error"); free(p); p = p2; } TRACE(printf("returns 0x%lx\n",p);) return(p); } markPtoQ(p, (uint32)newsize, q); return(q); } /*==========================================================================*/ void *pfarmalloc(uint32 bytes) { void *p; void *p2; void *q; /* Make life easier when dealing with segment and offset pointers. */ static union {void *A; struct {uint16 Offset,Segment;} Word; } LrgPtr; TRACE(printf("[pfarmalloc] want: %lu, ",bytes);) p = farmalloc(bytes); if (p EQ NULL) ERROR("pfarmalloc","Out of memory space on heap"); LrgPtr.A = q = createBoundedWindow(p,bytes); if (LrgPtr.Word.Segment == 21U) {/* The local descriptor table is full; "fail soft" by foregoing the pleasure of bounds checking. When freeing occurs, the decision about whether the pointer refers to a bounds-checking data window like q or just a direct alloc like p is made by viewing the offset. A data window always has a zero offset. The odds are 1 in 64K that p has a zero offset. */ if (((uint32)p & 0x0000ffff) == 0) {/* We have to make sure that p doesn't have a zero offset! */ p2 = farmalloc(bytes); /* If we don't succeed next time, periodicity suggests we won't succeed n times. Just complain and fail */ if (((uint32)p2 & 0x0000ffff) == 0) ERROR("pfarmalloc","Unlikely memory error"); free(p); p = p2; } TRACE(printf("returns 0x%lx\n",p);) return(p); } markPtoQ(p, bytes, q); return(q); } /*==========================================================================*/ void *pfarcalloc(uint32 nitems, uint32 bytes) { void *p; void *p2; void *q; /* Make life easier when dealing with segment and offset pointers. */ static union {void *A; struct {uint16 Offset,Segment;} Word; } LrgPtr; TRACE(printf("[pfarcalloc] want: %lu * %lu = %lu, ",nitems,bytes,nitems*bytes);) /* We won't check to see if (nitems * bytes) overflows uint32; we'll let farcalloc do that job */ p = farcalloc(nitems, bytes); if (p EQ NULL) ERROR("pfarcalloc","Out of memory space on heap"); LrgPtr.A = q = createBoundedWindow(p,nitems*bytes); if (LrgPtr.Word.Segment == 21U) {/* The local descriptor table is full; "fail soft" by foregoing the pleasure of bounds checking. When freeing occurs, the decision about whether the pointer refers to a bounds-checking data window like q or just a direct alloc like p is made by viewing the offset. A data window always has a zero offset. The odds are 1 in 64K that p has a zero offset. */ if (((uint32)p & 0x0000ffff) == 0) {/* We have to make sure that p doesn't have a zero offset! */ p2 = farcalloc(nitems, bytes); /* If we don't succeed next time, periodicity suggests we won't succeed n times. Just complain and fail */ if (((uint32)p2 & 0x0000ffff) == 0) ERROR("pfarmalloc","Unlikely memory error"); free(p); p = p2; } TRACE(printf("returns 0x%lx\n",p);) return(p); } markPtoQ(p, bytes, q); return(q); } /*=========================================================================*/ void *pfarrealloc(void *block, uint32 newsize) { void *p; void *p2; void *q; int16 i; /* Make life easier when dealing with segment and offset pointers. */ static union {void *A; struct {uint16 Offset,Segment;} Word; } LrgPtr; TRACE(printf("[pfarrealloc] with 0x%lx, want: %lu, ",block, newsize);) if (block EQ NULL) return(NULL); LrgPtr.A = block; if (LrgPtr.Word.Offset != 0) /* Assume no bounds protection for this allocation */ return(farrealloc(block,newsize)); /* Lower 3 bits of a selector are ALWAYS ON: */ i = (LrgPtr.Word.Segment & ~7) >> 3; TRACE(printf("index: %u ",i);) if (malloclist[i] == NULL) ERROR("pfarrealloc","Attempt to reallocate unknown window"); if (newsize == 0) {deleteSegOrWin(block); /* First remove bounds-checking window */ farrealloc(malloclist[i],newsize); /* Should return NULL */ malloclist[i] = NULL; /* Prevent screw ups */ TRACE(printf("returns NULL\n");) return(NULL); } p = (void *)farrealloc(malloclist[i],newsize); /* adjust memory */ if (p == NULL) {TRACE(printf("returns NULL\n");) return(NULL); /* Couldn't do it */ } malloclist[i] = NULL; /* Prevent screw ups */ /* Since realloc may change pointer location as well as size, we'll just throw away the old bounds window and get a new one */ deleteSegOrWin(block); LrgPtr.A = q = createBoundedWindow(p,newsize); if (LrgPtr.Word.Segment == 21U) {/* The local descriptor table is full; "fail soft" by foregoing the pleasure of bounds checking. When freeing occurs, the decision about whether the pointer refers to a bounds-checking data window like q or just a direct alloc like p is made by viewing the offset. A data window always has a zero offset. The odds are 1 in 64K that p has a zero offset. */ if (((int32)p & 0x0000ffff) == 0) {/* We have to make sure that p doesn't have a zero offset! */ p2 = farmalloc(newsize); /* If we don't succeed next time, periodicity suggests we won't succeed n times. Just complain and fail */ if (((int32)p2 & 0x0000ffff) == 0) ERROR("pfarrealloc","Unlikely memory error"); free(p); p = p2; } TRACE(printf("returns 0x%lx\n",p);) return(p); } markPtoQ(p, newsize, q); return(q); } /*===========================================================================*/ void pfree(void *q) /*---------------------------------------------------------------------------- "pfree" is called with a selector q, which is either an Ergo data window pointer used for bounds checking, or (less routinely) simply the pointer returned directly by malloc. Examining the low 16 bits determines which. For a window, we look up the stored value of the corresponding malloc pointer (which is a selector, not the real physical address), and q's slot in the Local Descriptor Table is freed for use by subsequent allocations. In any event, Turbo C's "free" is called with the malloc pointer. --------------------------------------------------------------------------- */ {uint16 i; void *p; /* Make life easier when dealing with segment and offset pointers. */ static union {void *A; struct {uint16 Offset,Segment;} Word; } LrgPtr; TRACE(printf("[pfree] with: 0x%lx, ",q);) if (q EQ NULL) return; LrgPtr.A = q; if (LrgPtr.Word.Offset != 0) /* Assume no bounds protection for this allocation */ {free(q); /* free memory */ TRACE(printf("\n");) return; } /* Lower 3 bits of a selector are ALWAYS ON: */ i = (LrgPtr.Word.Segment & ~7) >> 3; TRACE(printf("index: %u\n",i);) if ((p = malloclist[i]) == NULL) ERROR("pfree","Attempt to free unknown window"); /* First remove bounds-checking window(s): */ while (p == malloclist[i]) { deleteSegOrWin(LrgPtr.A); LrgPtr.Word.Segment++; /* q may be made of multiple tiles */ malloclist[i++] = NULL; /* Prevent screw ups */ } /* Turbo C's "free" call does not call DOS 0x49 (the equivalent of deleteSegOrWin); if it did, then the deleteSegOrWin(q) call might be unnecessary, since q is in some sense a child of malloclist[i].addr) */ free(p); /* free memory */ } /*===========================================================================*/ void pfarfree(void *q) /*---------------------------------------------------------------------------- "pfarfree" is called with a selector q, which is either an Eclipse data window pointer used for bounds checking, or (less routinely) simply the pointer returned directly by farmalloc. Examining the low 16 bits determines which. For a window, we look up the stored value of the corresponding farmalloc pointer (which is a selector, not the real physical address), and q's slot in the Local Descriptor Table is freed for use by subsequent allocations. In any event, Turbo C's "farfree" is called with the farmalloc pointer. --------------------------------------------------------------------------- */ {uint16 i; void *p; /* Make life easier when dealing with segment and offset pointers. */ static union {void *A; struct {uint16 Offset,Segment;} Word; } LrgPtr; TRACE(printf("[pfarfree] with: 0x%lx, ",q);) if (q EQ NULL) return; LrgPtr.A = q; if (LrgPtr.Word.Offset != 0) /* Assume no bounds protection for this allocation */ {farfree(q); /* free memory */ TRACE(printf("\n");) return; } /* Lower 3 bits of a selector are ALWAYS ON: */ i = (LrgPtr.Word.Segment & ~7) >> 3; TRACE(printf("index: %u\n",i);) if ((p = malloclist[i]) == NULL) ERROR("pfarfree","Attempt to free unknown window"); /* First remove bounds-checking window(s): */ while (p == malloclist[i]) { deleteSegOrWin(LrgPtr.A); LrgPtr.Word.Segment++; /* q may be made of multiple tiles */ malloclist[i++] = NULL; /* Prevent screw ups */ } /* Turbo C's "free" call does not call DOS 0x49 (the equivalent of deleteSegOrWin); if it did, then the deleteSegOrWin(q) call might be unnecessary, since q is in some sense a child of malloclist[i].addr) */ farfree(p); /* free memory */ } /*==========================================================================*/