|
|
/*++
* * WOW v1.0 * * Copyright (c) 1996, Microsoft Corporation * * WPARAM.C * * Created: VadimB * Added cache VadimB * -*/
#include "precomp.h"
#pragma hdrstop
MODNAME(wparam.c);
///////////////////////////////////////////////////////////////////////////
// Some defines
// Pre-allocated cache size for nodes
#define MAPCACHESIZE 0x1000 // 4K
// max "pointer movements" allowed per mapping
#define MAXNODEALIAS 0x10 // 16 aliases max
// (never ever seen more then 2 used)
// macro to generate the number of elements in array
#define ARRAYCOUNT(array) (sizeof(array)/sizeof((array)[0]))
// This define will enable code that allows for keeping 32-bit buffers
// allocated and integrated with nodes in cache
// #define MAPPARAM_EXTRA
///////////////////////////////////////////////////////////////////////////
typedef struct tagParamNode* LPPARAMNODE;
typedef struct tagParamNode { LPPARAMNODE pNext;
DWORD dwPtr32; // flat pointer
DWORD dwPtr16; DWORD dwFlags; // flags just in case
DWORD dwRefCount; // reference count
#ifdef MAPPARAM_EXTRA
DWORD cbExtra; // buffer size
#endif
DWORD nAliasCount; // index for an alias array
DWORD rgdwAlias[MAXNODEALIAS];
// word sized member of the struct -- alignment alert
HAND16 htask16; // this is HAND16 really - keep simple and aligned
} PARAMNODE, *LPPARAMNODE;
typedef struct tagMapParam {
LPPARAMNODE pHead;
BLKCACHE blkCache;
} MAPPARAM, *LPMAPPARAM;
typedef struct tagFindParam { LPPARAMNODE lpNode; LPPARAMNODE lpLast; } FINDPARAM, *LPFINDPARAM;
MAPPARAM gParamMap;
/////////////////////////////////////////////////////////////////////////////
//
// FindParamMap
// Finds lParam in a list assuming it is 16-bit (fMode == PARAM_16) or
// 32-bit flat (fMode == PARAM_32) pointer
//
// lpFindParam should be NULL or point to a valid FINDPARAM structure
//
DWORD FindParamMap(VOID* lpFindParam, DWORD lParam, UINT fMode) { LPPARAMNODE lpn = gParamMap.pHead; LPPARAMNODE lplast = NULL; DWORD dwRet = 0; BOOL fFound = FALSE;
switch(fMode) {
case PARAM_16: while (NULL != lpn) { if (lParam == lpn->dwPtr16) { dwRet = lpn->dwPtr32; break; } lplast = lpn; lpn = lpn->pNext; } break;
case PARAM_32: // We are looking for a 32-bit pointer
// cases:
// - exact match
// - no match because ptr has moved (ouch!)
while (NULL != lpn) {
INT i;
if (lParam == lpn->dwPtr32) { fFound = TRUE; } else if (lParam == (DWORD)GetPModeVDMPointer(lpn->dwPtr16, 0)) { LOGDEBUG(LOG_ALWAYS, ("WPARAM: Pointer has moved: 16:16 @%lx was 32 @%lx now @%lx\n", lpn->dwPtr16, lpn->dwPtr32, lParam)); fFound = TRUE; } else {
// look through the list of aliases
for (i = 0; i < (INT)lpn->nAliasCount; ++i) { if (lpn->rgdwAlias[i] == lParam) { fFound = TRUE; break; } } }
if (fFound) { // we found alias one way or the other...
dwRet = lpn->dwPtr16; break; }
lplast = lpn; lpn = lpn->pNext; } break; }
if (lpn) { LPFINDPARAM lpfp = (LPFINDPARAM)lpFindParam; lpfp->lpNode = lpn; lpfp->lpLast = lplast; }
return dwRet; }
//
// Find 32-bit param and return 16-bit equivalent
//
//
DWORD GetParam16(DWORD dwParam32) { FINDPARAM fp; DWORD dwParam16;
dwParam16 = FindParamMap(&fp, dwParam32, PARAM_32); if (dwParam16) { ++fp.lpNode->dwRefCount; }
return dwParam16; }
// set undead map entry
BOOL SetParamRefCount(DWORD dwParam, UINT fMode, DWORD dwRefCount) {
FINDPARAM fp;
FindParamMap(&fp, dwParam, fMode); if (NULL != fp.lpNode) { fp.lpNode->dwRefCount = dwRefCount; } return(NULL != fp.lpNode); }
//
// Typically this is called either from a thunk for an api or from
// 16->32 thunk for a message
//
// dwPtr32 most often is obtained by GETPSZPTR or GetPModeVdmPointer
//
//
PVOID AddParamMap(DWORD dwPtr32, DWORD dwPtr16) { LPPARAMNODE lpn; FINDPARAM fp;
// see if it's there already
if (FindParamMap(&fp, dwPtr16, PARAM_16)) {
lpn = fp.lpNode; // a bit faster ref
++lpn->dwRefCount; // increase ref count
ParamMapUpdateNode(dwPtr32, PARAM_32, lpn); // just update the node
} else { if (NULL != (lpn = CacheBlockAllocate(&gParamMap.blkCache, sizeof(*lpn)))) { lpn->dwPtr32 = dwPtr32; lpn->dwPtr16 = dwPtr16; lpn->pNext = gParamMap.pHead; lpn->dwRefCount = 1; #ifdef MAPPARAM_EXTRA
lpn->cbExtra = 0; #endif
lpn->nAliasCount = 0; lpn->htask16 = CURRENTPTD()->htask16; gParamMap.pHead = lpn; } }
return lpn ? (PVOID)lpn->dwPtr32 : NULL; }
#ifdef MAPPARAM_EXTRA
PVOID AddParamMapEx(DWORD dwPtr16, DWORD cbExtra) { LPPARAMNODE lpn; FINDPARAM fp;
// see if it's there already
if (FindParamMap(&fp, dwPtr16, PARAM_16)) { lpn = fp.lpNode; if (lpn->cbExtra == cbExtra) { ++lpn->dwRefCount; } else { WOW32ASSERTMSG(FALSE, ("\nWOW32: AddParamEx misused. Please contact VadimB or DOSWOW alias\n")); lpn = NULL; } } else { if (NULL != (lpn = CacheBlockAllocate(&gParamMap.blkCache, sizeof(*lpn) + cbExtra))) { lpn->dwPtr32 = (DWORD)(PVOID)(lpn+1); lpn->dwPtr16 = dwPtr16; lpn->pNext = gParamMap.pHead; lpn->dwRefCount = 1; lpn->cbExtra = cbExtra; lpn->htask16 = CURRENTPTD()->htask16; gParamMap.pHead = lpn; } }
return lpn ? (PVOID)lpn->dwPtr32 : NULL; }
#endif
//
// This should be called from the places we know pointers could get updated
//
//
PVOID ParamMapUpdateNode(DWORD dwPtr, UINT fMode, VOID* lpNode) { LPPARAMNODE lpn; PVOID pv;
if (NULL == lpNode) { FINDPARAM fp; if (FindParamMap(&fp, dwPtr, fMode)) { lpn = fp.lpNode; // node found!
} else { LOGDEBUG(LOG_ALWAYS, ("WOW: ParamMapUpdateNode could not find node\n")); // return here as we've failed to find node same as we got in
return (PVOID)dwPtr; } } else { lpn = (LPPARAMNODE)lpNode; }
// if pointer is up-to-date then exit
pv = GetPModeVDMPointer(lpn->dwPtr16, 0); if ((DWORD)pv == lpn->dwPtr32) { return pv; // up-to-date
} #ifdef MAPPARAM_EXTRA
else if (0 < lpn->cbExtra) { return (PVOID)lpn->dwPtr32; } #endif
if (lpn->nAliasCount < ARRAYCOUNT(lpn->rgdwAlias)) {
lpn->rgdwAlias[lpn->nAliasCount++] = lpn->dwPtr32; } else { WOW32ASSERTMSG(FALSE, ("WOW:AddParamMap is out of alias space\n")); // so we will throw the oldest alias out - this will mean if they refer
// to it - they are doomed... That is why we assert here!
lpn->rgdwAlias[0] = lpn->dwPtr32; }
lpn->dwPtr32 = (DWORD)pv; // new pointer here
return pv; }
//
// lParam - 16- or 32-bit pointer (see fMode)
// fMode - PARAM_16 or PARAM_32 - specifies what lParam represents
// pfFreePtr - points to a boolean that receives TRUE if caller should
// do a FREEVDMPTR on a 32-bit parameter
// Returns TRUE if parameter was found and FALSE otherwise
//
BOOL DeleteParamMap(DWORD lParam, UINT fMode, BOOL* pfFreePtr) { FINDPARAM fp; LPPARAMNODE lpn = NULL;
if (FindParamMap(&fp, lParam, fMode)) { lpn = fp.lpNode;
if (!--lpn->dwRefCount) {
if (NULL != fp.lpLast) { fp.lpLast->pNext = lpn->pNext; } else { gParamMap.pHead = lpn->pNext; }
if (NULL != pfFreePtr) { #ifdef MAPPARAM_EXTRA
*pfFreePtr = !!lpn->cbExtra; #else
*pfFreePtr = FALSE; #endif
} CacheBlockFree(&gParamMap.blkCache, lpn); } else { LOGDEBUG(12, ("\nWOW: DeleteParamMap called refCount > 0 Node@%x\n", (DWORD)lpn));
if (NULL != pfFreePtr) { // not done with mapping yet
*pfFreePtr = FALSE; } } } else { LOGDEBUG(LOG_ALWAYS, ("\nWOW: DeleteParamMap called but param was not found\n")); if (NULL != pfFreePtr) { *pfFreePtr = TRUE; // we found none, assume free
} }
return NULL != lpn; }
BOOL W32CheckThunkParamFlag(void) { return !!(CURRENTPTD()->dwWOWCompatFlags & WOWCF_NOCBDIRTHUNK); }
//
// This function is called to cleanup all the leftover items in case
// application is dead. Please note, that it should not be called in
// any other case ever.
//
//
VOID FreeParamMap(HAND16 htask16) { LPPARAMNODE lpn = gParamMap.pHead; LPPARAMNODE lplast = NULL, lpnext;
while (NULL != lpn) {
lpnext = lpn->pNext;
if (lpn->htask16 == htask16) {
if (NULL != lplast) { lplast->pNext = lpnext; } else { gParamMap.pHead = lpnext; }
CacheBlockFree(&gParamMap.blkCache, lpn); } else { lplast = lpn; }
lpn = lpnext; } }
VOID InitParamMap(VOID) { CacheBlockInit(&gParamMap.blkCache, MAPCACHESIZE); }
////////////////////////////////////////////////////////////////////////////
//
// Cache manager
//
//
// This is a rather simplistic allocator which uses stack-like allocation
// as this is the pattern in which allocation/free is being used
// each block is preceded by a 2-dword header indicating it's size
/*
Note:
1. Free Blocks are included in the list in the order of descending address value, that is, the free block with the highest address goes first. This leads allocator not to re-use free blocks unless there is no more memory left 2. When the block is allocated, it is chipped away from the first block that fits (no best-fit or other allocating strategy). 3. When the block is being freed, it is inserted in appropriate place in the list of free blocks or appended to the existing block
Usually allocations occur on first in - first out basis. These points above provide for minimal overhead in this scenario. In more complicated cases (when hooks are installed and some other crazy things happen) it could be necessary to free block that was allocated out-of order In this case this block would be included somewhere in the free list and possibly re-used.
The list of free blocks never needs compacting as it could never become fragmented.
My performance testing suggests that 95% of allocations occur in a stack- like fashion. The most often hit code path is optimized for this case. With random allocations (which is not the case with wow thunks) the ratio of left merges to right(more effective) merges on 'free' calls is 3:1. With wow thunks it is more like 1:10.
*/
BOOL IsCacheBlock(PBLKCACHE pc, LPVOID pv);
#define LINK_FREELIST(pc, pNew, pLast) \
if (NULL == pLast) { \ pc->pCacheFree = pNew; \ } \ else { \ pLast->pNext = pNew; \ }
#ifdef DEBUG
#define LINK_WORKLIST(pc, pNew, pLast) \
if (NULL == pLast) { \ pc->pCacheHead = pNew; \ } \ else { \ pLast->pNext = pNew; \ } #else
#define LINK_WORKLIST(pc, pNew, pLast)
#endif
VOID CacheBlockInit(PBLKCACHE pc, DWORD dwCacheSize) { PBLKHEADER pCache = (PBLKHEADER)malloc_w(dwCacheSize);
RtlZeroMemory(pc, sizeof(*pc));
if (NULL != pCache) { pc->pCache = (LPBYTE)pCache; pc->pCacheFree = pCache; pc->dwCacheSize= dwCacheSize; pCache->dwSize = dwCacheSize; pCache->pNext = NULL; } }
LPVOID CacheBlockAllocate(PBLKCACHE pc, DWORD dwSize) { LPVOID lpv;
// suballocate a block from the free list
if (NULL != pc->pCacheFree) {
PBLKHEADER pbh = pc->pCacheFree; PBLKHEADER pbhLast = NULL; DWORD dwSizeBlk;
// dword - align dwSizeBlk, sizeof(DWORD) is power of 2 always
dwSizeBlk = (dwSize + sizeof(BLKHEADER) + (sizeof(DWORD) - 1)) & ~(sizeof(DWORD)-1);
// so we allocate from the highest address in hopes of filling holes
// almost always this will be the largest block around
while (NULL != pbh) { if (pbh->dwSize >= dwSizeBlk) { // does this block fit ?
if (pbh->dwSize - dwSizeBlk > sizeof(BLKHEADER)) { // do we keep the leftovers ?
// most often hit - chip off from the end
pbh->dwSize -= dwSizeBlk;
// now on to the new chunk
pbh = (PBLKHEADER)((LPBYTE)pbh + pbh->dwSize); pbh->dwSize = dwSizeBlk; } else {
// less likely case - entire block will be used
// so unlink from the free list
LINK_FREELIST(pc, pbh->pNext, pbhLast); }
// include into busy blocks
#ifdef DEBUG
pbh->pNext = pc->pCacheHead; pc->pCacheHead = pbh; #endif
return (LPVOID)(pbh+1); }
pbhLast = pbh; pbh = pbh->pNext; }
}
// no free blocks
if (NULL == (lpv = (LPPARAMNODE)malloc_w(dwSize))) { LOGDEBUG(2, ("Malloc failure in CacheBlockAllocate\n")); }
return (lpv); }
VOID CacheBlockFree(PBLKCACHE pc, LPVOID lpv) { if (IsCacheBlock(pc, lpv)) { PBLKHEADER pbh = (PBLKHEADER)lpv - 1;
#ifdef DEBUG
PBLKHEADER pbhf = pc->pCacheHead; PBLKHEADER pbhLast = NULL;
// remove from the list of working nodes
while (NULL != pbhf && pbhf != pbh) { pbhLast = pbhf; pbhf = pbhf->pNext; }
if (NULL != pbhf) {
// link in pbh->pNext into a worklist
LINK_WORKLIST(pc, pbh->pNext, pbhLast); } else { LOGDEBUG(LOG_ALWAYS, ("Alert! CacheBlockFree - invalid ptr\n")); return; }
pbhf = pc->pCacheFree; pbhLast = NULL;
#else
PBLKHEADER pbhf = pc->pCacheFree; PBLKHEADER pbhLast = NULL; #endif
// list of free nodes
// insert in order
while (NULL != pbhf) {
// most often case - append from the right
if (((LPBYTE)pbhf + pbhf->dwSize) == (LPBYTE)pbh) {
pbhf->dwSize += pbh->dwSize; // adjust the size
// now see if we need compact
if (NULL != pbhLast) { if (((LPBYTE)pbhf + pbhf->dwSize) == (LPBYTE)pbhLast) { // consolidate
pbhLast->dwSize += pbhf->dwSize; pbhLast->pNext = pbhf->pNext; } }
return; } else // check if we can append from the left
if (((LPBYTE)pbh + pbh->dwSize) == (LPBYTE)pbhf) {
pbh->dwSize += pbhf->dwSize; // adjust the size
pbh->pNext = pbhf->pNext; // next ptr too
// now also check the next free ptr so we can compact
// the next ptr has lesser address
if (NULL != pbh->pNext) { pbhf = pbh->pNext;
if (((LPBYTE)pbhf + pbhf->dwSize) == (LPBYTE)pbh) {
pbhf->dwSize += pbh->dwSize; pbh = pbhf; } }
LINK_FREELIST(pc, pbh, pbhLast);
return; }
// check for address
if (pbh > pbhf) { // we have to link-in a standalone block
break; }
pbhLast = pbhf; pbhf = pbhf->pNext; // on to the next block
}
// LOGDEBUG(LOG_ALWAYS, ("Param Map Cache: OUT-OF-ORDER free!!!\n"));
pbh->pNext = pbhf;
LINK_FREELIST(pc, pbh, pbhLast);
} else { free_w(lpv); } }
BOOL IsCacheBlock(PBLKCACHE pc, LPVOID pv) { LONG lOffset = (LONG)pv - (LONG)pc->pCache; return (lOffset >= 0 && lOffset < (LONG)pc->dwCacheSize); }
|