| 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 | // |
| 9 | // |
| 10 | |
| 11 | // |
| 12 | // ==--== |
| 13 | |
| 14 | #include "stdafx.h" |
| 15 | |
| 16 | #include "mscoree.h" |
| 17 | #include "clrinternal.h" |
| 18 | #include "hostimpl.h" |
| 19 | #include "predeftlsslot.h" |
| 20 | #include "unsafe.h" |
| 21 | |
| 22 | // to avoid to include clrhost.h in this file |
| 23 | #ifdef FAILPOINTS_ENABLED |
| 24 | extern int RFS_HashStack(); |
| 25 | #endif |
| 26 | |
| 27 | static DWORD TlsIndex = TLS_OUT_OF_INDEXES; |
| 28 | static PTLS_CALLBACK_FUNCTION Callbacks[MAX_PREDEFINED_TLS_SLOT]; |
| 29 | |
| 30 | #ifdef SELF_NO_HOST |
| 31 | HANDLE (*g_fnGetExecutableHeapHandle)(); |
| 32 | #endif |
| 33 | |
| 34 | extern LPVOID* (*__ClrFlsGetBlock)(); |
| 35 | |
| 36 | // |
| 37 | // FLS getter to avoid unnecessary indirection via execution engine. |
| 38 | // |
| 39 | LPVOID* ClrFlsGetBlockDirect() |
| 40 | { |
| 41 | return (LPVOID*)UnsafeTlsGetValue(TlsIndex); |
| 42 | } |
| 43 | |
| 44 | // |
| 45 | // utility functions for tls functionality |
| 46 | // |
| 47 | static void **CheckThreadState(DWORD slot, BOOL force = TRUE) |
| 48 | { |
| 49 | // Treat as a runtime assertion, since the invariant spans many DLLs. |
| 50 | _ASSERTE(slot < MAX_PREDEFINED_TLS_SLOT); |
| 51 | |
| 52 | // Ensure we have a TLS Index |
| 53 | if (TlsIndex == TLS_OUT_OF_INDEXES) |
| 54 | { |
| 55 | DWORD tmp = UnsafeTlsAlloc(); |
| 56 | |
| 57 | if (InterlockedCompareExchange((LONG*)&TlsIndex, tmp, TLS_OUT_OF_INDEXES) != (LONG) TLS_OUT_OF_INDEXES) |
| 58 | { |
| 59 | // We lost the race with another thread. |
| 60 | UnsafeTlsFree(tmp); |
| 61 | } |
| 62 | |
| 63 | // Switch to faster TLS getter now that the TLS slot is initialized |
| 64 | __ClrFlsGetBlock = ClrFlsGetBlockDirect; |
| 65 | } |
| 66 | |
| 67 | _ASSERTE(TlsIndex != TLS_OUT_OF_INDEXES); |
| 68 | |
| 69 | void **pTlsData = (void **)TlsGetValue(TlsIndex); |
| 70 | |
| 71 | if (pTlsData == 0 && force) { |
| 72 | |
| 73 | // !!! Contract uses our TLS support. Contract may be used before our host support is set up. |
| 74 | // !!! To better support contract, we call into OS for memory allocation. |
| 75 | pTlsData = (void**) ::HeapAlloc(GetProcessHeap(),0,MAX_PREDEFINED_TLS_SLOT*sizeof(void*)); |
| 76 | |
| 77 | |
| 78 | if (pTlsData == NULL) |
| 79 | { |
| 80 | // workaround! We don't want exceptions being thrown during ClrInitDebugState. Just return NULL out of TlsSetValue. |
| 81 | // ClrInitDebugState will do a confirming FlsGet to see if the value stuck. |
| 82 | |
| 83 | // If this is for the stack probe, and we failed to allocate memory for it, we won't |
| 84 | // put in a guard page. |
| 85 | if (slot == TlsIdx_ClrDebugState || slot == TlsIdx_StackProbe) |
| 86 | { |
| 87 | return NULL; |
| 88 | } |
| 89 | RaiseException(STATUS_NO_MEMORY, 0, 0, NULL); |
| 90 | } |
| 91 | for (int i=0; i<MAX_PREDEFINED_TLS_SLOT; i++) |
| 92 | pTlsData[i] = 0; |
| 93 | UnsafeTlsSetValue(TlsIndex, pTlsData); |
| 94 | } |
| 95 | |
| 96 | return pTlsData; |
| 97 | } // CheckThreadState |
| 98 | |
| 99 | // This function should only be called during process detatch for |
| 100 | // mscoree.dll. |
| 101 | VOID STDMETHODCALLTYPE TLS_FreeMasterSlotIndex() |
| 102 | { |
| 103 | if (TlsIndex != TLS_OUT_OF_INDEXES) |
| 104 | if (UnsafeTlsFree(TlsIndex)) |
| 105 | TlsIndex = TLS_OUT_OF_INDEXES; |
| 106 | } // TLS_FreeMasterSlotIndex |
| 107 | |
| 108 | |
| 109 | |
| 110 | HRESULT STDMETHODCALLTYPE UtilExecutionEngine::QueryInterface(REFIID id, void **pInterface) |
| 111 | { |
| 112 | if (!pInterface) |
| 113 | return E_POINTER; |
| 114 | |
| 115 | *pInterface = NULL; |
| 116 | |
| 117 | if (id == IID_IExecutionEngine) |
| 118 | *pInterface = (IExecutionEngine *)this; |
| 119 | else if (id == IID_IEEMemoryManager) |
| 120 | *pInterface = (IEEMemoryManager *)this; |
| 121 | else if (id == IID_IUnknown) |
| 122 | *pInterface = (IUnknown *)(IExecutionEngine *)this; |
| 123 | else |
| 124 | return E_NOINTERFACE; |
| 125 | |
| 126 | AddRef(); |
| 127 | return S_OK; |
| 128 | } // UtilExecutionEngine::QueryInterface |
| 129 | |
| 130 | // |
| 131 | // lifetime of this object is that of the app it lives in so no point in AddRef/Release |
| 132 | // |
| 133 | ULONG STDMETHODCALLTYPE UtilExecutionEngine::AddRef() |
| 134 | { |
| 135 | return 1; |
| 136 | } |
| 137 | |
| 138 | ULONG STDMETHODCALLTYPE UtilExecutionEngine::Release() |
| 139 | { |
| 140 | return 1; |
| 141 | } |
| 142 | |
| 143 | VOID STDMETHODCALLTYPE UtilExecutionEngine::TLS_AssociateCallback(DWORD slot, PTLS_CALLBACK_FUNCTION callback) |
| 144 | { |
| 145 | CheckThreadState(slot); |
| 146 | |
| 147 | // They can toggle between a callback and no callback. But anything else looks like |
| 148 | // confusion on their part. |
| 149 | // |
| 150 | // (TlsIdx_ClrDebugState associates its callback from utilcode.lib - which can be replicated. But |
| 151 | // all the callbacks are equally good.) |
| 152 | _ASSERTE(slot == TlsIdx_ClrDebugState || Callbacks[slot] == 0 || Callbacks[slot] == callback || callback == 0); |
| 153 | Callbacks[slot] = callback; |
| 154 | } |
| 155 | |
| 156 | LPVOID* STDMETHODCALLTYPE UtilExecutionEngine::TLS_GetDataBlock() |
| 157 | { |
| 158 | if (TlsIndex == TLS_OUT_OF_INDEXES) |
| 159 | return NULL; |
| 160 | |
| 161 | return (LPVOID *)UnsafeTlsGetValue(TlsIndex); |
| 162 | } |
| 163 | |
| 164 | LPVOID STDMETHODCALLTYPE UtilExecutionEngine::TLS_GetValue(DWORD slot) |
| 165 | { |
| 166 | void **pTlsData = CheckThreadState(slot, FALSE); |
| 167 | if (pTlsData) |
| 168 | return pTlsData[slot]; |
| 169 | else |
| 170 | return NULL; |
| 171 | } |
| 172 | |
| 173 | BOOL STDMETHODCALLTYPE UtilExecutionEngine::TLS_CheckValue(DWORD slot, LPVOID * pValue) |
| 174 | { |
| 175 | void **pTlsData = CheckThreadState(slot, FALSE); |
| 176 | if (pTlsData) |
| 177 | { |
| 178 | *pValue = pTlsData[slot]; |
| 179 | return TRUE; |
| 180 | } |
| 181 | return FALSE; |
| 182 | } |
| 183 | |
| 184 | VOID STDMETHODCALLTYPE UtilExecutionEngine::TLS_SetValue(DWORD slot, LPVOID pData) |
| 185 | { |
| 186 | void **pTlsData = CheckThreadState(slot); |
| 187 | if (pTlsData) // Yes, CheckThreadState(slot, TRUE) can return NULL now. |
| 188 | { |
| 189 | pTlsData[slot] = pData; |
| 190 | } |
| 191 | } |
| 192 | |
| 193 | VOID STDMETHODCALLTYPE UtilExecutionEngine::TLS_ThreadDetaching() |
| 194 | { |
| 195 | void **pTlsData = CheckThreadState(0, FALSE); |
| 196 | if (pTlsData) |
| 197 | { |
| 198 | for (int i=0; i<MAX_PREDEFINED_TLS_SLOT; i++) |
| 199 | { |
| 200 | // If we have some data and a callback, issue it. |
| 201 | if (Callbacks[i] != 0 && pTlsData[i] != 0) |
| 202 | (*Callbacks[i])(pTlsData[i]); |
| 203 | } |
| 204 | ::HeapFree (GetProcessHeap(),0,pTlsData); |
| 205 | |
| 206 | } |
| 207 | } |
| 208 | |
| 209 | CRITSEC_COOKIE STDMETHODCALLTYPE UtilExecutionEngine::CreateLock(LPCSTR szTag, LPCSTR level, CrstFlags flags) |
| 210 | { |
| 211 | CRITICAL_SECTION *cs = (CRITICAL_SECTION*)malloc(sizeof(CRITICAL_SECTION)); |
| 212 | UnsafeInitializeCriticalSection(cs); |
| 213 | return (CRITSEC_COOKIE)cs; |
| 214 | } |
| 215 | |
| 216 | void STDMETHODCALLTYPE UtilExecutionEngine::DestroyLock(CRITSEC_COOKIE lock) |
| 217 | { |
| 218 | _ASSERTE(lock); |
| 219 | UnsafeDeleteCriticalSection((CRITICAL_SECTION*)lock); |
| 220 | free(lock); |
| 221 | } |
| 222 | |
| 223 | void STDMETHODCALLTYPE UtilExecutionEngine::AcquireLock(CRITSEC_COOKIE lock) |
| 224 | { |
| 225 | _ASSERTE(lock); |
| 226 | UnsafeEnterCriticalSection((CRITICAL_SECTION*)lock); |
| 227 | } |
| 228 | |
| 229 | void STDMETHODCALLTYPE UtilExecutionEngine::ReleaseLock(CRITSEC_COOKIE lock) |
| 230 | { |
| 231 | _ASSERTE(lock); |
| 232 | UnsafeLeaveCriticalSection((CRITICAL_SECTION*)lock); |
| 233 | } |
| 234 | |
| 235 | EVENT_COOKIE STDMETHODCALLTYPE UtilExecutionEngine::CreateAutoEvent(BOOL bInitialState) |
| 236 | { |
| 237 | HANDLE handle = UnsafeCreateEvent(NULL, FALSE, bInitialState, NULL); |
| 238 | _ASSERTE(handle); |
| 239 | return (EVENT_COOKIE)handle; |
| 240 | } |
| 241 | |
| 242 | EVENT_COOKIE STDMETHODCALLTYPE UtilExecutionEngine::CreateManualEvent(BOOL bInitialState) |
| 243 | { |
| 244 | HANDLE handle = UnsafeCreateEvent(NULL, TRUE, bInitialState, NULL); |
| 245 | _ASSERTE(handle); |
| 246 | return (EVENT_COOKIE)handle; |
| 247 | } |
| 248 | |
| 249 | void STDMETHODCALLTYPE UtilExecutionEngine::CloseEvent(EVENT_COOKIE event) |
| 250 | { |
| 251 | _ASSERTE(event); |
| 252 | CloseHandle((HANDLE)event); |
| 253 | } |
| 254 | |
| 255 | BOOL STDMETHODCALLTYPE UtilExecutionEngine::ClrSetEvent(EVENT_COOKIE event) |
| 256 | { |
| 257 | _ASSERTE(event); |
| 258 | return UnsafeSetEvent((HANDLE)event); |
| 259 | } |
| 260 | |
| 261 | BOOL STDMETHODCALLTYPE UtilExecutionEngine::ClrResetEvent(EVENT_COOKIE event) |
| 262 | { |
| 263 | _ASSERTE(event); |
| 264 | return UnsafeResetEvent((HANDLE)event); |
| 265 | } |
| 266 | |
| 267 | DWORD STDMETHODCALLTYPE UtilExecutionEngine::WaitForEvent(EVENT_COOKIE event, DWORD dwMilliseconds, BOOL bAlertable) |
| 268 | { |
| 269 | _ASSERTE(event); |
| 270 | return WaitForSingleObjectEx((HANDLE)event, dwMilliseconds, bAlertable); |
| 271 | } |
| 272 | |
| 273 | DWORD STDMETHODCALLTYPE UtilExecutionEngine::WaitForSingleObject(HANDLE handle, DWORD dwMilliseconds) |
| 274 | { |
| 275 | _ASSERTE(handle); |
| 276 | return WaitForSingleObjectEx(handle, dwMilliseconds, FALSE); |
| 277 | } |
| 278 | |
| 279 | SEMAPHORE_COOKIE STDMETHODCALLTYPE UtilExecutionEngine::ClrCreateSemaphore(DWORD dwInitial, DWORD dwMax) |
| 280 | { |
| 281 | HANDLE handle = UnsafeCreateSemaphore(NULL, (LONG)dwInitial, (LONG)dwMax, NULL); |
| 282 | _ASSERTE(handle); |
| 283 | return (SEMAPHORE_COOKIE)handle; |
| 284 | } |
| 285 | |
| 286 | void STDMETHODCALLTYPE UtilExecutionEngine::ClrCloseSemaphore(SEMAPHORE_COOKIE semaphore) |
| 287 | { |
| 288 | _ASSERTE(semaphore); |
| 289 | CloseHandle((HANDLE)semaphore); |
| 290 | } |
| 291 | |
| 292 | DWORD STDMETHODCALLTYPE UtilExecutionEngine::ClrWaitForSemaphore(SEMAPHORE_COOKIE semaphore, DWORD dwMilliseconds, BOOL bAlertable) |
| 293 | { |
| 294 | _ASSERTE(semaphore); |
| 295 | return WaitForSingleObjectEx((HANDLE)semaphore, dwMilliseconds, bAlertable); |
| 296 | } |
| 297 | |
| 298 | BOOL STDMETHODCALLTYPE UtilExecutionEngine::ClrReleaseSemaphore(SEMAPHORE_COOKIE semaphore, LONG lReleaseCount, LONG *lpPreviousCount) |
| 299 | { |
| 300 | _ASSERTE(semaphore); |
| 301 | return UnsafeReleaseSemaphore((HANDLE)semaphore, lReleaseCount, lpPreviousCount); |
| 302 | } |
| 303 | |
| 304 | MUTEX_COOKIE STDMETHODCALLTYPE UtilExecutionEngine::ClrCreateMutex(LPSECURITY_ATTRIBUTES lpMutexAttributes, |
| 305 | BOOL bInitialOwner, |
| 306 | LPCTSTR lpName) |
| 307 | { |
| 308 | return (MUTEX_COOKIE)WszCreateMutex(lpMutexAttributes,bInitialOwner,lpName); |
| 309 | } |
| 310 | |
| 311 | void STDMETHODCALLTYPE UtilExecutionEngine::ClrCloseMutex(MUTEX_COOKIE mutex) |
| 312 | { |
| 313 | _ASSERTE(mutex); |
| 314 | CloseHandle((HANDLE)mutex); |
| 315 | } |
| 316 | |
| 317 | BOOL STDMETHODCALLTYPE UtilExecutionEngine::ClrReleaseMutex(MUTEX_COOKIE mutex) |
| 318 | { |
| 319 | _ASSERTE(mutex); |
| 320 | return ReleaseMutex((HANDLE)mutex); |
| 321 | } |
| 322 | |
| 323 | DWORD STDMETHODCALLTYPE UtilExecutionEngine::ClrWaitForMutex(MUTEX_COOKIE mutex, |
| 324 | DWORD dwMilliseconds, |
| 325 | BOOL bAlertable) |
| 326 | { |
| 327 | _ASSERTE(mutex); |
| 328 | return WaitForSingleObjectEx ((HANDLE)mutex, dwMilliseconds, bAlertable); |
| 329 | } |
| 330 | |
| 331 | DWORD STDMETHODCALLTYPE UtilExecutionEngine::ClrSleepEx(DWORD dwMilliseconds, BOOL bAlertable) |
| 332 | { |
| 333 | return SleepEx (dwMilliseconds, bAlertable); |
| 334 | } |
| 335 | |
| 336 | BOOL STDMETHODCALLTYPE UtilExecutionEngine::ClrAllocationDisallowed() |
| 337 | { |
| 338 | return FALSE; |
| 339 | } |
| 340 | |
| 341 | LPVOID STDMETHODCALLTYPE UtilExecutionEngine::ClrVirtualAlloc(LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect) |
| 342 | { |
| 343 | #ifdef FAILPOINTS_ENABLED |
| 344 | if (RFS_HashStack ()) |
| 345 | return NULL; |
| 346 | #endif |
| 347 | return VirtualAlloc(lpAddress, dwSize, flAllocationType, flProtect); |
| 348 | } |
| 349 | |
| 350 | BOOL STDMETHODCALLTYPE UtilExecutionEngine::ClrVirtualFree(LPVOID lpAddress, SIZE_T dwSize, DWORD dwFreeType) |
| 351 | { |
| 352 | return VirtualFree(lpAddress, dwSize, dwFreeType); |
| 353 | } |
| 354 | |
| 355 | SIZE_T STDMETHODCALLTYPE UtilExecutionEngine::ClrVirtualQuery(LPCVOID lpAddress, PMEMORY_BASIC_INFORMATION lpBuffer, SIZE_T dwLength) |
| 356 | { |
| 357 | return VirtualQuery(lpAddress, lpBuffer, dwLength); |
| 358 | } |
| 359 | |
| 360 | BOOL STDMETHODCALLTYPE UtilExecutionEngine::ClrVirtualProtect(LPVOID lpAddress, SIZE_T dwSize, DWORD flNewProtect, PDWORD lpflOldProtect) |
| 361 | { |
| 362 | return VirtualProtect(lpAddress, dwSize, flNewProtect, lpflOldProtect); |
| 363 | } |
| 364 | |
| 365 | HANDLE STDMETHODCALLTYPE UtilExecutionEngine::ClrGetProcessHeap() |
| 366 | { |
| 367 | return GetProcessHeap(); |
| 368 | } |
| 369 | |
| 370 | HANDLE STDMETHODCALLTYPE UtilExecutionEngine::ClrGetProcessExecutableHeap() |
| 371 | { |
| 372 | #ifndef CROSSGEN_COMPILE |
| 373 | _ASSERTE(g_fnGetExecutableHeapHandle); |
| 374 | return (g_fnGetExecutableHeapHandle != NULL) ? g_fnGetExecutableHeapHandle() : NULL; |
| 375 | #else |
| 376 | return GetProcessHeap(); |
| 377 | #endif |
| 378 | } |
| 379 | |
| 380 | HANDLE STDMETHODCALLTYPE UtilExecutionEngine::ClrHeapCreate(DWORD flOptions, SIZE_T dwInitialSize, SIZE_T dwMaximumSize) |
| 381 | { |
| 382 | #ifdef FEATURE_PAL |
| 383 | return NULL; |
| 384 | #else |
| 385 | return HeapCreate(flOptions, dwInitialSize, dwMaximumSize); |
| 386 | #endif |
| 387 | } |
| 388 | |
| 389 | BOOL STDMETHODCALLTYPE UtilExecutionEngine::ClrHeapDestroy(HANDLE hHeap) |
| 390 | { |
| 391 | #ifdef FEATURE_PAL |
| 392 | return FALSE; |
| 393 | #else |
| 394 | return HeapDestroy(hHeap); |
| 395 | #endif |
| 396 | } |
| 397 | |
| 398 | LPVOID STDMETHODCALLTYPE UtilExecutionEngine::ClrHeapAlloc(HANDLE hHeap, DWORD dwFlags, SIZE_T dwBytes) |
| 399 | { |
| 400 | #ifdef FAILPOINTS_ENABLED |
| 401 | if (RFS_HashStack ()) |
| 402 | return NULL; |
| 403 | #endif |
| 404 | return HeapAlloc(hHeap, dwFlags, dwBytes); |
| 405 | } |
| 406 | |
| 407 | BOOL STDMETHODCALLTYPE UtilExecutionEngine::ClrHeapFree(HANDLE hHeap, DWORD dwFlags, LPVOID lpMem) |
| 408 | { |
| 409 | return HeapFree(hHeap, dwFlags, lpMem); |
| 410 | } |
| 411 | |
| 412 | BOOL STDMETHODCALLTYPE UtilExecutionEngine::ClrHeapValidate(HANDLE hHeap, DWORD dwFlags, LPCVOID lpMem) |
| 413 | { |
| 414 | #ifdef FEATURE_PAL |
| 415 | return FALSE; |
| 416 | #else |
| 417 | return HeapValidate(hHeap, dwFlags, lpMem); |
| 418 | #endif |
| 419 | } |
| 420 | |
| 421 | |
| 422 | //------------------------------------------------------------------------------ |
| 423 | // Helper function to get an exception from outside the exception. In |
| 424 | // the CLR, it may be from the Thread object. Non-CLR users have no thread object, |
| 425 | // and it will do nothing. |
| 426 | |
| 427 | void UtilExecutionEngine::GetLastThrownObjectExceptionFromThread(void **ppvException) |
| 428 | { |
| 429 | // Declare class so we can declare Exception** |
| 430 | class Exception; |
| 431 | |
| 432 | // Cast to our real type. |
| 433 | Exception **ppException = reinterpret_cast<Exception**>(ppvException); |
| 434 | |
| 435 | *ppException = NULL; |
| 436 | } // UtilExecutionEngine::GetLastThrownObjectExceptionFromThread |
| 437 | |
| 438 | |