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
18CoreClrCallbacks 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
30struct CantAllocThread
31{
32 PVOID m_fiberId;
33 LONG m_CantCount;
34};
35
36#define MaxCantAllocThreadNum 100
37static CantAllocThread g_CantAllocThreads[MaxCantAllocThreadNum] = {};
38static Volatile<LONG> g_CantAllocStressLogCount = 0;
39
40void 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
75void 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
107BOOL 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
133typedef int (*FHashStack) ();
134
135static FHashStack fHashStack = 0;
136static _TEB *HashStackSetupThread = NULL;
137static _TEB *RFSCustomDataSetupThread = NULL;
138
139static 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
188int 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//-----------------------------------------------------------------------------------
220HMODULE 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
267HMODULE CLRLoadLibrary(LPCWSTR lpLibFileName)
268{
269 WRAPPER_NO_CONTRACT;
270 return CLRLoadLibraryEx(lpLibFileName, NULL, 0);
271}
272
273HMODULE CLRLoadLibraryEx(LPCWSTR lpLibFileName, HANDLE hFile, DWORD dwFlags)
274{
275 WRAPPER_NO_CONTRACT;
276 return WszLoadLibraryEx(lpLibFileName, hFile, dwFlags);
277}
278
279BOOL 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//-----------------------------------------------------------------------------------------------
319LoadsTypeHolder::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//-----------------------------------------------------------------------------------------------
376LoadsTypeHolder::~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//--------------------------------------------------------------------------
404VOID 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
414CoreClrCallbacks const & GetClrCallbacks()
415{
416 LIMITED_METHOD_CONTRACT;
417
418 VALIDATECORECLRCALLBACKS();
419 return g_CoreClrCallbacks;
420}
421
422#ifdef _DEBUG
423void 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