1 | // Licensed to the .NET Foundation under one or more agreements. |
2 | // The .NET Foundation licenses this file to you under the MIT license. |
3 | // See the LICENSE file in the project root for more information. |
4 | // |
5 | |
6 | // |
7 | |
8 | #include "stdafx.h" |
9 | |
10 | #include "unsafe.h" |
11 | #include "clrhost.h" |
12 | #include "utilcode.h" |
13 | #include "ex.h" |
14 | #include "hostimpl.h" |
15 | #include "clrnt.h" |
16 | #include "contract.h" |
17 | |
18 | #if defined __llvm__ |
19 | # if defined(__has_feature) && __has_feature(address_sanitizer) |
20 | # define HAS_ADDRESS_SANITIZER |
21 | # endif |
22 | #endif |
23 | |
24 | #ifdef _DEBUG_IMPL |
25 | |
26 | // |
27 | // I'd very much like for this to go away. Its used to disable all THROWS contracts within whatever DLL this |
28 | // function is called from. That's obviously very, very bad, since there's no validation of those macros. But it |
29 | // can be difficult to remove this without actually fixing every violation at the same time. |
30 | // |
31 | // When this flag is finally removed, remove RealCLRThrowsExceptionWorker() too and put CONTRACT_THROWS() in place |
32 | // of it. |
33 | // |
34 | // |
35 | static BOOL dbg_fDisableThrowCheck = FALSE; |
36 | |
37 | void DisableThrowCheck() |
38 | { |
39 | LIMITED_METHOD_CONTRACT; |
40 | |
41 | dbg_fDisableThrowCheck = TRUE; |
42 | } |
43 | |
44 | #ifdef HAS_ADDRESS_SANITIZER |
45 | // use the functionality from address santizier (which does not throw exceptions) |
46 | #else |
47 | |
48 | #define CLRThrowsExceptionWorker() RealCLRThrowsExceptionWorker(__FUNCTION__, __FILE__, __LINE__) |
49 | |
50 | static void RealCLRThrowsExceptionWorker(__in_z const char *szFunction, |
51 | __in_z const char *szFile, |
52 | int lineNum) |
53 | { |
54 | WRAPPER_NO_CONTRACT; |
55 | |
56 | if (dbg_fDisableThrowCheck) |
57 | { |
58 | return; |
59 | } |
60 | |
61 | CONTRACT_THROWSEX(szFunction, szFile, lineNum); |
62 | } |
63 | |
64 | #endif // HAS_ADDRESS_SANITIZER |
65 | #endif //_DEBUG_IMPL |
66 | |
67 | #if defined(_DEBUG_IMPL) && defined(ENABLE_CONTRACTS_IMPL) |
68 | |
69 | // Fls callback to deallocate ClrDebugState when our FLS block goes away. |
70 | void FreeClrDebugState(LPVOID pTlsData) |
71 | { |
72 | #ifdef _DEBUG |
73 | ClrDebugState *pClrDebugState = (ClrDebugState*)pTlsData; |
74 | |
75 | // Make sure the ClrDebugState was initialized by a compatible version of |
76 | // utilcode.lib. If it was initialized by an older version, we just let it leak. |
77 | if (pClrDebugState && (pClrDebugState->ViolationMask() & CanFreeMe) && !(pClrDebugState->ViolationMask() & BadDebugState)) |
78 | { |
79 | #undef HeapFree |
80 | #undef GetProcessHeap |
81 | |
82 | // Since "!(pClrDebugState->m_violationmask & BadDebugState)", we know we have |
83 | // a valid m_pLockData |
84 | _ASSERTE(pClrDebugState->GetDbgStateLockData() != NULL); |
85 | ::HeapFree (GetProcessHeap(), 0, pClrDebugState->GetDbgStateLockData()); |
86 | |
87 | ::HeapFree (GetProcessHeap(), 0, pClrDebugState); |
88 | #define HeapFree(hHeap, dwFlags, lpMem) Dont_Use_HeapFree(hHeap, dwFlags, lpMem) |
89 | #define GetProcessHeap() Dont_Use_GetProcessHeap() |
90 | } |
91 | #endif //_DEBUG |
92 | } |
93 | |
94 | // This is a drastic shutoff toggle that forces all new threads to fail their CLRInitDebugState calls. |
95 | // We only invoke this if FLS can't allocate its master block, preventing us from tracking the shutoff |
96 | // on a per-thread basis. |
97 | BYTE* GetGlobalContractShutoffFlag() |
98 | { |
99 | #ifdef SELF_NO_HOST |
100 | |
101 | static BYTE gGlobalContractShutoffFlag = 0; |
102 | return &gGlobalContractShutoffFlag; |
103 | #else //!SELF_NO_HOST |
104 | HINSTANCE hmod = GetCLRModule(); |
105 | if (!hmod) |
106 | { |
107 | return NULL; |
108 | } |
109 | typedef BYTE*(__stdcall * PGETSHUTOFFADDRFUNC)(); |
110 | PGETSHUTOFFADDRFUNC pGetContractShutoffFlagFunc = (PGETSHUTOFFADDRFUNC)GetProcAddress(hmod, "GetAddrOfContractShutoffFlag" ); |
111 | if (!pGetContractShutoffFlagFunc) |
112 | { |
113 | return NULL; |
114 | } |
115 | return pGetContractShutoffFlagFunc(); |
116 | #endif //!SELF_NO_HOST |
117 | } |
118 | |
119 | static BOOL AreContractsShutoff() |
120 | { |
121 | BYTE *pShutoff = GetGlobalContractShutoffFlag(); |
122 | if (!pShutoff) |
123 | { |
124 | return FALSE; |
125 | } |
126 | else |
127 | { |
128 | return 0 != *pShutoff; |
129 | } |
130 | } |
131 | |
132 | static VOID ShutoffContracts() |
133 | { |
134 | BYTE *pShutoff = GetGlobalContractShutoffFlag(); |
135 | if (pShutoff) |
136 | { |
137 | *pShutoff = 1; |
138 | } |
139 | } |
140 | |
141 | //============================================================================================= |
142 | // Used to initialize the per-thread ClrDebugState. This is called once per thread (with |
143 | // possible exceptions for OOM scenarios.) |
144 | // |
145 | // No matter what, this function will not return NULL. If it can't do its job because of OOM reasons, |
146 | // it will return a pointer to &gBadClrDebugState which effectively disables contracts for |
147 | // this thread. |
148 | //============================================================================================= |
149 | ClrDebugState *CLRInitDebugState() |
150 | { |
151 | // workaround! |
152 | // |
153 | // The existing Fls apis didn't provide the support we need and adding support cleanly is |
154 | // messy because of the brittleness of IExecutionEngine. |
155 | // |
156 | // To understand this function, you need to know that the Fls routines have special semantics |
157 | // for the TlsIdx_ClrDebugState slot: |
158 | // |
159 | // - FlsSetValue will never throw. If it fails due to OOM on creation of the slot storage, |
160 | // it will silently bail. Thus, we must do a confirming FlsGetValue before we can conclude |
161 | // that the SetValue succeeded. |
162 | // |
163 | // - FlsAssociateCallback will not complain about multiple sets of the callback. |
164 | // |
165 | // - The mscorwks implemention of FlsAssociateCallback will ignore the passed in value |
166 | // and use the version of FreeClrDebugState compiled into mscorwks. This is needed to |
167 | // avoid dangling pointer races on shutdown. |
168 | |
169 | |
170 | // This is our global "bad" debug state that thread use when they OOM on CLRInitDebugState. |
171 | // We really only need to initialize it once but initializing each time is convenient |
172 | // and has low perf impact. |
173 | static ClrDebugState gBadClrDebugState; |
174 | gBadClrDebugState.ViolationMaskSet( AllViolation ); |
175 | // SO_INFRASTRUCTURE_CODE() Macro to remove SO infrastructure code during build |
176 | SO_INFRASTRUCTURE_CODE(gBadClrDebugState.BeginSOTolerant();) |
177 | gBadClrDebugState.SetOkToThrow(); |
178 | |
179 | ClrDebugState *pNewClrDebugState = NULL; |
180 | ClrDebugState *pClrDebugState = NULL; |
181 | DbgStateLockData *pNewLockData = NULL; |
182 | |
183 | // We call this first partly to force a CheckThreadState. We've hopefully chased out all the |
184 | // recursive contract calls inside here but if we haven't, it's best to get them out of the way |
185 | // early. |
186 | ClrFlsAssociateCallback(TlsIdx_ClrDebugState, FreeClrDebugState); |
187 | |
188 | |
189 | if (AreContractsShutoff()) |
190 | { |
191 | pNewClrDebugState = NULL; |
192 | } |
193 | else |
194 | { |
195 | // Yuck. We cannot call the hosted allocator for ClrDebugState (it is impossible to maintain a guarantee |
196 | // that none of code paths, many of them called conditionally, don't themselves trigger a ClrDebugState creation.) |
197 | // We have to call the OS directly for this. |
198 | #undef HeapAlloc |
199 | #undef GetProcessHeap |
200 | pNewClrDebugState = (ClrDebugState*)::HeapAlloc(GetProcessHeap(), 0, sizeof(ClrDebugState)); |
201 | if (pNewClrDebugState != NULL) |
202 | { |
203 | // Only allocate a DbgStateLockData if its owning ClrDebugState was successfully allocated |
204 | pNewLockData = (DbgStateLockData *)::HeapAlloc(GetProcessHeap(), 0, sizeof(DbgStateLockData)); |
205 | } |
206 | #define GetProcessHeap() Dont_Use_GetProcessHeap() |
207 | #define HeapAlloc(hHeap, dwFlags, dwBytes) Dont_Use_HeapAlloc(hHeap, dwFlags, dwBytes) |
208 | |
209 | if ((pNewClrDebugState != NULL) && (pNewLockData != NULL)) |
210 | { |
211 | // Both allocations succeeded, so initialize the structures, and have |
212 | // pNewClrDebugState point to pNewLockData. If either of the allocations |
213 | // failed, we'll use gBadClrDebugState for this thread, and free whichever of |
214 | // pNewClrDebugState or pNewLockData actually did get allocated (if either did). |
215 | // (See code in this function below, outside this block.) |
216 | |
217 | pNewClrDebugState->SetStartingValues(); |
218 | pNewClrDebugState->ViolationMaskSet( CanFreeMe ); |
219 | _ASSERTE(!(pNewClrDebugState->ViolationMask() & BadDebugState)); |
220 | |
221 | pNewLockData->SetStartingValues(); |
222 | pNewClrDebugState->SetDbgStateLockData(pNewLockData); |
223 | } |
224 | } |
225 | |
226 | |
227 | // This is getting really diseased. All the one-time host init stuff inside the ClrFlsStuff could actually |
228 | // have caused mscorwks contracts to be executed since the last time we actually checked to see if the ClrDebugState |
229 | // needed creating. |
230 | // |
231 | // So we must make one last check to see if the ClrDebugState still needs creating. |
232 | // |
233 | ClrDebugState *pTmp = (ClrDebugState*)(ClrFlsGetValue(TlsIdx_ClrDebugState)); |
234 | if (pTmp != NULL) |
235 | { |
236 | // Recursive call set up ClrDebugState for us |
237 | pClrDebugState = pTmp; |
238 | } |
239 | else if ((pNewClrDebugState != NULL) && (pNewLockData != NULL)) |
240 | { |
241 | // Normal case: our new ClrDebugState will be the one we just allocated. |
242 | // Note that we require BOTH the ClrDebugState and the DbgStateLockData |
243 | // structures to have been successfully allocated for contracts to be |
244 | // enabled for this thread. |
245 | _ASSERTE(!(pNewClrDebugState->ViolationMask() & BadDebugState)); |
246 | _ASSERTE(pNewClrDebugState->GetDbgStateLockData() == pNewLockData); |
247 | pClrDebugState = pNewClrDebugState; |
248 | } |
249 | else |
250 | { |
251 | // OOM case: HeapAlloc of newClrDebugState failed. |
252 | pClrDebugState = &gBadClrDebugState; |
253 | } |
254 | |
255 | _ASSERTE(pClrDebugState != NULL); |
256 | |
257 | |
258 | ClrFlsSetValue(TlsIdx_ClrDebugState, (LPVOID)pClrDebugState); |
259 | |
260 | // For the ClrDebugState index, ClrFlsSetValue does *not* throw on OOM. |
261 | // Instead, it silently throws away the value. So we must now do a confirming |
262 | // FlsGet to learn if our Set succeeded. |
263 | if (ClrFlsGetValue(TlsIdx_ClrDebugState) == NULL) |
264 | { |
265 | // Our FlsSet didn't work. That means it couldn't allocate the master FLS block for our thread. |
266 | // Now we're a bad state because not only can't we succeed, we can't record that we didn't succeed. |
267 | // And it's invalid to return a BadClrDebugState here only to return a good debug state later. |
268 | // |
269 | // So we now take the drastic step of forcing all future ClrInitDebugState calls to return the OOM state. |
270 | ShutoffContracts(); |
271 | pClrDebugState = &gBadClrDebugState; |
272 | |
273 | // Try once more time to set the FLS (if it doesn't work, the next call will keep cycling through here |
274 | // until it does succeed.) |
275 | ClrFlsSetValue(TlsIdx_ClrDebugState, &gBadClrDebugState); |
276 | } |
277 | |
278 | |
279 | #if defined(_DEBUG) |
280 | // The ClrDebugState we allocated above made it into FLS iff |
281 | // the DbgStateLockData we allocated above made it into |
282 | // the FLS's ClrDebugState::m_pLockData |
283 | // These debug-only checks enforce this invariant |
284 | |
285 | if (pClrDebugState != NULL) |
286 | { |
287 | // If we're here, then typically pClrDebugState is what's in FLS. However, |
288 | // it's possible that pClrDebugState is gBadClrDebugState, and FLS is NULL |
289 | // (if the last ClrFlsSetValue() failed). Either way, our checks below |
290 | // are valid ones to make. |
291 | |
292 | if (pClrDebugState == pNewClrDebugState) |
293 | { |
294 | // ClrDebugState we allocated above made it into FLS, so DbgStateLockData |
295 | // must be there, too |
296 | _ASSERTE(pNewLockData != NULL); |
297 | _ASSERTE(pClrDebugState->GetDbgStateLockData() == pNewLockData); |
298 | } |
299 | else |
300 | { |
301 | // ClrDebugState we allocated above did NOT make it into FLS, |
302 | // so the DbgStateLockData we allocated must not be there, either |
303 | _ASSERTE(pClrDebugState->GetDbgStateLockData() == NULL || pClrDebugState->GetDbgStateLockData() != pNewLockData); |
304 | } |
305 | } |
306 | |
307 | // One more invariant: Because of ordering & conditions around the HeapAllocs above, |
308 | // we'll never have a DbgStateLockData without a ClrDebugState |
309 | _ASSERTE((pNewLockData == NULL) || (pNewClrDebugState != NULL)); |
310 | |
311 | #endif //_DEBUG |
312 | |
313 | #undef HeapFree |
314 | #undef GetProcessHeap |
315 | if (pNewClrDebugState != NULL && pClrDebugState != pNewClrDebugState) |
316 | { |
317 | // We allocated a ClrDebugState which didn't make it into FLS, so free it. |
318 | ::HeapFree (GetProcessHeap(), 0, pNewClrDebugState); |
319 | if (pNewLockData != NULL) |
320 | { |
321 | // We also allocated a DbgStateLockData that didn't make it into FLS, so |
322 | // free it, too. (Remember, we asserted above that we can only have |
323 | // this unused DbgStateLockData if we had an unused ClrDebugState |
324 | // as well (which we just freed).) |
325 | ::HeapFree (GetProcessHeap(), 0, pNewLockData); |
326 | } |
327 | } |
328 | #define HeapFree(hHeap, dwFlags, lpMem) Dont_Use_HeapFree(hHeap, dwFlags, lpMem) |
329 | #define GetProcessHeap() Dont_Use_GetProcessHeap() |
330 | |
331 | // Not necessary as TLS slots are born NULL and potentially problematic for OOM cases as we can't |
332 | // take an exception here. |
333 | //ClrFlsSetValue(TlsIdx_OwnedCrstsChain, NULL); |
334 | |
335 | return pClrDebugState; |
336 | } // CLRInitDebugState |
337 | |
338 | #endif //defined(_DEBUG_IMPL) && defined(ENABLE_CONTRACTS_IMPL) |
339 | |
340 | |
341 | LPVOID ClrAllocInProcessHeapBootstrap (DWORD dwFlags, SIZE_T dwBytes) |
342 | { |
343 | STATIC_CONTRACT_SO_INTOLERANT; |
344 | |
345 | #if defined(SELF_NO_HOST) |
346 | static HANDLE hHeap = NULL; |
347 | |
348 | // This could race, but the result would be that this |
349 | // variable gets double initialized. |
350 | if (hHeap == NULL) |
351 | hHeap = ClrGetProcessHeap(); |
352 | |
353 | return ClrHeapAlloc(hHeap, dwFlags, S_SIZE_T(dwBytes)); |
354 | #else //!defined(SELF_NO_HOST) |
355 | FastAllocInProcessHeapFunc pfnHeapAlloc = (FastAllocInProcessHeapFunc) |
356 | GetClrCallbacks().m_pfnGetCLRFunction("EEHeapAllocInProcessHeap" ); |
357 | if (pfnHeapAlloc != NULL) |
358 | { |
359 | __ClrAllocInProcessHeap = pfnHeapAlloc; |
360 | return pfnHeapAlloc(dwFlags, dwBytes); |
361 | } |
362 | return ClrHeapAlloc(ClrGetProcessHeap(), dwFlags, S_SIZE_T(dwBytes)); |
363 | #endif // !defined(SELF_NO_HOST) |
364 | } |
365 | FastAllocInProcessHeapFunc __ClrAllocInProcessHeap = (FastAllocInProcessHeapFunc) ClrAllocInProcessHeapBootstrap; |
366 | |
367 | BOOL ClrFreeInProcessHeapBootstrap (DWORD dwFlags, LPVOID lpMem) |
368 | { |
369 | STATIC_CONTRACT_SO_INTOLERANT; |
370 | |
371 | #if defined(SELF_NO_HOST) |
372 | static HANDLE hHeap = NULL; |
373 | |
374 | // This could race, but the result would be that this |
375 | // variable gets double initialized. |
376 | if (hHeap == NULL) |
377 | hHeap = ClrGetProcessHeap(); |
378 | |
379 | return ClrHeapFree(hHeap, dwFlags,lpMem); |
380 | #else //!defined(SELF_NO_HOST) |
381 | FastFreeInProcessHeapFunc pfnHeapFree = (FastFreeInProcessHeapFunc) |
382 | GetClrCallbacks().m_pfnGetCLRFunction("EEHeapFreeInProcessHeap" ); |
383 | if (pfnHeapFree) |
384 | { |
385 | __ClrFreeInProcessHeap = pfnHeapFree; |
386 | return (*pfnHeapFree)(dwFlags,lpMem); |
387 | } |
388 | return ClrHeapFree(ClrGetProcessHeap(),dwFlags,lpMem); |
389 | #endif //!defined(SELF_NO_HOST) |
390 | } |
391 | FastFreeInProcessHeapFunc __ClrFreeInProcessHeap = (FastFreeInProcessHeapFunc) ClrFreeInProcessHeapBootstrap; |
392 | |
393 | const NoThrow nothrow = { 0 }; |
394 | |
395 | #ifdef HAS_ADDRESS_SANITIZER |
396 | // use standard heap functions for address santizier |
397 | #else |
398 | |
399 | void * __cdecl |
400 | operator new(size_t n) |
401 | { |
402 | #ifdef _DEBUG_IMPL |
403 | CLRThrowsExceptionWorker(); |
404 | #endif |
405 | |
406 | STATIC_CONTRACT_THROWS; |
407 | STATIC_CONTRACT_GC_NOTRIGGER; |
408 | STATIC_CONTRACT_FAULT; |
409 | STATIC_CONTRACT_SO_TOLERANT; // The memory allocation itself should be SO-tolerant. But we must protect the use of it. |
410 | STATIC_CONTRACT_SUPPORTS_DAC_HOST_ONLY; |
411 | |
412 | void * result = ClrAllocInProcessHeap(0, S_SIZE_T(n)); |
413 | if (result == NULL) { |
414 | ThrowOutOfMemory(); |
415 | } |
416 | TRASH_LASTERROR; |
417 | return result; |
418 | } |
419 | |
420 | void * __cdecl |
421 | operator new[](size_t n) |
422 | { |
423 | #ifdef _DEBUG_IMPL |
424 | CLRThrowsExceptionWorker(); |
425 | #endif |
426 | |
427 | STATIC_CONTRACT_THROWS; |
428 | STATIC_CONTRACT_GC_NOTRIGGER; |
429 | STATIC_CONTRACT_FAULT; |
430 | STATIC_CONTRACT_SO_TOLERANT; // The memory allocation itself should be SO-tolerant. But we must protect the use of it. |
431 | STATIC_CONTRACT_SUPPORTS_DAC_HOST_ONLY; |
432 | |
433 | void * result = ClrAllocInProcessHeap(0, S_SIZE_T(n)); |
434 | if (result == NULL) { |
435 | ThrowOutOfMemory(); |
436 | } |
437 | TRASH_LASTERROR; |
438 | return result; |
439 | }; |
440 | |
441 | #endif // HAS_ADDRESS_SANITIZER |
442 | |
443 | void * __cdecl operator new(size_t n, const NoThrow&) NOEXCEPT |
444 | { |
445 | #ifdef HAS_ADDRESS_SANITIZER |
446 | // use standard heap functions for address santizier (which doesn't provide for NoThrow) |
447 | void * result = operator new(n); |
448 | #else |
449 | STATIC_CONTRACT_NOTHROW; |
450 | STATIC_CONTRACT_GC_NOTRIGGER; |
451 | STATIC_CONTRACT_FAULT; |
452 | STATIC_CONTRACT_SO_TOLERANT; // The memory allocation itself should be SO-tolerant. But we must protect the use of it. |
453 | STATIC_CONTRACT_SUPPORTS_DAC_HOST_ONLY; |
454 | |
455 | INCONTRACT(_ASSERTE(!ARE_FAULTS_FORBIDDEN())); |
456 | |
457 | void * result = ClrAllocInProcessHeap(0, S_SIZE_T(n)); |
458 | #endif // HAS_ADDRESS_SANITIZER |
459 | TRASH_LASTERROR; |
460 | return result; |
461 | } |
462 | |
463 | void * __cdecl operator new[](size_t n, const NoThrow&) NOEXCEPT |
464 | { |
465 | #ifdef HAS_ADDRESS_SANITIZER |
466 | // use standard heap functions for address santizier (which doesn't provide for NoThrow) |
467 | void * result = operator new[](n); |
468 | #else |
469 | STATIC_CONTRACT_NOTHROW; |
470 | STATIC_CONTRACT_GC_NOTRIGGER; |
471 | STATIC_CONTRACT_FAULT; |
472 | STATIC_CONTRACT_SO_TOLERANT; // The memory allocation itself should be SO-tolerant. But we must protect the use of it. |
473 | STATIC_CONTRACT_SUPPORTS_DAC_HOST_ONLY; |
474 | |
475 | INCONTRACT(_ASSERTE(!ARE_FAULTS_FORBIDDEN())); |
476 | |
477 | void * result = ClrAllocInProcessHeap(0, S_SIZE_T(n)); |
478 | #endif // HAS_ADDRESS_SANITIZER |
479 | TRASH_LASTERROR; |
480 | return result; |
481 | } |
482 | |
483 | #ifdef HAS_ADDRESS_SANITIZER |
484 | // use standard heap functions for address santizier |
485 | #else |
486 | void __cdecl |
487 | operator delete(void *p) NOEXCEPT |
488 | { |
489 | STATIC_CONTRACT_NOTHROW; |
490 | STATIC_CONTRACT_GC_NOTRIGGER; |
491 | STATIC_CONTRACT_SO_TOLERANT; // The memory management routines should be SO-tolerant. |
492 | STATIC_CONTRACT_SUPPORTS_DAC_HOST_ONLY; |
493 | |
494 | if (p != NULL) |
495 | ClrFreeInProcessHeap(0, p); |
496 | TRASH_LASTERROR; |
497 | } |
498 | |
499 | void __cdecl |
500 | operator delete[](void *p) NOEXCEPT |
501 | { |
502 | STATIC_CONTRACT_NOTHROW; |
503 | STATIC_CONTRACT_GC_NOTRIGGER; |
504 | STATIC_CONTRACT_SO_TOLERANT; // The memory management routines should be SO-tolerant. |
505 | STATIC_CONTRACT_SUPPORTS_DAC_HOST_ONLY; |
506 | |
507 | if (p != NULL) |
508 | ClrFreeInProcessHeap(0, p); |
509 | TRASH_LASTERROR; |
510 | } |
511 | |
512 | #endif // HAS_ADDRESS_SANITIZER |
513 | |
514 | |
515 | /* ------------------------------------------------------------------------ * |
516 | * New operator overloading for the executable heap |
517 | * ------------------------------------------------------------------------ */ |
518 | |
519 | #ifndef FEATURE_PAL |
520 | |
521 | const CExecutable executable = { 0 }; |
522 | |
523 | void * __cdecl operator new(size_t n, const CExecutable&) |
524 | { |
525 | #if defined(_DEBUG_IMPL) |
526 | CLRThrowsExceptionWorker(); |
527 | #endif |
528 | |
529 | STATIC_CONTRACT_THROWS; |
530 | STATIC_CONTRACT_GC_NOTRIGGER; |
531 | STATIC_CONTRACT_FAULT; |
532 | STATIC_CONTRACT_SO_TOLERANT; // The memory management routines should be SO-tolerant. |
533 | |
534 | HANDLE hExecutableHeap = ClrGetProcessExecutableHeap(); |
535 | if (hExecutableHeap == NULL) { |
536 | ThrowOutOfMemory(); |
537 | } |
538 | |
539 | void * result = ClrHeapAlloc(hExecutableHeap, 0, S_SIZE_T(n)); |
540 | if (result == NULL) { |
541 | ThrowOutOfMemory(); |
542 | } |
543 | TRASH_LASTERROR; |
544 | return result; |
545 | } |
546 | |
547 | void * __cdecl operator new[](size_t n, const CExecutable&) |
548 | { |
549 | #if defined(_DEBUG_IMPL) |
550 | CLRThrowsExceptionWorker(); |
551 | #endif |
552 | |
553 | STATIC_CONTRACT_THROWS; |
554 | STATIC_CONTRACT_GC_NOTRIGGER; |
555 | STATIC_CONTRACT_FAULT; |
556 | STATIC_CONTRACT_SO_TOLERANT; // The memory management routines should be SO-tolerant. |
557 | |
558 | HANDLE hExecutableHeap = ClrGetProcessExecutableHeap(); |
559 | if (hExecutableHeap == NULL) { |
560 | ThrowOutOfMemory(); |
561 | } |
562 | |
563 | void * result = ClrHeapAlloc(hExecutableHeap, 0, S_SIZE_T(n)); |
564 | if (result == NULL) { |
565 | ThrowOutOfMemory(); |
566 | } |
567 | TRASH_LASTERROR; |
568 | return result; |
569 | } |
570 | |
571 | void * __cdecl operator new(size_t n, const CExecutable&, const NoThrow&) |
572 | { |
573 | STATIC_CONTRACT_NOTHROW; |
574 | STATIC_CONTRACT_GC_NOTRIGGER; |
575 | STATIC_CONTRACT_FAULT; |
576 | STATIC_CONTRACT_SO_TOLERANT; // The memory management routines should be SO-tolerant. |
577 | |
578 | INCONTRACT(_ASSERTE(!ARE_FAULTS_FORBIDDEN())); |
579 | |
580 | HANDLE hExecutableHeap = ClrGetProcessExecutableHeap(); |
581 | if (hExecutableHeap == NULL) |
582 | return NULL; |
583 | |
584 | void * result = ClrHeapAlloc(hExecutableHeap, 0, S_SIZE_T(n)); |
585 | TRASH_LASTERROR; |
586 | return result; |
587 | } |
588 | |
589 | void * __cdecl operator new[](size_t n, const CExecutable&, const NoThrow&) |
590 | { |
591 | STATIC_CONTRACT_NOTHROW; |
592 | STATIC_CONTRACT_GC_NOTRIGGER; |
593 | STATIC_CONTRACT_FAULT; |
594 | STATIC_CONTRACT_SO_TOLERANT; // The memory management routines should be SO-tolerant. |
595 | |
596 | INCONTRACT(_ASSERTE(!ARE_FAULTS_FORBIDDEN())); |
597 | |
598 | HANDLE hExecutableHeap = ClrGetProcessExecutableHeap(); |
599 | if (hExecutableHeap == NULL) |
600 | return NULL; |
601 | |
602 | void * result = ClrHeapAlloc(hExecutableHeap, 0, S_SIZE_T(n)); |
603 | TRASH_LASTERROR; |
604 | return result; |
605 | } |
606 | |
607 | #endif // FEATURE_PAL |
608 | |
609 | #ifdef _DEBUG |
610 | |
611 | // This is a DEBUG routing to verify that a memory region complies with executable requirements |
612 | BOOL DbgIsExecutable(LPVOID lpMem, SIZE_T length) |
613 | { |
614 | #if defined(CROSSGEN_COMPILE) || defined(FEATURE_PAL) |
615 | // No NX support on PAL or for crossgen compilations. |
616 | return TRUE; |
617 | #else // !(CROSSGEN_COMPILE || FEATURE_PAL) |
618 | BYTE *regionStart = (BYTE*) ALIGN_DOWN((BYTE*)lpMem, GetOsPageSize()); |
619 | BYTE *regionEnd = (BYTE*) ALIGN_UP((BYTE*)lpMem+length, GetOsPageSize()); |
620 | _ASSERTE(length > 0); |
621 | _ASSERTE(regionStart < regionEnd); |
622 | |
623 | while(regionStart < regionEnd) |
624 | { |
625 | MEMORY_BASIC_INFORMATION mbi; |
626 | |
627 | SIZE_T cbBytes = ClrVirtualQuery(regionStart, &mbi, sizeof(mbi)); |
628 | _ASSERTE(cbBytes); |
629 | |
630 | // The pages must have EXECUTE set |
631 | if(!(mbi.Protect & (PAGE_EXECUTE | PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY))) |
632 | return FALSE; |
633 | |
634 | _ASSERTE((BYTE*)mbi.BaseAddress + mbi.RegionSize > regionStart); |
635 | regionStart = (BYTE*)mbi.BaseAddress + mbi.RegionSize; |
636 | } |
637 | |
638 | return TRUE; |
639 | #endif // CROSSGEN_COMPILE || FEATURE_PAL |
640 | } |
641 | |
642 | #endif //_DEBUG |
643 | |
644 | |
645 | |
646 | |
647 | // Access various ExecutionEngine support services, like a logical TLS that abstracts |
648 | // fiber vs. thread issues. We obtain it from a DLL export via the shim. |
649 | |
650 | typedef IExecutionEngine * (__stdcall * IEE_FPTR) (); |
651 | |
652 | // |
653 | // Access various ExecutionEngine support services, like a logical TLS that abstracts |
654 | // fiber vs. thread issues. |
655 | // From an IExecutionEngine is possible to get other services via QueryInterfaces such |
656 | // as memory management |
657 | // |
658 | IExecutionEngine *g_pExecutionEngine = NULL; |
659 | |
660 | #ifdef SELF_NO_HOST |
661 | BYTE g_ExecutionEngineInstance[sizeof(UtilExecutionEngine)]; |
662 | #endif |
663 | |
664 | |
665 | IExecutionEngine *GetExecutionEngine() |
666 | { |
667 | STATIC_CONTRACT_NOTHROW; |
668 | STATIC_CONTRACT_GC_NOTRIGGER; |
669 | STATIC_CONTRACT_CANNOT_TAKE_LOCK; |
670 | STATIC_CONTRACT_SO_TOLERANT; |
671 | SUPPORTS_DAC_HOST_ONLY; |
672 | |
673 | if (g_pExecutionEngine == NULL) |
674 | { |
675 | IExecutionEngine* pExecutionEngine; |
676 | #ifdef SELF_NO_HOST |
677 | // Create a local copy on the stack and then copy it over to the static instance. |
678 | // This avoids race conditions caused by multiple initializations of vtable in the constructor |
679 | UtilExecutionEngine local; |
680 | memcpy((void*)&g_ExecutionEngineInstance, (void*)&local, sizeof(UtilExecutionEngine)); |
681 | pExecutionEngine = (IExecutionEngine*)(UtilExecutionEngine*)&g_ExecutionEngineInstance; |
682 | #else |
683 | // statically linked. |
684 | VALIDATECORECLRCALLBACKS(); |
685 | pExecutionEngine = g_CoreClrCallbacks.m_pfnIEE(); |
686 | #endif // SELF_NO_HOST |
687 | |
688 | //We use an explicit memory barrier here so that the reference g_pExecutionEngine is valid when |
689 | //it is used, This ia a requirement on platforms with weak memory model . We cannot use VolatileStore |
690 | //because they are the same as normal assignment for DAC builds [see code:VOLATILE] |
691 | |
692 | MemoryBarrier(); |
693 | g_pExecutionEngine = pExecutionEngine; |
694 | } |
695 | |
696 | // It's a bug to ask for the ExecutionEngine interface in scenarios where the |
697 | // ExecutionEngine cannot be loaded. |
698 | _ASSERTE(g_pExecutionEngine); |
699 | return g_pExecutionEngine; |
700 | } // GetExecutionEngine |
701 | |
702 | IEEMemoryManager * GetEEMemoryManager() |
703 | { |
704 | STATIC_CONTRACT_SO_TOLERANT; |
705 | STATIC_CONTRACT_GC_NOTRIGGER; |
706 | STATIC_CONTRACT_NOTHROW; |
707 | STATIC_CONTRACT_CANNOT_TAKE_LOCK; |
708 | SUPPORTS_DAC_HOST_ONLY; |
709 | |
710 | static IEEMemoryManager *pEEMemoryManager = NULL; |
711 | if (NULL == pEEMemoryManager) { |
712 | IExecutionEngine *pExecutionEngine = GetExecutionEngine(); |
713 | _ASSERTE(pExecutionEngine); |
714 | |
715 | // It is dangerous to pass a global pointer to QueryInterface. The pointer may be set |
716 | // to NULL in the call. Imagine that thread 1 calls QI, and get a pointer. But before thread 1 |
717 | // returns the pointer to caller, thread 2 calls QI and the pointer is set to NULL. |
718 | IEEMemoryManager *pEEMM; |
719 | pExecutionEngine->QueryInterface(IID_IEEMemoryManager, (void**)&pEEMM); |
720 | pEEMemoryManager = pEEMM; |
721 | } |
722 | // It's a bug to ask for the MemoryManager interface in scenarios where it cannot be loaded. |
723 | _ASSERTE(pEEMemoryManager); |
724 | return pEEMemoryManager; |
725 | } |
726 | |
727 | // should return some error code or exception |
728 | void SetExecutionEngine(IExecutionEngine *pExecutionEngine) |
729 | { |
730 | STATIC_CONTRACT_NOTHROW; |
731 | STATIC_CONTRACT_GC_NOTRIGGER; |
732 | |
733 | _ASSERTE(pExecutionEngine && !g_pExecutionEngine); |
734 | if (!g_pExecutionEngine) { |
735 | g_pExecutionEngine = pExecutionEngine; |
736 | g_pExecutionEngine->AddRef(); |
737 | } |
738 | } |
739 | |
740 | void ClrFlsAssociateCallback(DWORD slot, PTLS_CALLBACK_FUNCTION callback) |
741 | { |
742 | WRAPPER_NO_CONTRACT; |
743 | |
744 | GetExecutionEngine()->TLS_AssociateCallback(slot, callback); |
745 | } |
746 | |
747 | LPVOID *ClrFlsGetBlockGeneric() |
748 | { |
749 | WRAPPER_NO_CONTRACT; |
750 | STATIC_CONTRACT_SO_TOLERANT; |
751 | |
752 | return (LPVOID *) GetExecutionEngine()->TLS_GetDataBlock(); |
753 | } |
754 | |
755 | CLRFLSGETBLOCK __ClrFlsGetBlock = ClrFlsGetBlockGeneric; |
756 | |
757 | CRITSEC_COOKIE ClrCreateCriticalSection(CrstType crstType, CrstFlags flags) |
758 | { |
759 | WRAPPER_NO_CONTRACT; |
760 | |
761 | return GetExecutionEngine()->CreateLock(NULL, (LPCSTR)crstType, flags); |
762 | } |
763 | |
764 | HRESULT ClrDeleteCriticalSection(CRITSEC_COOKIE cookie) |
765 | { |
766 | WRAPPER_NO_CONTRACT; |
767 | GetExecutionEngine()->DestroyLock(cookie); |
768 | return S_OK; |
769 | } |
770 | |
771 | void ClrEnterCriticalSection(CRITSEC_COOKIE cookie) |
772 | { |
773 | WRAPPER_NO_CONTRACT; |
774 | |
775 | return GetExecutionEngine()->AcquireLock(cookie); |
776 | } |
777 | |
778 | void ClrLeaveCriticalSection(CRITSEC_COOKIE cookie) |
779 | { |
780 | WRAPPER_NO_CONTRACT; |
781 | |
782 | return GetExecutionEngine()->ReleaseLock(cookie); |
783 | } |
784 | |
785 | EVENT_COOKIE ClrCreateAutoEvent(BOOL bInitialState) |
786 | { |
787 | WRAPPER_NO_CONTRACT; |
788 | |
789 | return GetExecutionEngine()->CreateAutoEvent(bInitialState); |
790 | } |
791 | |
792 | EVENT_COOKIE ClrCreateManualEvent(BOOL bInitialState) |
793 | { |
794 | WRAPPER_NO_CONTRACT; |
795 | |
796 | return GetExecutionEngine()->CreateManualEvent(bInitialState); |
797 | } |
798 | |
799 | void ClrCloseEvent(EVENT_COOKIE event) |
800 | { |
801 | WRAPPER_NO_CONTRACT; |
802 | |
803 | GetExecutionEngine()->CloseEvent(event); |
804 | } |
805 | |
806 | BOOL ClrSetEvent(EVENT_COOKIE event) |
807 | { |
808 | WRAPPER_NO_CONTRACT; |
809 | |
810 | return GetExecutionEngine()->ClrSetEvent(event); |
811 | } |
812 | |
813 | BOOL ClrResetEvent(EVENT_COOKIE event) |
814 | { |
815 | WRAPPER_NO_CONTRACT; |
816 | |
817 | return GetExecutionEngine()->ClrResetEvent(event); |
818 | } |
819 | |
820 | DWORD ClrWaitEvent(EVENT_COOKIE event, DWORD dwMilliseconds, BOOL bAlertable) |
821 | { |
822 | WRAPPER_NO_CONTRACT; |
823 | |
824 | return GetExecutionEngine()->WaitForEvent(event, dwMilliseconds, bAlertable); |
825 | } |
826 | |
827 | SEMAPHORE_COOKIE ClrCreateSemaphore(DWORD dwInitial, DWORD dwMax) |
828 | { |
829 | WRAPPER_NO_CONTRACT; |
830 | |
831 | return GetExecutionEngine()->ClrCreateSemaphore(dwInitial, dwMax); |
832 | } |
833 | |
834 | void ClrCloseSemaphore(SEMAPHORE_COOKIE semaphore) |
835 | { |
836 | WRAPPER_NO_CONTRACT; |
837 | |
838 | GetExecutionEngine()->ClrCloseSemaphore(semaphore); |
839 | } |
840 | |
841 | BOOL ClrReleaseSemaphore(SEMAPHORE_COOKIE semaphore, LONG lReleaseCount, LONG *lpPreviousCount) |
842 | { |
843 | WRAPPER_NO_CONTRACT; |
844 | |
845 | return GetExecutionEngine()->ClrReleaseSemaphore(semaphore, lReleaseCount, lpPreviousCount); |
846 | } |
847 | |
848 | DWORD ClrWaitSemaphore(SEMAPHORE_COOKIE semaphore, DWORD dwMilliseconds, BOOL bAlertable) |
849 | { |
850 | WRAPPER_NO_CONTRACT; |
851 | |
852 | return GetExecutionEngine()->ClrWaitForSemaphore(semaphore, dwMilliseconds, bAlertable); |
853 | } |
854 | |
855 | MUTEX_COOKIE ClrCreateMutex(LPSECURITY_ATTRIBUTES lpMutexAttributes, |
856 | BOOL bInitialOwner, |
857 | LPCTSTR lpName) |
858 | { |
859 | WRAPPER_NO_CONTRACT; |
860 | |
861 | return GetExecutionEngine()->ClrCreateMutex(lpMutexAttributes, bInitialOwner, lpName); |
862 | } |
863 | |
864 | void ClrCloseMutex(MUTEX_COOKIE mutex) |
865 | { |
866 | WRAPPER_NO_CONTRACT; |
867 | |
868 | GetExecutionEngine()->ClrCloseMutex(mutex); |
869 | } |
870 | |
871 | BOOL ClrReleaseMutex(MUTEX_COOKIE mutex) |
872 | { |
873 | WRAPPER_NO_CONTRACT; |
874 | |
875 | return GetExecutionEngine()->ClrReleaseMutex(mutex); |
876 | } |
877 | |
878 | DWORD ClrWaitForMutex(MUTEX_COOKIE mutex, DWORD dwMilliseconds, BOOL bAlertable) |
879 | { |
880 | WRAPPER_NO_CONTRACT; |
881 | |
882 | return GetExecutionEngine()->ClrWaitForMutex(mutex, dwMilliseconds, bAlertable); |
883 | } |
884 | |
885 | DWORD ClrSleepEx(DWORD dwMilliseconds, BOOL bAlertable) |
886 | { |
887 | WRAPPER_NO_CONTRACT; |
888 | |
889 | return GetExecutionEngine()->ClrSleepEx(dwMilliseconds, bAlertable); |
890 | } |
891 | |
892 | LPVOID ClrVirtualAlloc(LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect) |
893 | { |
894 | WRAPPER_NO_CONTRACT; |
895 | |
896 | LPVOID result = GetEEMemoryManager()->ClrVirtualAlloc(lpAddress, dwSize, flAllocationType, flProtect); |
897 | LOG((LF_EEMEM, LL_INFO100000, "ClrVirtualAlloc (0x%p, 0x%06x, 0x%06x, 0x%02x) = 0x%p\n" , lpAddress, dwSize, flAllocationType, flProtect, result)); |
898 | |
899 | return result; |
900 | } |
901 | |
902 | BOOL ClrVirtualFree(LPVOID lpAddress, SIZE_T dwSize, DWORD dwFreeType) |
903 | { |
904 | WRAPPER_NO_CONTRACT; |
905 | |
906 | LOG((LF_EEMEM, LL_INFO100000, "ClrVirtualFree (0x%p, 0x%06x, 0x%04x)\n" , lpAddress, dwSize, dwFreeType)); |
907 | BOOL result = GetEEMemoryManager()->ClrVirtualFree(lpAddress, dwSize, dwFreeType); |
908 | |
909 | return result; |
910 | } |
911 | |
912 | SIZE_T ClrVirtualQuery(LPCVOID lpAddress, PMEMORY_BASIC_INFORMATION lpBuffer, SIZE_T dwLength) |
913 | { |
914 | WRAPPER_NO_CONTRACT; |
915 | |
916 | LOG((LF_EEMEM, LL_INFO100000, "ClrVirtualQuery (0x%p)\n" , lpAddress)); |
917 | return GetEEMemoryManager()->ClrVirtualQuery(lpAddress, lpBuffer, dwLength); |
918 | } |
919 | |
920 | BOOL ClrVirtualProtect(LPVOID lpAddress, SIZE_T dwSize, DWORD flNewProtect, PDWORD lpflOldProtect) |
921 | { |
922 | WRAPPER_NO_CONTRACT; |
923 | |
924 | LOG((LF_EEMEM, LL_INFO100000, "ClrVirtualProtect(0x%p, 0x%06x, 0x%02x)\n" , lpAddress, dwSize, flNewProtect)); |
925 | return GetEEMemoryManager()->ClrVirtualProtect(lpAddress, dwSize, flNewProtect, lpflOldProtect); |
926 | } |
927 | |
928 | HANDLE ClrGetProcessHeap() |
929 | { |
930 | WRAPPER_NO_CONTRACT; |
931 | |
932 | return GetEEMemoryManager()->ClrGetProcessHeap(); |
933 | } |
934 | |
935 | HANDLE ClrHeapCreate(DWORD flOptions, SIZE_T dwInitialSize, SIZE_T dwMaximumSize) |
936 | { |
937 | WRAPPER_NO_CONTRACT; |
938 | |
939 | return GetEEMemoryManager()->ClrHeapCreate(flOptions, dwInitialSize, dwMaximumSize); |
940 | } |
941 | |
942 | BOOL ClrHeapDestroy(HANDLE hHeap) |
943 | { |
944 | WRAPPER_NO_CONTRACT; |
945 | |
946 | return GetEEMemoryManager()->ClrHeapDestroy(hHeap); |
947 | } |
948 | |
949 | LPVOID ClrHeapAlloc(HANDLE hHeap, DWORD dwFlags, S_SIZE_T dwBytes) |
950 | { |
951 | WRAPPER_NO_CONTRACT; |
952 | |
953 | if(dwBytes.IsOverflow()) return NULL; |
954 | |
955 | LPVOID result = GetEEMemoryManager()->ClrHeapAlloc(hHeap, dwFlags, dwBytes.Value()); |
956 | |
957 | return result; |
958 | } |
959 | |
960 | BOOL ClrHeapFree(HANDLE hHeap, DWORD dwFlags, LPVOID lpMem) |
961 | { |
962 | WRAPPER_NO_CONTRACT; |
963 | |
964 | BOOL result = GetEEMemoryManager()->ClrHeapFree(hHeap, dwFlags, lpMem); |
965 | |
966 | return result; |
967 | } |
968 | |
969 | BOOL ClrHeapValidate(HANDLE hHeap, DWORD dwFlags, LPCVOID lpMem) |
970 | { |
971 | WRAPPER_NO_CONTRACT; |
972 | |
973 | return GetEEMemoryManager()->ClrHeapValidate(hHeap, dwFlags, lpMem); |
974 | } |
975 | |
976 | HANDLE ClrGetProcessExecutableHeap() |
977 | { |
978 | WRAPPER_NO_CONTRACT; |
979 | |
980 | return GetEEMemoryManager()->ClrGetProcessExecutableHeap(); |
981 | } |
982 | |
983 | void GetLastThrownObjectExceptionFromThread(void **ppvException) |
984 | { |
985 | WRAPPER_NO_CONTRACT; |
986 | |
987 | GetExecutionEngine()->GetLastThrownObjectExceptionFromThread(ppvException); |
988 | } |
989 | |