You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
681 lines
17 KiB
681 lines
17 KiB
/*++
|
|
*
|
|
* 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);
|
|
WOW32ASSERT(pbh);
|
|
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);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|