Source code of Windows XP (NT5)
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.

680 lines
17 KiB

  1. /*++
  2. *
  3. * WOW v1.0
  4. *
  5. * Copyright (c) 1996, Microsoft Corporation
  6. *
  7. * WPARAM.C
  8. *
  9. * Created: VadimB
  10. * Added cache VadimB
  11. *
  12. -*/
  13. #include "precomp.h"
  14. #pragma hdrstop
  15. MODNAME(wparam.c);
  16. ///////////////////////////////////////////////////////////////////////////
  17. // Some defines
  18. // Pre-allocated cache size for nodes
  19. #define MAPCACHESIZE 0x1000 // 4K
  20. // max "pointer movements" allowed per mapping
  21. #define MAXNODEALIAS 0x10 // 16 aliases max
  22. // (never ever seen more then 2 used)
  23. // macro to generate the number of elements in array
  24. #define ARRAYCOUNT(array) (sizeof(array)/sizeof((array)[0]))
  25. // This define will enable code that allows for keeping 32-bit buffers
  26. // allocated and integrated with nodes in cache
  27. // #define MAPPARAM_EXTRA
  28. ///////////////////////////////////////////////////////////////////////////
  29. typedef struct tagParamNode* LPPARAMNODE;
  30. typedef struct tagParamNode {
  31. LPPARAMNODE pNext;
  32. DWORD dwPtr32; // flat pointer
  33. DWORD dwPtr16;
  34. DWORD dwFlags; // flags just in case
  35. DWORD dwRefCount; // reference count
  36. #ifdef MAPPARAM_EXTRA
  37. DWORD cbExtra; // buffer size
  38. #endif
  39. DWORD nAliasCount; // index for an alias array
  40. DWORD rgdwAlias[MAXNODEALIAS];
  41. // word sized member of the struct -- alignment alert
  42. HAND16 htask16; // this is HAND16 really - keep simple and aligned
  43. } PARAMNODE, *LPPARAMNODE;
  44. typedef struct tagMapParam {
  45. LPPARAMNODE pHead;
  46. BLKCACHE blkCache;
  47. } MAPPARAM, *LPMAPPARAM;
  48. typedef struct tagFindParam {
  49. LPPARAMNODE lpNode;
  50. LPPARAMNODE lpLast;
  51. } FINDPARAM, *LPFINDPARAM;
  52. MAPPARAM gParamMap;
  53. /////////////////////////////////////////////////////////////////////////////
  54. //
  55. // FindParamMap
  56. // Finds lParam in a list assuming it is 16-bit (fMode == PARAM_16) or
  57. // 32-bit flat (fMode == PARAM_32) pointer
  58. //
  59. // lpFindParam should be NULL or point to a valid FINDPARAM structure
  60. //
  61. DWORD FindParamMap(VOID* lpFindParam, DWORD lParam, UINT fMode)
  62. {
  63. LPPARAMNODE lpn = gParamMap.pHead;
  64. LPPARAMNODE lplast = NULL;
  65. DWORD dwRet = 0;
  66. BOOL fFound = FALSE;
  67. switch(fMode) {
  68. case PARAM_16:
  69. while (NULL != lpn) {
  70. if (lParam == lpn->dwPtr16) {
  71. dwRet = lpn->dwPtr32;
  72. break;
  73. }
  74. lplast = lpn;
  75. lpn = lpn->pNext;
  76. }
  77. break;
  78. case PARAM_32:
  79. // We are looking for a 32-bit pointer
  80. // cases:
  81. // - exact match
  82. // - no match because ptr has moved (ouch!)
  83. while (NULL != lpn) {
  84. INT i;
  85. if (lParam == lpn->dwPtr32) {
  86. fFound = TRUE;
  87. }
  88. else
  89. if (lParam == (DWORD)GetPModeVDMPointer(lpn->dwPtr16, 0)) {
  90. LOGDEBUG(LOG_ALWAYS,
  91. ("WPARAM: Pointer has moved: 16:16 @%lx was 32 @%lx now @%lx\n",
  92. lpn->dwPtr16, lpn->dwPtr32, lParam));
  93. fFound = TRUE;
  94. }
  95. else {
  96. // look through the list of aliases
  97. for (i = 0; i < (INT)lpn->nAliasCount; ++i) {
  98. if (lpn->rgdwAlias[i] == lParam) {
  99. fFound = TRUE;
  100. break;
  101. }
  102. }
  103. }
  104. if (fFound) { // we found alias one way or the other...
  105. dwRet = lpn->dwPtr16;
  106. break;
  107. }
  108. lplast = lpn;
  109. lpn = lpn->pNext;
  110. }
  111. break;
  112. }
  113. if (lpn) {
  114. LPFINDPARAM lpfp = (LPFINDPARAM)lpFindParam;
  115. lpfp->lpNode = lpn;
  116. lpfp->lpLast = lplast;
  117. }
  118. return dwRet;
  119. }
  120. //
  121. // Find 32-bit param and return 16-bit equivalent
  122. //
  123. //
  124. DWORD GetParam16(DWORD dwParam32)
  125. {
  126. FINDPARAM fp;
  127. DWORD dwParam16;
  128. dwParam16 = FindParamMap(&fp, dwParam32, PARAM_32);
  129. if (dwParam16) {
  130. ++fp.lpNode->dwRefCount;
  131. }
  132. return dwParam16;
  133. }
  134. // set undead map entry
  135. BOOL SetParamRefCount(DWORD dwParam, UINT fMode, DWORD dwRefCount)
  136. {
  137. FINDPARAM fp;
  138. FindParamMap(&fp, dwParam, fMode);
  139. if (NULL != fp.lpNode) {
  140. fp.lpNode->dwRefCount = dwRefCount;
  141. }
  142. return(NULL != fp.lpNode);
  143. }
  144. //
  145. // Typically this is called either from a thunk for an api or from
  146. // 16->32 thunk for a message
  147. //
  148. // dwPtr32 most often is obtained by GETPSZPTR or GetPModeVdmPointer
  149. //
  150. //
  151. PVOID AddParamMap(DWORD dwPtr32, DWORD dwPtr16)
  152. {
  153. LPPARAMNODE lpn;
  154. FINDPARAM fp;
  155. // see if it's there already
  156. if (FindParamMap(&fp, dwPtr16, PARAM_16)) {
  157. lpn = fp.lpNode; // a bit faster ref
  158. ++lpn->dwRefCount; // increase ref count
  159. ParamMapUpdateNode(dwPtr32, PARAM_32, lpn); // just update the node
  160. }
  161. else {
  162. if (NULL != (lpn = CacheBlockAllocate(&gParamMap.blkCache, sizeof(*lpn)))) {
  163. lpn->dwPtr32 = dwPtr32;
  164. lpn->dwPtr16 = dwPtr16;
  165. lpn->pNext = gParamMap.pHead;
  166. lpn->dwRefCount = 1;
  167. #ifdef MAPPARAM_EXTRA
  168. lpn->cbExtra = 0;
  169. #endif
  170. lpn->nAliasCount = 0;
  171. lpn->htask16 = CURRENTPTD()->htask16;
  172. gParamMap.pHead = lpn;
  173. }
  174. }
  175. return lpn ? (PVOID)lpn->dwPtr32 : NULL;
  176. }
  177. #ifdef MAPPARAM_EXTRA
  178. PVOID AddParamMapEx(DWORD dwPtr16, DWORD cbExtra)
  179. {
  180. LPPARAMNODE lpn;
  181. FINDPARAM fp;
  182. // see if it's there already
  183. if (FindParamMap(&fp, dwPtr16, PARAM_16)) {
  184. lpn = fp.lpNode;
  185. if (lpn->cbExtra == cbExtra) {
  186. ++lpn->dwRefCount;
  187. }
  188. else {
  189. WOW32ASSERTMSG(FALSE, ("\nWOW32: AddParamEx misused. Please contact VadimB or DOSWOW alias\n"));
  190. lpn = NULL;
  191. }
  192. }
  193. else {
  194. if (NULL != (lpn = CacheBlockAllocate(&gParamMap.blkCache, sizeof(*lpn) + cbExtra))) {
  195. lpn->dwPtr32 = (DWORD)(PVOID)(lpn+1);
  196. lpn->dwPtr16 = dwPtr16;
  197. lpn->pNext = gParamMap.pHead;
  198. lpn->dwRefCount = 1;
  199. lpn->cbExtra = cbExtra;
  200. lpn->htask16 = CURRENTPTD()->htask16;
  201. gParamMap.pHead = lpn;
  202. }
  203. }
  204. return lpn ? (PVOID)lpn->dwPtr32 : NULL;
  205. }
  206. #endif
  207. //
  208. // This should be called from the places we know pointers could get updated
  209. //
  210. //
  211. PVOID ParamMapUpdateNode(DWORD dwPtr, UINT fMode, VOID* lpNode)
  212. {
  213. LPPARAMNODE lpn;
  214. PVOID pv;
  215. if (NULL == lpNode) {
  216. FINDPARAM fp;
  217. if (FindParamMap(&fp, dwPtr, fMode)) {
  218. lpn = fp.lpNode; // node found!
  219. }
  220. else {
  221. LOGDEBUG(LOG_ALWAYS, ("WOW: ParamMapUpdateNode could not find node\n"));
  222. // return here as we've failed to find node same as we got in
  223. return (PVOID)dwPtr;
  224. }
  225. }
  226. else {
  227. lpn = (LPPARAMNODE)lpNode;
  228. }
  229. // if pointer is up-to-date then exit
  230. pv = GetPModeVDMPointer(lpn->dwPtr16, 0);
  231. if ((DWORD)pv == lpn->dwPtr32) {
  232. return pv; // up-to-date
  233. }
  234. #ifdef MAPPARAM_EXTRA
  235. else
  236. if (0 < lpn->cbExtra) {
  237. return (PVOID)lpn->dwPtr32;
  238. }
  239. #endif
  240. if (lpn->nAliasCount < ARRAYCOUNT(lpn->rgdwAlias)) {
  241. lpn->rgdwAlias[lpn->nAliasCount++] = lpn->dwPtr32;
  242. }
  243. else {
  244. WOW32ASSERTMSG(FALSE, ("WOW:AddParamMap is out of alias space\n"));
  245. // so we will throw the oldest alias out - this will mean if they refer
  246. // to it - they are doomed... That is why we assert here!
  247. lpn->rgdwAlias[0] = lpn->dwPtr32;
  248. }
  249. lpn->dwPtr32 = (DWORD)pv; // new pointer here
  250. return pv;
  251. }
  252. //
  253. // lParam - 16- or 32-bit pointer (see fMode)
  254. // fMode - PARAM_16 or PARAM_32 - specifies what lParam represents
  255. // pfFreePtr - points to a boolean that receives TRUE if caller should
  256. // do a FREEVDMPTR on a 32-bit parameter
  257. // Returns TRUE if parameter was found and FALSE otherwise
  258. //
  259. BOOL DeleteParamMap(DWORD lParam, UINT fMode, BOOL* pfFreePtr)
  260. {
  261. FINDPARAM fp;
  262. LPPARAMNODE lpn = NULL;
  263. if (FindParamMap(&fp, lParam, fMode)) {
  264. lpn = fp.lpNode;
  265. if (!--lpn->dwRefCount) {
  266. if (NULL != fp.lpLast) {
  267. fp.lpLast->pNext = lpn->pNext;
  268. }
  269. else {
  270. gParamMap.pHead = lpn->pNext;
  271. }
  272. if (NULL != pfFreePtr) {
  273. #ifdef MAPPARAM_EXTRA
  274. *pfFreePtr = !!lpn->cbExtra;
  275. #else
  276. *pfFreePtr = FALSE;
  277. #endif
  278. }
  279. CacheBlockFree(&gParamMap.blkCache, lpn);
  280. }
  281. else {
  282. LOGDEBUG(12, ("\nWOW: DeleteParamMap called refCount > 0 Node@%x\n", (DWORD)lpn));
  283. if (NULL != pfFreePtr) { // not done with mapping yet
  284. *pfFreePtr = FALSE;
  285. }
  286. }
  287. }
  288. else {
  289. LOGDEBUG(LOG_ALWAYS, ("\nWOW: DeleteParamMap called but param was not found\n"));
  290. if (NULL != pfFreePtr) {
  291. *pfFreePtr = TRUE; // we found none, assume free
  292. }
  293. }
  294. return NULL != lpn;
  295. }
  296. BOOL W32CheckThunkParamFlag(void)
  297. {
  298. return !!(CURRENTPTD()->dwWOWCompatFlags & WOWCF_NOCBDIRTHUNK);
  299. }
  300. //
  301. // This function is called to cleanup all the leftover items in case
  302. // application is dead. Please note, that it should not be called in
  303. // any other case ever.
  304. //
  305. //
  306. VOID FreeParamMap(HAND16 htask16)
  307. {
  308. LPPARAMNODE lpn = gParamMap.pHead;
  309. LPPARAMNODE lplast = NULL, lpnext;
  310. while (NULL != lpn) {
  311. lpnext = lpn->pNext;
  312. if (lpn->htask16 == htask16) {
  313. if (NULL != lplast) {
  314. lplast->pNext = lpnext;
  315. }
  316. else {
  317. gParamMap.pHead = lpnext;
  318. }
  319. CacheBlockFree(&gParamMap.blkCache, lpn);
  320. }
  321. else {
  322. lplast = lpn;
  323. }
  324. lpn = lpnext;
  325. }
  326. }
  327. VOID InitParamMap(VOID)
  328. {
  329. CacheBlockInit(&gParamMap.blkCache, MAPCACHESIZE);
  330. }
  331. ////////////////////////////////////////////////////////////////////////////
  332. //
  333. // Cache manager
  334. //
  335. //
  336. // This is a rather simplistic allocator which uses stack-like allocation
  337. // as this is the pattern in which allocation/free is being used
  338. // each block is preceded by a 2-dword header indicating it's size
  339. /*
  340. Note:
  341. 1. Free Blocks are included in the list in the order of descending
  342. address value, that is, the free block with the highest address
  343. goes first. This leads allocator not to re-use free blocks unless
  344. there is no more memory left
  345. 2. When the block is allocated, it is chipped away from the first block
  346. that fits (no best-fit or other allocating strategy).
  347. 3. When the block is being freed, it is inserted in appropriate place in
  348. the list of free blocks or appended to the existing block
  349. Usually allocations occur on first in - first out basis. These points
  350. above provide for minimal overhead in this scenario. In more complicated
  351. cases (when hooks are installed and some other crazy things happen) it
  352. could be necessary to free block that was allocated out-of order
  353. In this case this block would be included somewhere in the free list
  354. and possibly re-used.
  355. The list of free blocks never needs compacting as it could never become
  356. fragmented.
  357. My performance testing suggests that 95% of allocations occur in a stack-
  358. like fashion. The most often hit code path is optimized for this case.
  359. With random allocations (which is not the case with wow thunks)
  360. the ratio of left merges to right(more effective) merges on 'free' calls
  361. is 3:1. With wow thunks it is more like 1:10.
  362. */
  363. BOOL IsCacheBlock(PBLKCACHE pc, LPVOID pv);
  364. #define LINK_FREELIST(pc, pNew, pLast) \
  365. if (NULL == pLast) { \
  366. pc->pCacheFree = pNew; \
  367. } \
  368. else { \
  369. pLast->pNext = pNew; \
  370. }
  371. #ifdef DEBUG
  372. #define LINK_WORKLIST(pc, pNew, pLast) \
  373. if (NULL == pLast) { \
  374. pc->pCacheHead = pNew; \
  375. } \
  376. else { \
  377. pLast->pNext = pNew; \
  378. }
  379. #else
  380. #define LINK_WORKLIST(pc, pNew, pLast)
  381. #endif
  382. VOID CacheBlockInit(PBLKCACHE pc, DWORD dwCacheSize)
  383. {
  384. PBLKHEADER pCache = (PBLKHEADER)malloc_w(dwCacheSize);
  385. RtlZeroMemory(pc, sizeof(*pc));
  386. if (NULL != pCache) {
  387. pc->pCache = (LPBYTE)pCache;
  388. pc->pCacheFree = pCache;
  389. pc->dwCacheSize= dwCacheSize;
  390. pCache->dwSize = dwCacheSize;
  391. pCache->pNext = NULL;
  392. }
  393. }
  394. LPVOID CacheBlockAllocate(PBLKCACHE pc, DWORD dwSize)
  395. {
  396. LPVOID lpv;
  397. // suballocate a block from the free list
  398. if (NULL != pc->pCacheFree) {
  399. PBLKHEADER pbh = pc->pCacheFree;
  400. PBLKHEADER pbhLast = NULL;
  401. DWORD dwSizeBlk;
  402. // dword - align dwSizeBlk, sizeof(DWORD) is power of 2 always
  403. dwSizeBlk = (dwSize + sizeof(BLKHEADER) + (sizeof(DWORD) - 1)) & ~(sizeof(DWORD)-1);
  404. // so we allocate from the highest address in hopes of filling holes
  405. // almost always this will be the largest block around
  406. while (NULL != pbh) {
  407. if (pbh->dwSize >= dwSizeBlk) { // does this block fit ?
  408. if (pbh->dwSize - dwSizeBlk > sizeof(BLKHEADER)) { // do we keep the leftovers ?
  409. // most often hit - chip off from the end
  410. pbh->dwSize -= dwSizeBlk;
  411. // now on to the new chunk
  412. pbh = (PBLKHEADER)((LPBYTE)pbh + pbh->dwSize);
  413. pbh->dwSize = dwSizeBlk;
  414. }
  415. else {
  416. // less likely case - entire block will be used
  417. // so unlink from the free list
  418. LINK_FREELIST(pc, pbh->pNext, pbhLast);
  419. }
  420. // include into busy blocks
  421. #ifdef DEBUG
  422. pbh->pNext = pc->pCacheHead;
  423. pc->pCacheHead = pbh;
  424. #endif
  425. return (LPVOID)(pbh+1);
  426. }
  427. pbhLast = pbh;
  428. pbh = pbh->pNext;
  429. }
  430. }
  431. // no free blocks
  432. if (NULL == (lpv = (LPPARAMNODE)malloc_w(dwSize))) {
  433. LOGDEBUG(2, ("Malloc failure in CacheBlockAllocate\n"));
  434. }
  435. return (lpv);
  436. }
  437. VOID CacheBlockFree(PBLKCACHE pc, LPVOID lpv)
  438. {
  439. if (IsCacheBlock(pc, lpv)) {
  440. PBLKHEADER pbh = (PBLKHEADER)lpv - 1;
  441. #ifdef DEBUG
  442. PBLKHEADER pbhf = pc->pCacheHead;
  443. PBLKHEADER pbhLast = NULL;
  444. // remove from the list of working nodes
  445. while (NULL != pbhf && pbhf != pbh) {
  446. pbhLast = pbhf;
  447. pbhf = pbhf->pNext;
  448. }
  449. if (NULL != pbhf) {
  450. // link in pbh->pNext into a worklist
  451. LINK_WORKLIST(pc, pbh->pNext, pbhLast);
  452. }
  453. else {
  454. LOGDEBUG(LOG_ALWAYS, ("Alert! CacheBlockFree - invalid ptr\n"));
  455. return;
  456. }
  457. pbhf = pc->pCacheFree;
  458. pbhLast = NULL;
  459. #else
  460. PBLKHEADER pbhf = pc->pCacheFree;
  461. PBLKHEADER pbhLast = NULL;
  462. #endif
  463. // list of free nodes
  464. // insert in order
  465. while (NULL != pbhf) {
  466. // most often case - append from the right
  467. if (((LPBYTE)pbhf + pbhf->dwSize) == (LPBYTE)pbh) {
  468. pbhf->dwSize += pbh->dwSize; // adjust the size
  469. // now see if we need compact
  470. if (NULL != pbhLast) {
  471. if (((LPBYTE)pbhf + pbhf->dwSize) == (LPBYTE)pbhLast) {
  472. // consolidate
  473. pbhLast->dwSize += pbhf->dwSize;
  474. pbhLast->pNext = pbhf->pNext;
  475. }
  476. }
  477. return;
  478. }
  479. else
  480. // check if we can append from the left
  481. if (((LPBYTE)pbh + pbh->dwSize) == (LPBYTE)pbhf) {
  482. pbh->dwSize += pbhf->dwSize; // adjust the size
  483. pbh->pNext = pbhf->pNext; // next ptr too
  484. // now also check the next free ptr so we can compact
  485. // the next ptr has lesser address
  486. if (NULL != pbh->pNext) {
  487. pbhf = pbh->pNext;
  488. if (((LPBYTE)pbhf + pbhf->dwSize) == (LPBYTE)pbh) {
  489. pbhf->dwSize += pbh->dwSize;
  490. pbh = pbhf;
  491. }
  492. }
  493. LINK_FREELIST(pc, pbh, pbhLast);
  494. return;
  495. }
  496. // check for address
  497. if (pbh > pbhf) {
  498. // we have to link-in a standalone block
  499. break;
  500. }
  501. pbhLast = pbhf;
  502. pbhf = pbhf->pNext; // on to the next block
  503. }
  504. // LOGDEBUG(LOG_ALWAYS, ("Param Map Cache: OUT-OF-ORDER free!!!\n"));
  505. pbh->pNext = pbhf;
  506. LINK_FREELIST(pc, pbh, pbhLast);
  507. }
  508. else {
  509. free_w(lpv);
  510. }
  511. }
  512. BOOL IsCacheBlock(PBLKCACHE pc, LPVOID pv)
  513. {
  514. LONG lOffset = (LONG)pv - (LONG)pc->pCache;
  515. return (lOffset >= 0 && lOffset < (LONG)pc->dwCacheSize);
  516. }
  517.