681 lines
17 KiB
C
681 lines
17 KiB
C
/*++
|
||
*
|
||
* 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);
|
||
}
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|