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 | CoreClrCallbacks g_CoreClrCallbacks; |
19 | |
20 | |
21 | // In some cirumstance (e.g, the thread suspecd another thread), allocation on heap |
22 | // could cause dead lock. We use a counter in TLS to indicate the current thread is not allowed |
23 | // to do heap allocation. |
24 | //In cases where CLRTlsInfo doesn't exist and we still want to track CantAlloc info (this is important to |
25 | //stress log), We use a global counter. This introduces the problem where one thread could disable allocation |
26 | //for another thread, but the cases should be rare (we limit the use to stress log for now) and the period |
27 | //should (MUST) be short |
28 | //only stress log check this counter |
29 | |
30 | struct CantAllocThread |
31 | { |
32 | PVOID m_fiberId; |
33 | LONG m_CantCount; |
34 | }; |
35 | |
36 | #define MaxCantAllocThreadNum 100 |
37 | static CantAllocThread g_CantAllocThreads[MaxCantAllocThreadNum] = {}; |
38 | static Volatile<LONG> g_CantAllocStressLogCount = 0; |
39 | |
40 | void IncCantAllocCount() |
41 | { |
42 | size_t count = 0; |
43 | if (ClrFlsCheckValue(TlsIdx_CantAllocCount, (LPVOID *)&count)) |
44 | { |
45 | _ASSERTE (count >= 0); |
46 | ClrFlsSetValue(TlsIdx_CantAllocCount, (LPVOID)(count+1)); |
47 | return; |
48 | } |
49 | PVOID fiberId = ClrTeb::GetFiberPtrId(); |
50 | for (int i = 0; i < MaxCantAllocThreadNum; i ++) |
51 | { |
52 | if (g_CantAllocThreads[i].m_fiberId == fiberId) |
53 | { |
54 | g_CantAllocThreads[i].m_CantCount ++; |
55 | return; |
56 | } |
57 | } |
58 | for (int i = 0; i < MaxCantAllocThreadNum; i ++) |
59 | { |
60 | if (g_CantAllocThreads[i].m_fiberId == NULL) |
61 | { |
62 | if (InterlockedCompareExchangeT(&g_CantAllocThreads[i].m_fiberId, fiberId, NULL) == NULL) |
63 | { |
64 | _ASSERTE(g_CantAllocThreads[i].m_CantCount == 0); |
65 | g_CantAllocThreads[i].m_CantCount = 1; |
66 | return; |
67 | } |
68 | } |
69 | } |
70 | count = InterlockedIncrement (&g_CantAllocStressLogCount); |
71 | _ASSERTE (count >= 1); |
72 | return; |
73 | } |
74 | |
75 | void DecCantAllocCount() |
76 | { |
77 | size_t count = 0; |
78 | if (ClrFlsCheckValue(TlsIdx_CantAllocCount, (LPVOID *)&count)) |
79 | { |
80 | if (count > 0) |
81 | { |
82 | ClrFlsSetValue(TlsIdx_CantAllocCount, (LPVOID)(count-1)); |
83 | return; |
84 | } |
85 | } |
86 | PVOID fiberId = ClrTeb::GetFiberPtrId(); |
87 | for (int i = 0; i < MaxCantAllocThreadNum; i ++) |
88 | { |
89 | if (g_CantAllocThreads[i].m_fiberId == fiberId) |
90 | { |
91 | _ASSERTE (g_CantAllocThreads[i].m_CantCount > 0); |
92 | g_CantAllocThreads[i].m_CantCount --; |
93 | if (g_CantAllocThreads[i].m_CantCount == 0) |
94 | { |
95 | g_CantAllocThreads[i].m_fiberId = NULL; |
96 | } |
97 | return; |
98 | } |
99 | } |
100 | _ASSERTE (g_CantAllocStressLogCount > 0); |
101 | InterlockedDecrement (&g_CantAllocStressLogCount); |
102 | return; |
103 | |
104 | } |
105 | |
106 | // for stress log the rule is more restrict, we have to check the global counter too |
107 | BOOL IsInCantAllocStressLogRegion() |
108 | { |
109 | size_t count = 0; |
110 | if (ClrFlsCheckValue(TlsIdx_CantAllocCount, (LPVOID *)&count)) |
111 | { |
112 | if (count > 0) |
113 | { |
114 | return true; |
115 | } |
116 | } |
117 | PVOID fiberId = ClrTeb::GetFiberPtrId(); |
118 | for (int i = 0; i < MaxCantAllocThreadNum; i ++) |
119 | { |
120 | if (g_CantAllocThreads[i].m_fiberId == fiberId) |
121 | { |
122 | _ASSERTE (g_CantAllocThreads[i].m_CantCount > 0); |
123 | return true; |
124 | } |
125 | } |
126 | |
127 | return g_CantAllocStressLogCount > 0; |
128 | |
129 | } |
130 | |
131 | |
132 | #ifdef FAILPOINTS_ENABLED |
133 | typedef int (*FHashStack) (); |
134 | |
135 | static FHashStack fHashStack = 0; |
136 | static _TEB *HashStackSetupThread = NULL; |
137 | static _TEB *RFSCustomDataSetupThread = NULL; |
138 | |
139 | static void SetupHashStack () |
140 | { |
141 | CANNOT_HAVE_CONTRACT; |
142 | |
143 | FHashStack oldValue = InterlockedCompareExchangeT(&fHashStack, |
144 | reinterpret_cast<FHashStack>(1), reinterpret_cast<FHashStack>(0)); |
145 | if ((size_t) oldValue >= 2) { |
146 | return; |
147 | } |
148 | else if ((size_t) oldValue == 0) { |
149 | // We are the first thread to initialize |
150 | HashStackSetupThread = NtCurrentTeb(); |
151 | |
152 | if (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_HashStack) == 0) { |
153 | fHashStack = (FHashStack) 2; |
154 | return; |
155 | } |
156 | |
157 | PAL_TRY(void *, unused, NULL) { |
158 | FHashStack func; |
159 | HMODULE hmod = LoadLibraryExA ("mscorrfs.dll" , NULL, 0); |
160 | if (hmod) { |
161 | func = (FHashStack)GetProcAddress (hmod, "HashStack" ); |
162 | if (func == 0) { |
163 | func = (FHashStack)2; |
164 | } |
165 | } |
166 | else |
167 | func = (FHashStack)2; |
168 | fHashStack = func; |
169 | } |
170 | PAL_EXCEPT(EXCEPTION_EXECUTE_HANDLER) |
171 | { |
172 | fHashStack = (FHashStack) 2; |
173 | } |
174 | PAL_ENDTRY; |
175 | } |
176 | else if (NtCurrentTeb() == HashStackSetupThread) { |
177 | // We get here while initializing |
178 | return; |
179 | } |
180 | else { |
181 | // All other threads will wait |
182 | while (fHashStack == (FHashStack) 1) { |
183 | ClrSleepEx (100, FALSE); |
184 | } |
185 | } |
186 | } |
187 | |
188 | int RFS_HashStack () |
189 | { |
190 | CANNOT_HAVE_CONTRACT; |
191 | |
192 | if ((size_t)fHashStack < 2) { |
193 | SetupHashStack (); |
194 | } |
195 | |
196 | if ((size_t)fHashStack <= 2) { |
197 | return 0; |
198 | } |
199 | else |
200 | return fHashStack (); |
201 | } |
202 | |
203 | #endif // FAILPOINTS_ENABLED |
204 | |
205 | |
206 | |
207 | //----------------------------------------------------------------------------------- |
208 | // This is the approved way to get a module handle to mscorwks.dll (or coreclr.dll). |
209 | // Never call GetModuleHandle(mscorwks) yourself as this will break side-by-side inproc. |
210 | // |
211 | // This function is safe to call before or during CRT initialization. It can not |
212 | // legally return NULL (it only does so in the case of a broken build invariant.) |
213 | // |
214 | // TODO puCLR SxS utilcode work: Since this is never supposed to return NULL, it should |
215 | // not be present in SELF_NO_HOST builds of utilcode where there isn't necessarily a |
216 | // CLR in the process. We should also ASSERT that GetModuleHandleA isn't returning |
217 | // NULL below - we've probably been getting away with this in SELF_NO_HOST cases like |
218 | // mscordbi.dll. |
219 | //----------------------------------------------------------------------------------- |
220 | HMODULE GetCLRModule () |
221 | { |
222 | //! WARNING: At the time this function is invoked, the C Runtime has NOT been fully initialized, let alone the CLR. |
223 | //! So don't put in a runtime contract and don't invoke other functions in the CLR (not even _ASSERTE!) |
224 | |
225 | STATIC_CONTRACT_NOTHROW; |
226 | STATIC_CONTRACT_SO_TOLERANT; |
227 | STATIC_CONTRACT_SUPPORTS_DAC; // DAC can call in here since we initialize the SxS callbacks in ClrDataAccess::Initialize. |
228 | |
229 | #ifdef DACCESS_COMPILE |
230 | // For DAC, "g_CoreClrCallbacks" is populated in InitUtilCode when the latter is invoked |
231 | // from ClrDataAccess::Initialize alongwith a reference to a structure allocated in the |
232 | // host-process address space. |
233 | // |
234 | // This function will be invoked in the host when DAC uses SEHException::GetHr that calls into |
235 | // IsComplusException, which calls into WasThrownByUs that calls GetCLRModule when EH SxS is enabled. |
236 | // However, this function can also be executed within the target space as well. |
237 | // |
238 | // Since DACCop gives the warning to DACize this global that, actually, would be used only |
239 | // in the respective address spaces and does not require marshalling, we need to ignore this |
240 | // warning. |
241 | DACCOP_IGNORE(UndacizedGlobalVariable, "g_CoreClrCallbacks has the dual mode DAC issue." ); |
242 | #endif // DACCESS_COMPILE |
243 | VALIDATECORECLRCALLBACKS(); |
244 | |
245 | // This is the normal coreclr case - we return the module handle that was captured in our DllMain. |
246 | #ifdef DACCESS_COMPILE |
247 | // For DAC, "g_CoreClrCallbacks" is populated in InitUtilCode when the latter is invoked |
248 | // from ClrDataAccess::Initialize alongwith a reference to a structure allocated in the |
249 | // host-process address space. |
250 | // |
251 | // This function will be invoked in the host when DAC uses SEHException::GetHr that calls into |
252 | // IsComplusException, which calls into WasThrownByUs that calls GetCLRModule when EH SxS is enabled. |
253 | // However, this function can also be executed within the target space as well. |
254 | // |
255 | // Since DACCop gives the warning to DACize this global that, actually, would be used only |
256 | // in the respective address spaces and does not require marshalling, we need to ignore this |
257 | // warning. |
258 | DACCOP_IGNORE(UndacizedGlobalVariable, "g_CoreClrCallbacks has the dual mode DAC issue." ); |
259 | #endif // DACCESS_COMPILE |
260 | return g_CoreClrCallbacks.m_hmodCoreCLR; |
261 | } |
262 | |
263 | |
264 | |
265 | #if defined(SELF_NO_HOST) |
266 | |
267 | HMODULE CLRLoadLibrary(LPCWSTR lpLibFileName) |
268 | { |
269 | WRAPPER_NO_CONTRACT; |
270 | return CLRLoadLibraryEx(lpLibFileName, NULL, 0); |
271 | } |
272 | |
273 | HMODULE CLRLoadLibraryEx(LPCWSTR lpLibFileName, HANDLE hFile, DWORD dwFlags) |
274 | { |
275 | WRAPPER_NO_CONTRACT; |
276 | return WszLoadLibraryEx(lpLibFileName, hFile, dwFlags); |
277 | } |
278 | |
279 | BOOL CLRFreeLibrary(HMODULE hModule) |
280 | { |
281 | WRAPPER_NO_CONTRACT; |
282 | return FreeLibrary(hModule); |
283 | } |
284 | |
285 | #endif // defined(SELF_NO_HOST) |
286 | |
287 | |
288 | #if defined(_DEBUG_IMPL) && defined(ENABLE_CONTRACTS_IMPL) |
289 | |
290 | //----------------------------------------------------------------------------------------------- |
291 | // Imposes a new typeload level limit for the scope of the holder. Any attempt to load a type |
292 | // past that limit generates a contract violation assert. |
293 | // |
294 | // Do not invoke this directly. Invoke it through TRIGGERS_TYPE_LOAD or OVERRIDE_TYPE_LOAD_LEVEL_LIMIT. |
295 | // |
296 | // Arguments: |
297 | // fConditional - if FALSE, this holder is a nop - supports the MAYBE_* macros. |
298 | // newLevel - a value from classloadlevel.h - specifies the new max limit. |
299 | // fEnforceLevelChangeDirection |
300 | // - if true, implements TRIGGERS_TYPE_LOAD (level cap only allowed to decrease.) |
301 | // if false, implements OVERRIDE (level allowed to increase - may only be used |
302 | // by loader and only when recursion is structurally |
303 | // impossible.) |
304 | // szFunction, |
305 | // szFile, |
306 | // lineNum - records location of holder so we can print it in assertion boxes |
307 | // |
308 | // Assumptions: |
309 | // ClrDebugState must have been set up (executing any contract will do this.) |
310 | // Thread need *not* have a Thread* structure set up. |
311 | // |
312 | // Notes: |
313 | // The holder withholds the assert if a LoadsTypeViolation suppress is in effect (but |
314 | // still sets up the new limit.) |
315 | // |
316 | // As with other contract annotations, however, the violation suppression is *lifted* |
317 | // within the scope guarded by the holder itself. |
318 | //----------------------------------------------------------------------------------------------- |
319 | LoadsTypeHolder::LoadsTypeHolder(BOOL fConditional, |
320 | UINT newLevel, |
321 | BOOL fEnforceLevelChangeDirection, |
322 | const char *szFunction, |
323 | const char *szFile, |
324 | int lineNum |
325 | ) |
326 | { |
327 | // This fcn makes non-scoped changes to ClrDebugState so we cannot use a runtime CONTRACT here. |
328 | STATIC_CONTRACT_NOTHROW; |
329 | STATIC_CONTRACT_GC_NOTRIGGER; |
330 | STATIC_CONTRACT_FORBID_FAULT; |
331 | |
332 | |
333 | m_fConditional = fConditional; |
334 | if (m_fConditional) |
335 | { |
336 | m_pClrDebugState = CheckClrDebugState(); |
337 | _ASSERTE(m_pClrDebugState); |
338 | |
339 | m_oldClrDebugState = *m_pClrDebugState; |
340 | |
341 | if (fEnforceLevelChangeDirection) |
342 | { |
343 | if (newLevel > m_pClrDebugState->GetMaxLoadTypeLevel()) |
344 | { |
345 | if (!( (LoadsTypeViolation|BadDebugState) & m_pClrDebugState->ViolationMask())) |
346 | { |
347 | CONTRACT_ASSERT("Illegal attempt to load a type beyond the current level limit." , |
348 | (m_pClrDebugState->GetMaxLoadTypeLevel() + 1) << Contract::LOADS_TYPE_Shift, |
349 | Contract::LOADS_TYPE_Mask, |
350 | szFunction, |
351 | szFile, |
352 | lineNum |
353 | ); |
354 | } |
355 | } |
356 | } |
357 | |
358 | m_pClrDebugState->ViolationMaskReset(LoadsTypeViolation); |
359 | m_pClrDebugState->SetMaxLoadTypeLevel(newLevel); |
360 | |
361 | m_contractStackRecord.m_szFunction = szFunction; |
362 | m_contractStackRecord.m_szFile = szFile; |
363 | m_contractStackRecord.m_lineNum = lineNum; |
364 | m_contractStackRecord.m_testmask = (Contract::ALL_Disabled & ~((UINT)(Contract::LOADS_TYPE_Mask))) | (((newLevel) + 1) << Contract::LOADS_TYPE_Shift); |
365 | m_contractStackRecord.m_construct = fEnforceLevelChangeDirection ? "TRIGGERS_TYPE_LOAD" : "OVERRIDE_TYPE_LOAD_LEVEL_LIMIT" ; |
366 | m_contractStackRecord.m_pNext = m_pClrDebugState->GetContractStackTrace(); |
367 | m_pClrDebugState->SetContractStackTrace(&m_contractStackRecord); |
368 | |
369 | |
370 | } |
371 | } // LoadsTypeHolder::LoadsTypeHolder |
372 | |
373 | //----------------------------------------------------------------------------------------------- |
374 | // Restores prior typeload level limit. |
375 | //----------------------------------------------------------------------------------------------- |
376 | LoadsTypeHolder::~LoadsTypeHolder() |
377 | { |
378 | // This fcn makes non-scoped changes to ClrDebugState so we cannot use a runtime CONTRACT here. |
379 | STATIC_CONTRACT_NOTHROW; |
380 | STATIC_CONTRACT_GC_NOTRIGGER; |
381 | STATIC_CONTRACT_FORBID_FAULT; |
382 | |
383 | |
384 | if (m_fConditional) |
385 | { |
386 | *m_pClrDebugState = m_oldClrDebugState; |
387 | } |
388 | } |
389 | |
390 | #endif //defined(_DEBUG_IMPL) && defined(ENABLE_CONTRACTS_IMPL) |
391 | |
392 | |
393 | //-------------------------------------------------------------------------- |
394 | // Side by side inproc support |
395 | // |
396 | // These are new abstractions designed to support loading multiple CLR |
397 | // versions in the same process. |
398 | //-------------------------------------------------------------------------- |
399 | |
400 | |
401 | //-------------------------------------------------------------------------- |
402 | // One-time initialized called by coreclr.dll in its dllmain. |
403 | //-------------------------------------------------------------------------- |
404 | VOID InitUtilcode(CoreClrCallbacks const & cccallbacks) |
405 | { |
406 | //! WARNING: At the time this function is invoked, the C Runtime has NOT been fully initialized, let alone the CLR. |
407 | //! So don't put in a runtime contract and don't invoke other functions in the CLR (not even _ASSERTE!) |
408 | |
409 | LIMITED_METHOD_CONTRACT; |
410 | |
411 | g_CoreClrCallbacks = cccallbacks; |
412 | } |
413 | |
414 | CoreClrCallbacks const & GetClrCallbacks() |
415 | { |
416 | LIMITED_METHOD_CONTRACT; |
417 | |
418 | VALIDATECORECLRCALLBACKS(); |
419 | return g_CoreClrCallbacks; |
420 | } |
421 | |
422 | #ifdef _DEBUG |
423 | void OnUninitializedCoreClrCallbacks() |
424 | { |
425 | // Supports DAC since it can be called from GetCLRModule which supports DAC as well. |
426 | LIMITED_METHOD_DAC_CONTRACT; |
427 | |
428 | // If you got here, the most likely cause of the failure is that you're loading some DLL |
429 | // (other than coreclr.dll) that links to utilcode.lib, or that you're using a nohost |
430 | // variant of utilcode.lib but hitting code that assumes there is a CLR in the process. |
431 | // |
432 | // It is expected that coreclr.dll |
433 | // is the ONLY dll that links to utilcode libraries. |
434 | // |
435 | // If you must introduce a new dll that links to utilcode.lib, it is your responsibility |
436 | // to ensure that that dll invoke InitUtilcode() and forward it the right data from the *correct* |
437 | // loaded instance of coreclr. And you'll have to do without the CRT being initialized. |
438 | // |
439 | // Can't use an _ASSERTE here because even that's broken if we get to this point. |
440 | MessageBoxW(0, |
441 | W("g_CoreClrCallbacks not initialized." ), |
442 | W("\n\n" ) |
443 | W("You got here because the dll that included this copy of utilcode.lib " ) |
444 | W("did not call InitUtilcode() The most likely cause is that you're running " ) |
445 | W("a dll (other than coreclr.dll) that links to utilcode.lib." ) |
446 | , |
447 | 0); |
448 | _ASSERTE(FALSE); |
449 | DebugBreak(); |
450 | } |
451 | #endif // _DEBUG |
452 | |
453 | |