summaryrefslogtreecommitdiff
path: root/src/vm/ceemain.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/vm/ceemain.cpp')
-rw-r--r--src/vm/ceemain.cpp5063
1 files changed, 5063 insertions, 0 deletions
diff --git a/src/vm/ceemain.cpp b/src/vm/ceemain.cpp
new file mode 100644
index 0000000000..07781261f7
--- /dev/null
+++ b/src/vm/ceemain.cpp
@@ -0,0 +1,5063 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+// ===========================================================================
+// File: CEEMAIN.CPP
+// ===========================================================================
+//
+
+//
+//
+// The CLR code base uses a hyperlink feature of the HyperAddin plugin for Visual Studio. If you don't see
+// 'HyperAddin' in your Visual Studio menu bar you don't have this support. To get it type
+//
+// \\clrmain\tools\installCLRAddins
+//
+// After installing HyperAddin, your first run of VS should be as an administrator so HyperAddin can update
+// some registry information.
+//
+// At this point the code: prefixes become hyperlinks in Visual Studio and life is good. See
+// http://mswikis/clr/dev/Pages/CLR%20Team%20Commenting.aspx for more information
+//
+// There is a bug associated with Visual Studio where it does not recognise the hyperlink if there is a ::
+// preceeding it on the same line. Since C++ uses :: as a namespace separator, this can often mean that the
+// second hyperlink on a line does not work. To work around this it is better to use '.' instead of :: as
+// the namespace separators in code: hyperlinks.
+//
+// #StartHere
+// #TableOfContents The .NET Runtime Table of contents
+//
+// This comment is mean to be a nexus that allows you to jump quickly to various interesting parts of the
+// runtime.
+//
+// You can refer to product studio bugs using urls like the following
+// * http://bugcheck/bugs/DevDivBugs/2320.asp
+// * http://bugcheck/bugs/VSWhidbey/601210.asp
+//
+// Dev10 Bugs can be added with URLs like the following (for Dev10 bug 671409)
+// * http://tkbgitvstfat01:8090/wi.aspx?id=671409
+//
+//*************************************************************************************************
+//
+// * Introduction to the rutnime file:../../doc/BookOfTheRuntime/Introduction/BOTR%20Introduction.docx
+//
+// #MajorDataStructures. The major data structures associated with the runtime are
+// * code:Thread (see file:threads.h#ThreadClass) - the additional thread state the runtime needs.
+// * code:AppDomain - The managed version of a process
+// * code:Assembly - The unit of deployment and versioing (may be several DLLs but often is only one).
+// * code:Module -represents a Module (DLL or EXE).
+// * code:MethodTable - represents the 'hot' part of a type (needed during normal execution)
+// * code:EEClass - represents the 'cold' part of a type (used during compilation, interop, ...)
+// * code:MethodDesc - represents a Method
+// * code:FieldDesc - represents a Field.
+// * code:Object - represents a object on the GC heap alloated with code:Alloc
+//
+// * ECMA specifications
+// * Partition I Concepts
+// http://download.microsoft.com/download/D/C/1/DC1B219F-3B11-4A05-9DA3-2D0F98B20917/Partition%20I%20Architecture.doc
+// * Partition II Meta Data
+// http://download.microsoft.com/download/D/C/1/DC1B219F-3B11-4A05-9DA3-2D0F98B20917/Partition%20II%20Metadata.doc
+// * Parition III IL
+// http://download.microsoft.com/download/D/C/1/DC1B219F-3B11-4A05-9DA3-2D0F98B20917/Partition%20III%20CIL.doc
+//
+// * Serge Liden (worked on the CLR and owned ILASM / ILDASM for a long time wrote a good book on IL
+// * Expert .NET 2.0 IL Assembler http://www.amazon.com/Expert-NET-2-0-IL-Assembler/dp/1590596463
+//
+// * This is also a pretty nice overview of what the CLR is at
+// http://msdn2.microsoft.com/en-us/netframework/aa497266.aspx
+//
+// * code:EEStartup - This routine must be called before any interesting runtime services are used. It is
+// invoked as part of mscorwks's DllMain logic.
+// * code:#EEShutDown - Code called before we shut down the EE.
+//
+// * file:..\inc\corhdr.h#ManagedHeader - From a data structure point of view, this is the entry point into
+// the runtime. This is how all other data in the EXE are found.
+//
+// * code:ICorJitCompiler#EEToJitInterface - This is the interface from the the EE to the Just in time (JIT)
+// compiler. The interface to the JIT is relatively simple (compileMethod), however the EE provides a
+// rich set of callbacks so the JIT can get all the information it needs. See also
+// file:../../doc/BookOfTheRuntime/JIT/JIT%20Design.doc for general information on the JIT.
+//
+// * code:VirtualCallStubManager - This is the main class that implements interface dispatch
+//
+// * Precode - Every method needs entry point for other code to call even if that native code does not
+// actually exist yet. To support this methods can have code:Precode that is an entry point that exists
+// and will call the JIT compiler if the code does not yet exist.
+//
+// * NGEN - NGen stands for Native code GENeration and it is the runtime way of precomiling IL and IL
+// Meta-data into native code and runtime data structures. See
+// file:../../doc/BookOfTheRuntime/NGEN/NGENDesign.doc for an overview. At compilation time the most
+// fundamental data structures is the code:ZapNode which represents something that needs to go into the
+// NGEN image.
+//
+// * What is cooperative / preemtive mode ? file:threads.h#CooperativeMode and
+// file:threads.h#SuspendingTheRuntime
+// * Garbage collection - file:gc.cpp#Overview
+// * code:AppDomain - The managed version of a process.
+// * Calling Into the runtime (FCALLs QCalls) file:../../doc/BookOfTheRuntime/mscorlib/mscorlibDesign.doc
+// * Exceptions - file:../../doc/BookOfTheRuntime/ManagedEH\Design.doc. The most important routine to start
+// with is code:COMPlusFrameHandler which is the routine that we hook up to get called when an unanaged
+// exception happens.
+// * Constrain Execution Regions (reliability) file:../../doc/BookOfTheRuntime/CER/CERDesign.doc)
+// * Assembly Loading file:../../doc/BookOfTheRuntime/AssemblyLoader/AssemblyLoader.doc
+// * Fusion and loading files file:../../doc/BookOfTheRuntime/AssemblyLoader/FusionDesign.doc
+// * Strings file:../../doc/BookOfTheRuntime/BCL/SystemStringDesign.doc
+// * Profiling file:../../doc/BookOfTheRuntime/DiagnosticServices/ProfilingAPIDesign.doc
+// * Remoting file:../../doc/BookOfTheRuntime/EERemotingSupport/RemotingDesign.doc
+// * Managed Debug Assitants file:../../doc/BookOfTheRuntime/MDA/MDADesign.doc
+// * FCALLS QCALLS (calling into the runtime from managed code)
+// file:../../doc/BookOfTheRuntime/Mscorlib/MscorlibDesign.doc
+// * Reflection file:../../doc/BookOfTheRuntime/Reflection/ReflectionDesign.doc
+// * Security
+// * file:../../doc/BookOfTheRuntime/RuntimeSecurity/RuntimeSecurityDesign.doc
+// * file:../../doc/BookOfTheRuntime/LoadtimeSecurity/DeclarativeSecurity-Design.doc
+// * file:../../doc/BookOfTheRuntime/LoadtimeSecurity/StrongName.doc
+// * file:../../doc/BookOfTheRuntime/RuntimeSecurity/ClickOnce Activation.doc
+// * file:../../doc/BookOfTheRuntime/RuntimeSecurity/Cryptography.doc
+// * file:../../doc/BookOfTheRuntime/RuntimeSecurity/DemandEvalDesign.doc
+// * Event Tracing for Windows
+// * file:../inc/eventtrace.h#EventTracing -
+// * This is the main file dealing with event tracing in CLR
+// * The implementation of this class is available in file:eventtrace.cpp
+// * file:../inc/eventtrace.h#CEtwTracer - This is the main class dealing with event tracing in CLR.
+// Follow the link for more information on how this feature has been implemented
+// * http://mswikis/clr/dev/Pages/CLR%20ETW%20Events%20Wiki.aspx - Follow the link for more information on how to
+// use this instrumentation feature.
+
+// ----------------------------------------------------------------------------------------------------
+// Features in the runtime that have been given hyperlinks
+//
+// * code:Nullable#NullableFeature - the Nullable<T> type has special runtime semantics associated with
+// boxing this describes this feature.
+
+#include "common.h"
+
+#include "vars.hpp"
+#include "log.h"
+#include "ceemain.h"
+#include "clsload.hpp"
+#include "object.h"
+#include "hash.h"
+#include "ecall.h"
+#include "ceemain.h"
+#include "dllimport.h"
+#include "syncblk.h"
+#include "eeconfig.h"
+#include "stublink.h"
+#include "handletable.h"
+#include "method.hpp"
+#include "codeman.h"
+#include "frames.h"
+#include "threads.h"
+#include "stackwalk.h"
+#include "gc.h"
+#include "interoputil.h"
+#include "security.h"
+#include "fieldmarshaler.h"
+#include "dbginterface.h"
+#include "eedbginterfaceimpl.h"
+#include "debugdebugger.h"
+#include "cordbpriv.h"
+#ifdef FEATURE_REMOTING
+#include "remoting.h"
+#endif
+#include "comdelegate.h"
+#include "appdomain.hpp"
+#include "perfcounters.h"
+#include "rwlock.h"
+#ifdef FEATURE_IPCMAN
+#include "ipcmanagerinterface.h"
+#endif // FEATURE_IPCMAN
+#include "eventtrace.h"
+#include "corhost.h"
+#include "binder.h"
+#include "olevariant.h"
+#include "comcallablewrapper.h"
+#include "apithreadstress.h"
+#include "ipcfunccall.h"
+#include "perflog.h"
+#include "../dlls/mscorrc/resource.h"
+#if defined(FEATURE_LEGACYSURFACE) || defined(FEATURE_USE_LCID)
+#include "nlsinfo.h"
+#endif
+#include "util.hpp"
+#include "shimload.h"
+#include "comthreadpool.h"
+#include "stackprobe.h"
+#include "posterror.h"
+#include "virtualcallstub.h"
+#include "strongnameinternal.h"
+#include "syncclean.hpp"
+#include "typeparse.h"
+#include "debuginfostore.h"
+#include "mdaassistants.h"
+#include "eemessagebox.h"
+#include "finalizerthread.h"
+#include "threadsuspend.h"
+#include "disassembler.h"
+
+#ifndef FEATURE_PAL
+#include "dwreport.h"
+#endif // !FEATURE_PAL
+
+#include "stringarraylist.h"
+#include "stubhelpers.h"
+#include "perfdefaults.h"
+
+#ifdef FEATURE_STACK_SAMPLING
+#include "stacksampler.h"
+#endif
+
+#include <shlwapi.h>
+
+#include "bbsweep.h"
+
+#ifndef FEATURE_CORECLR
+#include <metahost.h>
+#include "assemblyusagelogmanager.h"
+#endif
+
+#ifdef FEATURE_COMINTEROP
+#include "runtimecallablewrapper.h"
+#include "notifyexternals.h"
+#include "mngstdinterfaces.h"
+#include "rcwwalker.h"
+#endif // FEATURE_COMINTEROP
+
+#ifdef FEATURE_COMINTEROP_APARTMENT_SUPPORT
+#include "olecontexthelpers.h"
+#endif // FEATURE_COMINTEROP_APARTMENT_SUPPORT
+
+#ifdef PROFILING_SUPPORTED
+#include "proftoeeinterfaceimpl.h"
+#include "profilinghelper.h"
+#endif // PROFILING_SUPPORTED
+
+#include "newapis.h"
+
+#ifdef FEATURE_COMINTEROP
+#include "synchronizationcontextnative.h" // For SynchronizationContextNative::Cleanup
+#endif
+
+#ifdef FEATURE_INTERPRETER
+#include "interpreter.h"
+#endif // FEATURE_INTERPRETER
+
+#ifndef FEATURE_FUSION
+#include "../binder/inc/coreclrbindercommon.h"
+#endif // !FEATURE_FUSION
+
+#ifdef FEATURE_UEF_CHAINMANAGER
+// This is required to register our UEF callback with the UEF chain manager
+#include <mscoruefwrapper.h>
+#endif // FEATURE_UEF_CHAINMANAGER
+
+#ifdef FEATURE_PERFMAP
+#include "perfmap.h"
+#endif
+
+#ifndef FEATURE_PAL
+// Included for referencing __security_cookie
+#include "process.h"
+#endif // !FEATURE_PAL
+
+#ifdef FEATURE_IPCMAN
+static HRESULT InitializeIPCManager(void);
+static void PublishIPCManager(void);
+static void TerminateIPCManager(void);
+#endif // FEATURE_IPCMAN
+
+#ifndef CROSSGEN_COMPILE
+static int GetThreadUICultureId(__out LocaleIDValue* pLocale); // TODO: This shouldn't use the LCID. We should rely on name instead
+
+static HRESULT GetThreadUICultureNames(__inout StringArrayList* pCultureNames);
+#endif // !CROSSGEN_COMPILE
+
+HRESULT EEStartup(COINITIEE fFlags);
+#ifdef FEATURE_FUSION
+extern "C" HRESULT STDMETHODCALLTYPE InitializeFusion();
+#endif
+
+#ifdef FEATURE_MIXEDMODE
+HRESULT PrepareExecuteDLLForThunk(HINSTANCE hInst,
+ DWORD dwReason,
+ LPVOID lpReserved);
+#endif // FEATURE_MIXEDMODE
+#ifndef FEATURE_CORECLR
+BOOL STDMETHODCALLTYPE ExecuteDLL(HINSTANCE hInst,
+ DWORD dwReason,
+ LPVOID lpReserved,
+ BOOL fFromThunk);
+#endif // !FEATURE_CORECLR
+
+BOOL STDMETHODCALLTYPE ExecuteEXE(HMODULE hMod);
+BOOL STDMETHODCALLTYPE ExecuteEXE(__in LPWSTR pImageNameIn);
+
+#ifndef CROSSGEN_COMPILE
+static void InitializeGarbageCollector();
+
+#ifdef DEBUGGING_SUPPORTED
+static void InitializeDebugger(void);
+static void TerminateDebugger(void);
+extern "C" HRESULT __cdecl CorDBGetInterface(DebugInterface** rcInterface);
+#endif // DEBUGGING_SUPPORTED
+#endif // !CROSSGEN_COMPILE
+
+
+#if !defined(FEATURE_CORECLR) && !defined(CROSSGEN_COMPILE)
+void* __stdcall GetCLRFunction(LPCSTR FunctionName);
+
+// Pointer to the activated CLR interface provided by the shim.
+ICLRRuntimeInfo *g_pCLRRuntime = NULL;
+
+#endif // !FEATURE_CORECLR && !CROSSGEN_COMPILE
+
+extern "C" IExecutionEngine* __stdcall IEE();
+
+// Remember how the last startup of EE went.
+HRESULT g_EEStartupStatus = S_OK;
+
+// Flag indicating if the EE has been started. This is set prior to initializing the default AppDomain, and so does not indicate that
+// the EE is fully able to execute arbitrary managed code. To ensure the EE is fully started, call EnsureEEStarted rather than just
+// checking this flag.
+Volatile<BOOL> g_fEEStarted = FALSE;
+
+// Flag indicating if the EE should be suspended on shutdown.
+BOOL g_fSuspendOnShutdown = FALSE;
+
+// Flag indicating if the finalizer thread should be suspended on shutdown.
+BOOL g_fSuspendFinalizerOnShutdown = FALSE;
+
+// Flag indicating if the EE was started up by an managed exe.
+BOOL g_fEEManagedEXEStartup = FALSE;
+
+// Flag indicating if the EE was started up by an IJW dll.
+BOOL g_fEEIJWStartup = FALSE;
+
+// Flag indicating if the EE was started up by COM.
+extern BOOL g_fEEComActivatedStartup;
+
+// flag indicating that EE was not started up by IJW, Hosted, COM or my managed exe.
+extern BOOL g_fEEOtherStartup;
+
+// The OS thread ID of the thread currently performing EE startup, or 0 if there is no such thread.
+DWORD g_dwStartupThreadId = 0;
+
+// Event to synchronize EE shutdown.
+static CLREvent * g_pEEShutDownEvent;
+
+static DangerousNonHostedSpinLock g_EEStartupLock;
+
+HRESULT InitializeEE(COINITIEE flags)
+{
+ WRAPPER_NO_CONTRACT;
+#ifdef FEATURE_EVENT_TRACE
+ if(!(g_fEEComActivatedStartup || g_fEEManagedEXEStartup || g_fEEIJWStartup))
+ g_fEEOtherStartup = TRUE;
+#endif // FEATURE_EVENT_TRACE
+ return EnsureEEStarted(flags);
+}
+
+// ---------------------------------------------------------------------------
+// %%Function: EnsureEEStarted()
+//
+// Description: Ensure the CLR is started.
+// ---------------------------------------------------------------------------
+HRESULT EnsureEEStarted(COINITIEE flags)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_TRIGGERS;
+ MODE_PREEMPTIVE;
+ ENTRY_POINT;
+ }
+ CONTRACTL_END;
+
+ if (g_fEEShutDown)
+ return E_FAIL;
+
+ HRESULT hr = E_FAIL;
+
+ // On non x86 platforms, when we load mscorlib.dll during EEStartup, we will
+ // re-enter _CorDllMain with a DLL_PROCESS_ATTACH for mscorlib.dll. We are
+ // far enough in startup that this is allowed, however we don't want to
+ // re-start the startup code so we need to check to see if startup has
+ // been initiated or completed before we call EEStartup.
+ //
+ // We do however want to make sure other threads block until the EE is started,
+ // which we will do further down.
+ if (!g_fEEStarted)
+ {
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+#if defined(FEATURE_CORECLR) && defined(FEATURE_APPX) && !defined(CROSSGEN_COMPILE)
+ STARTUP_FLAGS startupFlags = CorHost2::GetStartupFlags();
+ // On CoreCLR, the host is in charge of determining whether the process is AppX or not.
+ AppX::SetIsAppXProcess(!!(startupFlags & STARTUP_APPX_APP_MODEL));
+#endif
+
+#ifndef FEATURE_PAL
+ // The sooner we do this, the sooner we avoid probing registry entries.
+ // (Perf Optimization for VSWhidbey:113373.)
+ REGUTIL::InitOptionalConfigCache();
+#endif
+
+#ifdef FEATURE_INCLUDE_ALL_INTERFACES
+ IHostTaskManager *pHostTaskManager = CorHost2::GetHostTaskManager();
+ if (pHostTaskManager)
+ {
+ BEGIN_SO_TOLERANT_CODE_CALLING_HOST(GetThread());
+ pHostTaskManager->BeginThreadAffinity();
+ END_SO_TOLERANT_CODE_CALLING_HOST;
+ }
+#endif // FEATURE_INCLUDE_ALL_INTERFACES
+
+ BOOL bStarted=FALSE;
+
+ {
+ DangerousNonHostedSpinLockHolder lockHolder(&g_EEStartupLock);
+
+ // Now that we've acquired the lock, check again to make sure we aren't in
+ // the process of starting the CLR or that it hasn't already been fully started.
+ // At this point, if startup has been inited we don't have anything more to do.
+ // And if EEStartup already failed before, we don't do it again.
+ if (!g_fEEStarted && !g_fEEInit && SUCCEEDED (g_EEStartupStatus))
+ {
+ g_dwStartupThreadId = GetCurrentThreadId();
+
+ EEStartup(flags);
+ bStarted=g_fEEStarted;
+ hr = g_EEStartupStatus;
+
+ g_dwStartupThreadId = 0;
+ }
+ else
+ {
+ hr = g_EEStartupStatus;
+ if (SUCCEEDED(g_EEStartupStatus))
+ {
+ hr = S_FALSE;
+ }
+ }
+ }
+
+#ifdef FEATURE_INCLUDE_ALL_INTERFACES
+ if (pHostTaskManager)
+ {
+ BEGIN_SO_TOLERANT_CODE_CALLING_HOST(GetThread());
+ pHostTaskManager->EndThreadAffinity();
+ END_SO_TOLERANT_CODE_CALLING_HOST;
+ }
+#endif // FEATURE_INCLUDE_ALL_INTERFACES
+#ifdef FEATURE_TESTHOOKS
+ if(bStarted)
+ TESTHOOKCALL(RuntimeStarted(RTS_INITIALIZED));
+#endif
+ END_ENTRYPOINT_NOTHROW;
+ }
+ else
+ {
+ //
+ // g_fEEStarted is TRUE, but startup may not be complete since we initialize the default AppDomain
+ // *after* setting that flag. g_fEEStarted is set inside of g_EEStartupLock, and that lock is
+ // not released until the EE is really started - so we can quickly check whether the EE is definitely
+ // started by checking if that lock is currently held. If it is not, then we know the other thread
+ // (that is actually doing the startup) has finished startup. If it is currently held, then we
+ // need to wait for the other thread to release it, which we do by simply acquiring the lock ourselves.
+ //
+ // We do not want to do this blocking if we are the thread currently performing EE startup. So we check
+ // that first.
+ //
+ // Note that the call to IsHeld here is an "acquire" barrier, as is acquiring the lock. And the release of
+ // the lock by the other thread is a "release" barrier, due to the volatile semantics in the lock's
+ // implementation. This assures us that once we observe the lock having been released, we are guaranteed
+ // to observe a fully-initialized EE.
+ //
+ // A note about thread affinity here: we're using the OS thread ID of the current thread without
+ // asking the host to pin us to this thread, as we did above. We can get away with this, because we are
+ // only interested in a particular thread ID (that of the "startup" thread) and *that* particular thread
+ // is already affinitized by the code above. So if we get that particular OS thread ID, we know for sure
+ // we are really the startup thread.
+ //
+ if (g_EEStartupLock.IsHeld() && g_dwStartupThreadId != GetCurrentThreadId())
+ {
+ DangerousNonHostedSpinLockHolder lockHolder(&g_EEStartupLock);
+ }
+
+ hr = g_EEStartupStatus;
+ if (SUCCEEDED(g_EEStartupStatus))
+ {
+ hr = S_FALSE;
+ }
+ }
+
+ return hr;
+}
+
+
+#ifndef CROSSGEN_COMPILE
+
+#ifndef FEATURE_PAL
+// This is our Ctrl-C, Ctrl-Break, etc. handler.
+static BOOL WINAPI DbgCtrlCHandler(DWORD dwCtrlType)
+{
+ WRAPPER_NO_CONTRACT;
+ STATIC_CONTRACT_SO_TOLERANT;
+
+#if defined(DEBUGGING_SUPPORTED)
+ // Note that if a managed-debugger is attached, it's actually attached with the native
+ // debugging pipeline and it will get a control-c notifications via native debug events.
+ // However, if we let the native debugging pipeline handle the event and send the notification
+ // to the debugger, then we break pre-V4 behaviour because we intercept handlers registered
+ // in-process. See Dev10 Bug 846455 for more information.
+ if (CORDebuggerAttached() &&
+ (dwCtrlType == CTRL_C_EVENT || dwCtrlType == CTRL_BREAK_EVENT))
+ {
+ return g_pDebugInterface->SendCtrlCToDebugger(dwCtrlType);
+ }
+ else
+#endif // DEBUGGING_SUPPORTED
+ {
+ g_fInControlC = true; // only for weakening assertions in checked build.
+ return FALSE; // keep looking for a real handler.
+ }
+}
+#endif
+
+// A host can specify that it only wants one version of hosting interface to be used.
+BOOL g_singleVersionHosting;
+
+#ifndef FEATURE_CORECLR
+HRESULT STDMETHODCALLTYPE
+SetRuntimeInfo(
+ IUnknown * pUnk,
+ STARTUP_FLAGS dwStartupFlags,
+ LPCWSTR pwzHostConfig,
+ const CoreClrCallbacks ** ppClrCallbacks)
+{
+ CONTRACTL {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ ENTRY_POINT;
+ PRECONDITION(CheckPointer(pUnk));
+ PRECONDITION(CheckPointer(pwzHostConfig, NULL_OK));
+ } CONTRACTL_END;
+
+ ICLRRuntimeInfo *pRuntime;
+ HRESULT hr;
+
+ IfFailGo(pUnk->QueryInterface(IID_ICLRRuntimeInfo, (LPVOID *)&pRuntime));
+
+ IfFailGo(CorHost2::SetFlagsAndHostConfig(dwStartupFlags, pwzHostConfig, FALSE));
+
+ if (InterlockedCompareExchangeT(&g_pCLRRuntime, pRuntime, NULL) != NULL)
+ {
+ // already set, release this one
+ pRuntime->Release();
+ }
+ *ppClrCallbacks = &GetClrCallbacks();
+
+ErrExit:
+ return hr;
+}
+#endif // !FEATURE_CORECLR
+
+#ifndef FEATURE_CORECLR
+HRESULT InitializeHostConfigFile()
+{
+ CONTRACTL {
+ NOTHROW;
+ GC_TRIGGERS;
+ MODE_ANY;
+ INJECT_FAULT(return E_OUTOFMEMORY);
+ } CONTRACTL_END;
+
+ g_pszHostConfigFile = CorHost2::GetHostConfigFile();
+ g_dwHostConfigFile = (g_pszHostConfigFile == NULL ? 0 : wcslen(g_pszHostConfigFile));
+
+ return S_OK;
+}
+#endif // !FEATURE_CORECLR
+
+void InitializeStartupFlags()
+{
+ CONTRACTL {
+ NOTHROW;
+ GC_TRIGGERS;
+ MODE_ANY;
+ } CONTRACTL_END;
+
+ STARTUP_FLAGS flags = CorHost2::GetStartupFlags();
+
+#ifndef FEATURE_CORECLR
+ // If we are running under a requested performance default mode, honor any changes to startup flags
+ // In the future, we could make this conditional on the host telling us which subset of flags is
+ // valid to override. See file:PerfDefaults.h
+ flags = PerformanceDefaults::GetModifiedStartupFlags(flags);
+#endif // !FEATURE_CORECLR
+
+ if (flags & STARTUP_CONCURRENT_GC)
+ g_IGCconcurrent = 1;
+ else
+ g_IGCconcurrent = 0;
+
+#ifndef FEATURE_CORECLR // TODO: We can remove this. Retaining it now just to be safe
+ if (flags & STARTUP_SINGLE_VERSION_HOSTING_INTERFACE)
+ {
+ g_singleVersionHosting = TRUE;
+ }
+
+#ifndef FEATURE_CORECLR
+ g_pConfig->SetDisableCommitThreadStack(!CLRHosted() || (flags & STARTUP_DISABLE_COMMITTHREADSTACK));
+#else
+ g_pConfig->SetDisableCommitThreadStack(true);
+#endif
+
+ if(flags & STARTUP_LEGACY_IMPERSONATION)
+ g_pConfig->SetLegacyImpersonationPolicy();
+
+ if(flags & STARTUP_ALWAYSFLOW_IMPERSONATION)
+ g_pConfig->SetAlwaysFlowImpersonationPolicy();
+
+ if(flags & STARTUP_HOARD_GC_VM)
+ g_IGCHoardVM = 1;
+ else
+ g_IGCHoardVM = 0;
+
+#ifdef GCTRIMCOMMIT
+ if (flags & STARTUP_TRIM_GC_COMMIT)
+ g_IGCTrimCommit = 1;
+ else
+ g_IGCTrimCommit = 0;
+#endif
+
+ if(flags & STARTUP_ETW)
+ g_fEnableETW = TRUE;
+
+ if(flags & STARTUP_ARM)
+ g_fEnableARM = TRUE;
+#endif // !FEATURE_CORECLR
+
+ GCHeap::InitializeHeapType((flags & STARTUP_SERVER_GC) != 0);
+
+#ifdef FEATURE_LOADER_OPTIMIZATION
+ g_dwGlobalSharePolicy = (flags&STARTUP_LOADER_OPTIMIZATION_MASK)>>1;
+#endif
+}
+#endif // CROSSGEN_COMPILE
+
+
+#ifdef FEATURE_PREJIT
+// BBSweepStartFunction is the first function to execute in the BBT sweeper thread.
+// It calls WatchForSweepEvent where we wait until a sweep occurs.
+DWORD __stdcall BBSweepStartFunction(LPVOID lpArgs)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_PREEMPTIVE;
+ }
+ CONTRACTL_END;
+
+ class CLRBBSweepCallback : public ICLRBBSweepCallback
+ {
+ virtual HRESULT WriteProfileData()
+ {
+ BEGIN_ENTRYPOINT_NOTHROW
+ WRAPPER_NO_CONTRACT;
+ Module::WriteAllModuleProfileData(false);
+ END_ENTRYPOINT_NOTHROW;
+ return S_OK;
+ }
+ } clrCallback;
+
+ EX_TRY
+ {
+ g_BBSweep.WatchForSweepEvents(&clrCallback);
+ }
+ EX_CATCH
+ {
+ }
+ EX_END_CATCH(RethrowTerminalExceptions)
+
+ return 0;
+}
+#endif // FEATURE_PREJIT
+
+
+//-----------------------------------------------------------------------------
+
+void InitGSCookie()
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_NOTRIGGER;
+ SO_TOLERANT;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ GSCookie * pGSCookiePtr = GetProcessGSCookiePtr();
+
+ DWORD oldProtection;
+ if(!ClrVirtualProtect((LPVOID)pGSCookiePtr, sizeof(GSCookie), PAGE_EXECUTE_READWRITE, &oldProtection))
+ {
+ ThrowLastError();
+ }
+
+#ifndef FEATURE_PAL
+ // The GSCookie cannot be in a writeable page
+ assert(((oldProtection & (PAGE_READWRITE|PAGE_WRITECOPY|PAGE_EXECUTE_READWRITE|
+ PAGE_EXECUTE_WRITECOPY|PAGE_WRITECOMBINE)) == 0));
+
+ // Forces VC cookie to be initialized.
+ void * pf = &__security_check_cookie;
+ pf = NULL;
+
+ GSCookie val = (GSCookie)(__security_cookie ^ GetTickCount());
+#else // !FEATURE_PAL
+ // REVIEW: Need something better for PAL...
+ GSCookie val = (GSCookie)GetTickCount();
+#endif // !FEATURE_PAL
+
+#ifdef _DEBUG
+ // In _DEBUG, always use the same value to make it easier to search for the cookie
+ val = (GSCookie) WIN64_ONLY(0x9ABCDEF012345678) NOT_WIN64(0x12345678);
+#endif
+
+ // To test if it is initialized. Also for ICorMethodInfo::getGSCookie()
+ if (val == 0)
+ val ++;
+ *pGSCookiePtr = val;
+
+ if(!ClrVirtualProtect((LPVOID)pGSCookiePtr, sizeof(GSCookie), oldProtection, &oldProtection))
+ {
+ ThrowLastError();
+ }
+}
+
+#if !defined(FEATURE_CORECLR) && !defined(CROSSGEN_COMPILE)
+void InitAssemblyUsageLogManager()
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ } CONTRACTL_END;
+
+ HRESULT hr = S_OK;
+
+ g_pIAssemblyUsageLogGac = NULL;
+
+ AssemblyUsageLogManager::Config config;
+
+ config.wszLogDir = NULL;
+ config.cLogBufferSize = 32768;
+#ifdef FEATURE_APPX
+ config.uiLogRefreshInterval = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_NGenAssemblyUsageLogRefreshInterval);
+#endif
+
+ NewArrayHolder<WCHAR> szCustomLogDir(CLRConfig::GetConfigValue(CLRConfig::INTERNAL_NGenAssemblyUsageLog));
+ config.wszLogDir = szCustomLogDir;
+
+ AssemblyUsageLogManager::Init(&config);
+
+ // Once the logger is initialized, create a log object for logging GAC loads.
+ AssemblyUsageLogManager::GetUsageLogForContext(W("fusion"), W("GAC"), &g_pIAssemblyUsageLogGac);
+}
+#endif
+
+// ---------------------------------------------------------------------------
+// %%Function: EEStartupHelper
+//
+// Parameters:
+// fFlags - Initialization flags for the engine. See the
+// EEStartupFlags enumerator for valid values.
+//
+// Returns:
+// S_OK - On success
+//
+// Description:
+// Reserved to initialize the EE runtime engine explicitly.
+// ---------------------------------------------------------------------------
+
+#ifndef IfFailGotoLog
+#define IfFailGotoLog(EXPR, LABEL) \
+do { \
+ hr = (EXPR);\
+ if(FAILED(hr)) { \
+ STRESS_LOG2(LF_STARTUP, LL_ALWAYS, "%s failed with code %x", #EXPR, hr);\
+ goto LABEL; \
+ } \
+ else \
+ STRESS_LOG1(LF_STARTUP, LL_ALWAYS, "%s completed", #EXPR);\
+} while (0)
+#endif
+
+#ifndef IfFailGoLog
+#define IfFailGoLog(EXPR) IfFailGotoLog(EXPR, ErrExit)
+#endif
+
+void EEStartupHelper(COINITIEE fFlags)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ } CONTRACTL_END;
+
+#ifdef ENABLE_CONTRACTS_IMPL
+ {
+ extern void ContractRegressionCheck();
+ ContractRegressionCheck();
+ }
+#endif
+
+ HRESULT hr = S_OK;
+ static ConfigDWORD breakOnEELoad;
+ EX_TRY
+ {
+ g_fEEInit = true;
+
+#ifndef CROSSGEN_COMPILE
+
+#ifdef _DEBUG
+ DisableGlobalAllocStore();
+#endif //_DEBUG
+
+#ifndef FEATURE_PAL
+ ::SetConsoleCtrlHandler(DbgCtrlCHandler, TRUE/*add*/);
+#endif
+
+#endif // CROSSGEN_COMPILE
+
+ // SString initialization
+ // This needs to be done before config because config uses SString::Empty()
+ SString::Startup();
+
+ // Initialize EEConfig
+ if (!g_pConfig)
+ {
+ IfFailGo(EEConfig::Setup());
+#if !defined(FEATURE_CORECLR) && !defined(CROSSGEN_COMPILE)
+ IfFailGo(InitializeHostConfigFile());
+ IfFailGo(g_pConfig->SetupConfiguration());
+#endif // !FEATURE_CORECLR && !CROSSGEN_COMPILE
+ }
+
+#ifndef CROSSGEN_COMPILE
+ // Initialize Numa and CPU group information
+ // Need to do this as early as possible. Used by creating object handle
+ // table inside Ref_Initialization() before GC is initialized.
+ NumaNodeInfo::InitNumaNodeInfo();
+ CPUGroupInfo::EnsureInitialized();
+
+#ifndef FEATURE_CORECLR
+ // Check in EEConfig whether a workload-specific set of performance defaults have been requested
+ // This needs to be done before InitializeStartupFlags in case one is to be overridden
+ PerformanceDefaults::InitializeForScenario(CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_PerformanceScenario));
+#endif
+
+ // Initialize global configuration settings based on startup flags
+ // This needs to be done before the EE has started
+ InitializeStartupFlags();
+
+ InitThreadManager();
+ STRESS_LOG0(LF_STARTUP, LL_ALWAYS, "Returned successfully from InitThreadManager");
+
+#ifdef FEATURE_EVENT_TRACE
+ // Initialize event tracing early so we can trace CLR startup time events.
+ InitializeEventTracing();
+
+ // Fire the EE startup ETW event
+ ETWFireEvent(EEStartupStart_V1);
+#endif // FEATURE_EVENT_TRACE
+
+#ifdef FEATURE_IPCMAN
+ // Give PerfMon a chance to hook up to us
+ // Do this both *before* and *after* ipcman init so corperfmonext.dll
+ // has a chance to release stale private blocks that IPCMan could collide with.
+ // do this early to maximize window between perfmon refresh and ipc block creation.
+ IPCFuncCallSource::DoThreadSafeCall();
+#endif // FEATURE_IPCMAN
+
+ InitGSCookie();
+
+ Frame::Init();
+
+#ifdef FEATURE_TESTHOOKS
+ IfFailGo(CLRTestHookManager::CheckConfig());
+#endif
+
+#endif // CROSSGEN_COMPILE
+
+#ifndef FEATURE_CORECLR
+ // Ensure initialization of Apphacks environment variables
+ GetGlobalCompatibilityFlags();
+#endif // !FEATURE_CORECLR
+
+#ifdef STRESS_LOG
+ if (REGUTIL::GetConfigDWORD_DontUse_(CLRConfig::UNSUPPORTED_StressLog, g_pConfig->StressLog ()) != 0) {
+ unsigned facilities = REGUTIL::GetConfigDWORD_DontUse_(CLRConfig::INTERNAL_LogFacility, LF_ALL);
+ unsigned level = REGUTIL::GetConfigDWORD_DontUse_(CLRConfig::EXTERNAL_LogLevel, LL_INFO1000);
+ unsigned bytesPerThread = REGUTIL::GetConfigDWORD_DontUse_(CLRConfig::UNSUPPORTED_StressLogSize, STRESSLOG_CHUNK_SIZE * 4);
+ unsigned totalBytes = REGUTIL::GetConfigDWORD_DontUse_(CLRConfig::UNSUPPORTED_TotalStressLogSize, STRESSLOG_CHUNK_SIZE * 1024);
+ StressLog::Initialize(facilities, level, bytesPerThread, totalBytes, GetModuleInst());
+ g_pStressLog = &StressLog::theLog;
+ }
+#endif
+
+#ifdef LOGGING
+ InitializeLogging();
+#endif
+
+#ifdef ENABLE_PERF_LOG
+ PerfLog::PerfLogInitialize();
+#endif //ENABLE_PERF_LOG
+
+#ifdef FEATURE_PERFMAP
+ PerfMap::Initialize();
+#endif
+
+ STRESS_LOG0(LF_STARTUP, LL_ALWAYS, "===================EEStartup Starting===================");
+
+#ifndef CROSSGEN_COMPILE
+#ifndef FEATURE_PAL
+ IfFailGoLog(EnsureRtlFunctions());
+#endif // !FEATURE_PAL
+ InitEventStore();
+#endif
+
+ // Fusion
+#ifdef FEATURE_FUSION
+ {
+ ETWOnStartup (FusionInit_V1, FusionInitEnd_V1);
+ IfFailGoLog(InitializeFusion());
+ }
+#else // FEATURE_FUSION
+ // Initialize the general Assembly Binder infrastructure
+ IfFailGoLog(CCoreCLRBinderHelper::Init());
+#endif // FEATURE_FUSION
+
+ if (g_pConfig != NULL)
+ {
+ IfFailGoLog(g_pConfig->sync());
+ }
+
+ // Fire the runtime information ETW event
+ ETW::InfoLog::RuntimeInformation(ETW::InfoLog::InfoStructs::Normal);
+
+ if (breakOnEELoad.val(CLRConfig::UNSUPPORTED_BreakOnEELoad) == 1)
+ {
+#ifdef _DEBUG
+ _ASSERTE(!"Start loading EE!");
+#else
+ DebugBreak();
+#endif
+ }
+
+#ifdef ENABLE_STARTUP_DELAY
+ PREFIX_ASSUME(NULL != g_pConfig);
+ if (g_pConfig->StartupDelayMS())
+ {
+ ClrSleepEx(g_pConfig->StartupDelayMS(), FALSE);
+ }
+#endif
+
+#if USE_DISASSEMBLER
+ if ((g_pConfig->GetGCStressLevel() & (EEConfig::GCSTRESS_INSTR_JIT | EEConfig::GCSTRESS_INSTR_NGEN)) != 0)
+ {
+ Disassembler::StaticInitialize();
+ if (!Disassembler::IsAvailable())
+ {
+ fprintf(stderr, "External disassembler is not available.\n");
+ IfFailGo(E_FAIL);
+ }
+ }
+#endif // USE_DISASSEMBLER
+
+ // Monitors, Crsts, and SimpleRWLocks all use the same spin heuristics
+ // Cache the (potentially user-overridden) values now so they are accessible from asm routines
+ InitializeSpinConstants();
+
+#ifndef CROSSGEN_COMPILE
+
+#if defined(STRESS_HEAP) && defined(_DEBUG) && !defined(FEATURE_CORECLR)
+ // TODO: is this still an issue?
+ // There is a race that causes random AVs on dual proc boxes
+ // that we suspect is due to memory coherancy problems (see Whidbey bug 2360)
+ // Avoid the issue by making the box effectively single proc.
+ if (GCStress<cfg_instr>::IsEnabled() &&
+ g_SystemInfo.dwNumberOfProcessors > 1)
+ SetProcessAffinityMask(GetCurrentProcess(),
+ 1 << (DbgGetEXETimeStamp() % g_SystemInfo.dwNumberOfProcessors));
+#endif // STRESS_HEAP && _DEBUG && !FEATURE_CORECLR
+
+#ifdef FEATURE_PREJIT
+ // Initialize the sweeper thread. THis is violating our rules with hosting
+ // so we only do it in the non-hosted case
+ if (g_pConfig->GetZapBBInstr() != NULL && !CLRTaskHosted())
+ {
+ DWORD threadID;
+ HANDLE hBBSweepThread = ::CreateThread(NULL,
+ 0,
+ (LPTHREAD_START_ROUTINE) BBSweepStartFunction,
+ NULL,
+ 0,
+ &threadID);
+ _ASSERTE(hBBSweepThread);
+ g_BBSweep.SetBBSweepThreadHandle(hBBSweepThread);
+ }
+#endif // FEATURE_PREJIT
+
+#ifdef FEATURE_IPCMAN
+ // Initialize all our InterProcess Communications with COM+
+ IfFailGoLog(InitializeIPCManager());
+#endif // FEATURE_IPCMAN
+
+#ifdef ENABLE_PERF_COUNTERS
+ hr = PerfCounters::Init();
+ _ASSERTE(SUCCEEDED(hr));
+ IfFailGo(hr);
+#endif
+
+#ifdef FEATURE_IPCMAN
+ // Marks the data in the IPC blocks as initialized so that readers know
+ // that it is safe to read data from the blocks
+ PublishIPCManager();
+#endif //FEATURE_IPCMAN
+
+#ifdef FEATURE_INTERPRETER
+ Interpreter::Initialize();
+#endif // FEATURE_INTERPRETER
+
+ StubManager::InitializeStubManagers();
+
+#ifndef FEATURE_PAL
+ {
+ // Record mscorwks geometry
+ PEDecoder pe(g_pMSCorEE);
+
+ g_runtimeLoadedBaseAddress = (SIZE_T)pe.GetBase();
+ g_runtimeVirtualSize = (SIZE_T)pe.GetVirtualSize();
+ InitCodeAllocHint(g_runtimeLoadedBaseAddress, g_runtimeVirtualSize, GetRandomInt(64));
+ }
+#endif // !FEATURE_PAL
+
+#endif // CROSSGEN_COMPILE
+
+ // Set up the cor handle map. This map is used to load assemblies in
+ // memory instead of using the normal system load
+ PEImage::Startup();
+
+ AccessCheckOptions::Startup();
+
+ MscorlibBinder::Startup();
+
+ Stub::Init();
+ StubLinkerCPU::Init();
+
+#ifndef CROSSGEN_COMPILE
+
+ // Initialize remoting
+#ifdef FEATURE_REMOTING
+ CRemotingServices::Initialize();
+#endif // FEATURE_REMOTING
+
+ // weak_short, weak_long, strong; no pin
+ if (!Ref_Initialize())
+ IfFailGo(E_OUTOFMEMORY);
+
+ // Initialize contexts
+ Context::Initialize();
+
+ g_pEEShutDownEvent = new CLREvent();
+ g_pEEShutDownEvent->CreateManualEvent(FALSE);
+
+#ifdef FEATURE_RWLOCK
+ // Initialize RWLocks
+ CRWLock::ProcessInit();
+#endif // FEATURE_RWLOCK
+
+#ifdef FEATURE_INCLUDE_ALL_INTERFACES
+ // Initialize debugger manager
+ CCLRDebugManager::ProcessInit();
+#endif // FEATURE_INCLUDE_ALL_INTERFACES
+
+#ifdef FEATURE_IPCMAN
+ // Initialize CCLRSecurityAttributeManager
+ CCLRSecurityAttributeManager::ProcessInit();
+#endif // FEATURE_IPCMAN
+
+ VirtualCallStubManager::InitStatic();
+
+ GCInterface::m_MemoryPressureLock.Init(CrstGCMemoryPressure);
+
+#ifndef FEATURE_CORECLR
+ // Initialize Assembly Usage Logger
+ InitAssemblyUsageLogManager();
+#endif
+
+#endif // CROSSGEN_COMPILE
+
+ // Setup the domains. Threads are started in a default domain.
+
+ // Static initialization
+ PEAssembly::Attach();
+ BaseDomain::Attach();
+ SystemDomain::Attach();
+
+ // Start up the EE intializing all the global variables
+ ECall::Init();
+
+ COMDelegate::Init();
+
+ ExecutionManager::Init();
+
+#ifndef CROSSGEN_COMPILE
+
+#ifndef FEATURE_PAL
+ // Watson initialization must precede InitializeDebugger() and InstallUnhandledExceptionFilter()
+ // because on CoreCLR when Waston is enabled, debugging service needs to be enabled and UEF will be used.
+ if (!InitializeWatson(fFlags))
+ {
+ IfFailGo(E_FAIL);
+ }
+
+ // Note: In Windows 7, the OS will take over the job of error reporting, and so most
+ // of our watson code should not be used. In such cases, we will however still need
+ // to provide some services to windows error reporting, such as computing bucket
+ // parameters for a managed unhandled exception.
+ if (RunningOnWin7() && IsWatsonEnabled() && !RegisterOutOfProcessWatsonCallbacks())
+ {
+ IfFailGo(E_FAIL);
+ }
+#endif // !FEATURE_PAL
+
+#ifdef DEBUGGING_SUPPORTED
+ if(!NingenEnabled())
+ {
+ // Initialize the debugging services. This must be done before any
+ // EE thread objects are created, and before any classes or
+ // modules are loaded.
+ InitializeDebugger(); // throws on error
+ }
+#endif // DEBUGGING_SUPPORTED
+
+#ifdef MDA_SUPPORTED
+ ManagedDebuggingAssistants::EEStartupActivation();
+#endif
+
+#ifdef PROFILING_SUPPORTED
+ // Initialize the profiling services.
+ hr = ProfilingAPIUtility::InitializeProfiling();
+
+ _ASSERTE(SUCCEEDED(hr));
+ IfFailGo(hr);
+#endif // PROFILING_SUPPORTED
+
+ InitializeExceptionHandling();
+
+ //
+ // Install our global exception filter
+ //
+ if (!InstallUnhandledExceptionFilter())
+ {
+ IfFailGo(E_FAIL);
+ }
+
+ // throws on error
+ SetupThread();
+
+#ifdef DEBUGGING_SUPPORTED
+ // Notify debugger once the first thread is created to finish initialization.
+ if (g_pDebugInterface != NULL)
+ {
+ g_pDebugInterface->StartupPhase2(GetThread());
+ }
+#endif
+
+#ifdef FEATURE_IPCMAN
+ // Give PerfMon a chance to hook up to us
+ // Do this both *before* and *after* ipcman init so corperfmonext.dll
+ // has a chance to release stale private blocks that IPCMan could collide with.
+ IPCFuncCallSource::DoThreadSafeCall();
+ STRESS_LOG0(LF_STARTUP, LL_ALWAYS, "Returned successfully from second call to IPCFuncCallSource::DoThreadSafeCall");
+#endif // FEATURE_IPCMAN
+
+ InitPreStubManager();
+
+#ifdef FEATURE_COMINTEROP
+ InitializeComInterop();
+#endif // FEATURE_COMINTEROP
+
+ StubHelpers::Init();
+ NDirect::Init();
+
+ // Before setting up the execution manager initialize the first part
+ // of the JIT helpers.
+ InitJITHelpers1();
+ InitJITHelpers2();
+
+ SyncBlockCache::Attach();
+
+ // Set up the sync block
+ SyncBlockCache::Start();
+
+ StackwalkCache::Init();
+
+ // Start up security
+ Security::Start();
+
+ AppDomain::CreateADUnloadStartEvent();
+
+ // In coreclr, clrjit is compiled into it, but SO work in clrjit has not been done.
+#ifdef FEATURE_STACK_PROBE
+ if (CLRHosted() && GetEEPolicy()->GetActionOnFailure(FAIL_StackOverflow) == eRudeUnloadAppDomain)
+ {
+ InitStackProbes();
+ }
+#endif
+
+ InitializeGarbageCollector();
+
+ InitializePinHandleTable();
+
+#ifdef DEBUGGING_SUPPORTED
+ // Make a call to publish the DefaultDomain for the debugger
+ // This should be done before assemblies/modules are loaded into it (i.e. SystemDomain::Init)
+ // and after its OK to switch GC modes and syncronize for sending events to the debugger.
+ // @dbgtodo synchronization: this can probably be simplified in V3
+ LOG((LF_CORDB | LF_SYNC | LF_STARTUP, LL_INFO1000, "EEStartup: adding default domain 0x%x\n",
+ SystemDomain::System()->DefaultDomain()));
+ SystemDomain::System()->PublishAppDomainAndInformDebugger(SystemDomain::System()->DefaultDomain());
+#endif
+
+#ifndef FEATURE_CORECLR
+ ExistingOobAssemblyList::Init();
+#endif
+
+#endif // CROSSGEN_COMPILE
+
+ SystemDomain::System()->Init();
+
+#ifdef PROFILING_SUPPORTED
+ // <TODO>This is to compensate for the DefaultDomain workaround contained in
+ // SystemDomain::Attach in which the first user domain is created before profiling
+ // services can be initialized. Profiling services cannot be moved to before the
+ // workaround because it needs SetupThread to be called.</TODO>
+
+ SystemDomain::NotifyProfilerStartup();
+#endif // PROFILING_SUPPORTED
+
+#ifndef CROSSGEN_COMPILE
+ if (CLRHosted()
+#ifdef _DEBUG
+ || ((fFlags & COINITEE_DLL) == 0 &&
+ g_pConfig->GetHostTestADUnload())
+#endif
+ ) {
+ // If we are hosted, a host may specify unloading AD when a managed allocation in
+ // critical region fails. We need to precreate a thread to unload AD.
+ AppDomain::CreateADUnloadWorker();
+ }
+#endif // CROSSGEN_COMPILE
+
+ g_fEEInit = false;
+
+ SystemDomain::System()->DefaultDomain()->LoadSystemAssemblies();
+
+ SystemDomain::System()->DefaultDomain()->SetupSharedStatics();
+
+#ifdef _DEBUG
+ APIThreadStress::SetThreadStressCount(g_pConfig->GetAPIThreadStressCount());
+#endif
+#ifdef FEATURE_STACK_SAMPLING
+ StackSampler::Init();
+#endif
+
+#ifndef CROSSGEN_COMPILE
+ if (!NingenEnabled())
+ {
+ // Perform any once-only SafeHandle initialization.
+ SafeHandle::Init();
+ }
+
+#ifdef FEATURE_MINIMETADATA_IN_TRIAGEDUMPS
+ // retrieve configured max size for the mini-metadata buffer (defaults to 64KB)
+ g_MiniMetaDataBuffMaxSize = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_MiniMdBufferCapacity);
+ // align up to OS_PAGE_SIZE, with a maximum of 1 MB
+ g_MiniMetaDataBuffMaxSize = (DWORD) min(ALIGN_UP(g_MiniMetaDataBuffMaxSize, OS_PAGE_SIZE), 1024 * 1024);
+ // allocate the buffer. this is never touched while the process is running, so it doesn't
+ // contribute to the process' working set. it is needed only as a "shadow" for a mini-metadata
+ // buffer that will be set up and reported / updated in the Watson process (the
+ // DacStreamsManager class coordinates this)
+ g_MiniMetaDataBuffAddress = (TADDR) ClrVirtualAlloc(NULL,
+ g_MiniMetaDataBuffMaxSize, MEM_COMMIT, PAGE_READWRITE);
+#endif // FEATURE_MINIMETADATA_IN_TRIAGEDUMPS
+
+ // Load mscorsn.dll if the app requested the legacy mode in its configuration file.
+ if (g_pConfig->LegacyLoadMscorsnOnStartup())
+ IfFailGo(LoadMscorsn());
+
+#endif // CROSSGEN_COMPILE
+
+ g_fEEStarted = TRUE;
+ g_EEStartupStatus = S_OK;
+ hr = S_OK;
+ STRESS_LOG0(LF_STARTUP, LL_ALWAYS, "===================EEStartup Completed===================");
+
+#if defined(_DEBUG) && !defined(CROSSGEN_COMPILE)
+
+ //if g_fEEStarted was false when we loaded the System Module, we did not run ExpandAll on it. In
+ //this case, make sure we run ExpandAll here. The rationale is that if we Jit before g_fEEStarted
+ //is true, we can't initialize Com, so we can't jit anything that uses Com types. Also, it's
+ //probably not safe to Jit while g_fEEStarted is false.
+ //
+ //Also, if you run this it's possible we'll call CoInitialize, which defaults to MTA. This might
+ //mess up an application that uses STA. However, this mode is only supported for certain limited
+ //jit testing scenarios, so it can live with the limitation.
+ if (g_pConfig->ExpandModulesOnLoad())
+ {
+ SystemDomain::SystemModule()->ExpandAll();
+ }
+
+ //For a similar reason, let's not run VerifyAllOnLoad either.
+#ifdef FEATURE_CORECLR
+ if (g_pConfig->VerifyModulesOnLoad())
+ {
+ SystemDomain::SystemModule()->VerifyAllMethods();
+ }
+#endif //FEATURE_CORECLR
+
+ // Perform mscorlib consistency check if requested
+ g_Mscorlib.CheckExtended();
+
+#endif // _DEBUG && !CROSSGEN_COMPILE
+
+ErrExit: ;
+ }
+ EX_CATCH
+ {
+#ifdef CROSSGEN_COMPILE
+ // for minimal impact we won't update hr for regular builds
+ hr = GET_EXCEPTION()->GetHR();
+ _ASSERTE(FAILED(hr));
+ StackSString exceptionMessage;
+ GET_EXCEPTION()->GetMessage(exceptionMessage);
+ fprintf(stderr, "%S\n", exceptionMessage.GetUnicode());
+#endif // CROSSGEN_COMPILE
+ }
+ EX_END_CATCH(RethrowTerminalExceptionsWithInitCheck)
+
+ if (!g_fEEStarted) {
+ if (g_fEEInit)
+ g_fEEInit = false;
+
+ if (!FAILED(hr))
+ hr = E_FAIL;
+
+ g_EEStartupStatus = hr;
+ }
+
+ if (breakOnEELoad.val(CLRConfig::UNSUPPORTED_BreakOnEELoad) == 2)
+ {
+#ifdef _DEBUG
+ _ASSERTE(!"Done loading EE!");
+#else
+ DebugBreak();
+#endif
+ }
+
+}
+
+LONG FilterStartupException(PEXCEPTION_POINTERS p, PVOID pv)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ PRECONDITION(CheckPointer(p));
+ PRECONDITION(CheckPointer(pv));
+ } CONTRACTL_END;
+
+ g_EEStartupStatus = (HRESULT)p->ExceptionRecord->ExceptionInformation[0];
+
+ // Make sure we got a failure code in this case
+ if (!FAILED(g_EEStartupStatus))
+ g_EEStartupStatus = E_FAIL;
+
+ // Initializations has failed so reset the g_fEEInit flag.
+ g_fEEInit = false;
+
+ if (p->ExceptionRecord->ExceptionCode == BOOTUP_EXCEPTION_COMPLUS)
+ {
+ // Don't ever handle the exception in a checked build
+#ifndef _DEBUG
+ return EXCEPTION_EXECUTE_HANDLER;
+#endif
+ }
+
+ return EXCEPTION_CONTINUE_SEARCH;
+}
+
+// EEStartup is responcible for all the one time intialization of the runtime. Some of the highlights of
+// what it does include
+// * Creates the default and shared, appdomains.
+// * Loads mscorlib.dll and loads up the fundamental types (System.Object ...)
+//
+// see code:EEStartup#TableOfContents for more on the runtime in general.
+// see code:#EEShutdown for a analagous routine run during shutdown.
+//
+HRESULT EEStartup(COINITIEE fFlags)
+{
+ // Cannot use normal contracts here because of the PAL_TRY.
+ STATIC_CONTRACT_NOTHROW;
+
+ _ASSERTE(!g_fEEStarted && !g_fEEInit && SUCCEEDED (g_EEStartupStatus));
+
+ PAL_TRY(COINITIEE *, pfFlags, &fFlags)
+ {
+#ifndef CROSSGEN_COMPILE
+ InitializeClrNotifications();
+#ifdef FEATURE_PAL
+ InitializeJITNotificationTable();
+ DacGlobals::Initialize();
+#endif
+#endif // CROSSGEN_COMPILE
+
+ EEStartupHelper(*pfFlags);
+ }
+ PAL_EXCEPT_FILTER (FilterStartupException)
+ {
+ // The filter should have set g_EEStartupStatus to a failure HRESULT.
+ _ASSERTE(FAILED(g_EEStartupStatus));
+ }
+ PAL_ENDTRY
+
+#ifndef CROSSGEN_COMPILE
+ if(SUCCEEDED(g_EEStartupStatus) && (fFlags & COINITEE_MAIN) == 0)
+ g_EEStartupStatus = SystemDomain::SetupDefaultDomainNoThrow();
+#endif
+
+ return g_EEStartupStatus;
+}
+
+
+#ifndef CROSSGEN_COMPILE
+
+#ifdef FEATURE_COMINTEROP
+
+void InnerCoEEShutDownCOM()
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ } CONTRACTL_END;
+
+ static LONG AlreadyDone = -1;
+
+ if (g_fEEStarted != TRUE)
+ return;
+
+ if (FastInterlockIncrement(&AlreadyDone) != 0)
+ return;
+
+ g_fShutDownCOM = true;
+
+ // Release IJupiterGCMgr *
+ RCWWalker::OnEEShutdown();
+
+ // Release all of the RCWs in all contexts in all caches.
+ ReleaseRCWsInCaches(NULL);
+
+ // Release all marshaling data in all AppDomains
+ AppDomainIterator i(TRUE);
+ while (i.Next())
+ i.GetDomain()->DeleteMarshalingData();
+
+ // Release marshaling data in shared domain as well
+ SharedDomain::GetDomain()->DeleteMarshalingData();
+
+#ifdef FEATURE_APPX
+ // Cleanup cached factory pointer in SynchronizationContextNative
+ SynchronizationContextNative::Cleanup();
+#endif
+}
+
+// ---------------------------------------------------------------------------
+// %%Function: CoEEShutdownCOM()
+//
+// Parameters:
+// none
+//
+// Returns:
+// Nothing
+//
+// Description:
+// COM Objects shutdown stuff should be done here
+// ---------------------------------------------------------------------------
+void STDMETHODCALLTYPE CoEEShutDownCOM()
+{
+
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_TRIGGERS;
+ MODE_PREEMPTIVE;
+ ENTRY_POINT;
+ } CONTRACTL_END;
+
+ if (g_fEEStarted != TRUE)
+ return;
+
+ HRESULT hr;
+ BEGIN_EXTERNAL_ENTRYPOINT(&hr)
+
+ InnerCoEEShutDownCOM();
+
+ END_EXTERNAL_ENTRYPOINT;
+
+ // API doesn't allow us to communicate a failure HRESULT. MDAs can
+ // be enabled to catch failure inside CanRunManagedCode.
+ // _ASSERTE(SUCCEEDED(hr));
+}
+
+#endif // FEATURE_COMINTEROP
+
+// ---------------------------------------------------------------------------
+// %%Function: ForceEEShutdown()
+//
+// Description: Force the EE to shutdown now.
+//
+// Note: returns when sca is SCA_ReturnWhenShutdownComplete.
+// ---------------------------------------------------------------------------
+void ForceEEShutdown(ShutdownCompleteAction sca)
+{
+ WRAPPER_NO_CONTRACT;
+
+ // Don't bother to take the lock for this case.
+
+ STRESS_LOG0(LF_STARTUP, INFO3, "EEShutdown invoked from ForceEEShutdown");
+ EEPolicy::HandleExitProcess(sca);
+}
+
+//---------------------------------------------------------------------------
+// %%Function: ExternalShutdownHelper
+//
+// Parameters:
+// int exitCode :: process exit code
+// ShutdownCompleteAction sca :: indicates whether ::ExitProcess() is
+// called or if the function returns.
+//
+// Returns:
+// Nothing
+//
+// Description:
+// This is a helper shared by CorExitProcess and ShutdownRuntimeWithoutExiting
+// which causes the runtime to shutdown after the appropriate checks.
+// ---------------------------------------------------------------------------
+static void ExternalShutdownHelper(int exitCode, ShutdownCompleteAction sca)
+{
+ CONTRACTL {
+ NOTHROW;
+ GC_TRIGGERS;
+ MODE_ANY;
+ ENTRY_POINT;
+ } CONTRACTL_END;
+
+ CONTRACT_VIOLATION(GCViolation | ModeViolation | SOToleranceViolation);
+
+ if (g_fEEShutDown || !g_fEEStarted)
+ return;
+
+ if (HasIllegalReentrancy())
+ {
+ return;
+ }
+ else
+ if (!CanRunManagedCode())
+ {
+ return;
+ }
+
+ // The exit code for the process is communicated in one of two ways. If the
+ // entrypoint returns an 'int' we take that. Otherwise we take a latched
+ // process exit code. This can be modified by the app via System.SetExitCode().
+ SetLatchedExitCode(exitCode);
+
+#ifndef FEATURE_CORECLR // no shim
+ // Bump up the ref-count on the module
+ for (int i =0; i<6; i++)
+ CLRLoadLibrary(MSCOREE_SHIM_W);
+#endif // FEATURE_CORECLR
+
+ ForceEEShutdown(sca);
+
+ // @TODO: If we cannot run ManagedCode, BEGIN_EXTERNAL_ENTRYPOINT will skip
+ // the shutdown. We could call ::ExitProcess in that failure case, but that
+ // would violate our hosting agreement. We are supposed to go through EEPolicy::
+ // HandleExitProcess(). Is this legal if !CanRunManagedCode()?
+
+}
+
+//---------------------------------------------------------------------------
+// %%Function: void STDMETHODCALLTYPE CorExitProcess(int exitCode)
+//
+// Parameters:
+// int exitCode :: process exit code
+//
+// Returns:
+// Nothing
+//
+// Description:
+// COM Objects shutdown stuff should be done here
+// ---------------------------------------------------------------------------
+extern "C" void STDMETHODCALLTYPE CorExitProcess(int exitCode)
+{
+ WRAPPER_NO_CONTRACT;
+
+ ExternalShutdownHelper(exitCode, SCA_ExitProcessWhenShutdownComplete);
+}
+
+//---------------------------------------------------------------------------
+// %%Function: ShutdownRuntimeWithoutExiting
+//
+// Parameters:
+// int exitCode :: process exit code
+//
+// Returns:
+// Nothing
+//
+// Description:
+// This is a helper used only by the v4+ Shim to shutdown this runtime and
+// and return when the work has completed. It is exposed to the Shim via
+// GetCLRFunction.
+// ---------------------------------------------------------------------------
+void ShutdownRuntimeWithoutExiting(int exitCode)
+{
+ WRAPPER_NO_CONTRACT;
+
+ ExternalShutdownHelper(exitCode, SCA_ReturnWhenShutdownComplete);
+}
+
+//---------------------------------------------------------------------------
+// %%Function: IsRuntimeStarted
+//
+// Parameters:
+// pdwStartupFlags: out parameter that is set to the startup flags if the
+// runtime is started.
+//
+// Returns:
+// TRUE if the runtime has been started, FALSE otherwise.
+//
+// Description:
+// This is a helper used only by the v4+ Shim to determine if this runtime
+// has ever been started. It is exposed ot the Shim via GetCLRFunction.
+// ---------------------------------------------------------------------------
+BOOL IsRuntimeStarted(DWORD *pdwStartupFlags)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ if (pdwStartupFlags != NULL) // this parameter is optional
+ {
+ *pdwStartupFlags = 0;
+#ifndef FEATURE_CORECLR
+ if (g_fEEStarted)
+ {
+ *pdwStartupFlags = CorHost2::GetStartupFlags();
+ }
+#endif
+ }
+ return g_fEEStarted;
+}
+
+static bool WaitForEndOfShutdown_OneIteration()
+{
+ CONTRACTL{
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_PREEMPTIVE;
+ } CONTRACTL_END;
+
+ // We are shutting down. GC triggers does not have any effect now.
+ CONTRACT_VIOLATION(GCViolation);
+
+ // If someone calls EEShutDown while holding OS loader lock, the thread we created for shutdown
+ // won't start running. This is a deadlock we can not fix. Instead, we timeout and continue the
+ // current thread.
+ DWORD timeout = GetEEPolicy()->GetTimeout(OPR_ProcessExit);
+ timeout *= 2;
+ ULONGLONG endTime = CLRGetTickCount64() + timeout;
+ bool done = false;
+
+ EX_TRY
+ {
+ ULONGLONG curTime = CLRGetTickCount64();
+ if (curTime > endTime)
+ {
+ done = true;
+ }
+ else
+ {
+#ifdef PROFILING_SUPPORTED
+ if (CORProfilerPresent())
+ {
+ // A profiler is loaded, so just wait without timeout. This allows
+ // profilers to complete potentially lengthy post processing, without the
+ // CLR killing them off first. The Office team's server memory profiler,
+ // for example, does a lot of post-processing that can exceed the 80
+ // second imit we normally impose here. The risk of waiting without
+ // timeout is that, if there really is a deadlock, shutdown will hang.
+ // Since that will only happen if a profiler is loaded, that is a
+ // reasonable compromise
+ timeout = INFINITE;
+ }
+ else
+#endif //PROFILING_SUPPORTED
+ {
+ timeout = static_cast<DWORD>(endTime - curTime);
+ }
+ DWORD status = g_pEEShutDownEvent->Wait(timeout,TRUE);
+ if (status == WAIT_OBJECT_0 || status == WAIT_TIMEOUT)
+ {
+ done = true;
+ }
+ else
+ {
+ done = false;
+ }
+ }
+ }
+ EX_CATCH
+ {
+ }
+ EX_END_CATCH(SwallowAllExceptions);
+ return done;
+}
+
+void WaitForEndOfShutdown()
+{
+ CONTRACTL{
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_PREEMPTIVE;
+ } CONTRACTL_END;
+
+ // We are shutting down. GC triggers does not have any effect now.
+ CONTRACT_VIOLATION(GCViolation);
+
+ Thread *pThread = GetThread();
+ // After a thread is blocked in WaitForEndOfShutdown, the thread should not enter runtime again,
+ // and block at WaitForEndOfShutdown again.
+ if (pThread)
+ {
+ _ASSERTE(!pThread->HasThreadStateNC(Thread::TSNC_BlockedForShutdown));
+ pThread->SetThreadStateNC(Thread::TSNC_BlockedForShutdown);
+ }
+
+ while (!WaitForEndOfShutdown_OneIteration());
+}
+
+// ---------------------------------------------------------------------------
+// Function: EEShutDownHelper(BOOL fIsDllUnloading)
+//
+// The real meat of shut down happens here. See code:#EEShutDown for details, including
+// what fIsDllUnloading means.
+//
+void STDMETHODCALLTYPE EEShutDownHelper(BOOL fIsDllUnloading)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_TRIGGERS;
+ MODE_ANY;
+ } CONTRACTL_END;
+
+ // Used later for a callback.
+ CEEInfo ceeInf;
+
+ if(fIsDllUnloading)
+ {
+ ETW::EnumerationLog::ProcessShutdown();
+ }
+
+#if defined(FEATURE_CAS_POLICY) || defined(FEATURE_COMINTEROP)
+ // Get the current thread.
+ Thread * pThisThread = GetThread();
+#endif
+
+ // If the process is detaching then set the global state.
+ // This is used to get around FreeLibrary problems.
+ if(fIsDllUnloading)
+ g_fProcessDetach = true;
+
+ if (IsDbgHelperSpecialThread())
+ {
+ // Our debugger helper thread does not allow Thread object to be set up.
+ // We should not run shutdown code on debugger helper thread.
+ _ASSERTE(fIsDllUnloading);
+ return;
+ }
+
+#ifdef _DEBUG
+ // stop API thread stress
+ APIThreadStress::SetThreadStressCount(0);
+#endif
+
+ STRESS_LOG1(LF_STARTUP, LL_INFO10, "EEShutDown entered unloading = %d", fIsDllUnloading);
+
+#ifdef _DEBUG
+ if (_DbgBreakCount)
+ _ASSERTE(!"An assert was hit before EE Shutting down");
+
+ if (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_BreakOnEEShutdown))
+ _ASSERTE(!"Shutting down EE!");
+#endif
+
+#ifdef DEBUGGING_SUPPORTED
+ // This is a nasty, terrible, horrible thing. If we're being
+ // called from our DLL main, then the odds are good that our DLL
+ // main has been called as the result of some person calling
+ // ExitProcess. That rips the debugger helper thread away very
+ // ungracefully. This check is an attempt to recognize that case
+ // and avoid the impending hang when attempting to get the helper
+ // thread to do things for us.
+ if ((g_pDebugInterface != NULL) && g_fProcessDetach)
+ g_pDebugInterface->EarlyHelperThreadDeath();
+#endif // DEBUGGING_SUPPORTED
+
+ BOOL fFinalizeOK = FALSE;
+
+ EX_TRY
+ {
+ ClrFlsSetThreadType(ThreadType_Shutdown);
+
+ if (!fIsDllUnloading)
+ {
+ ProcessEventForHost(Event_ClrDisabled, NULL);
+ }
+ else if (g_fEEShutDown)
+ {
+ // I'm in the final shutdown and the first part has already been run.
+ goto part2;
+ }
+
+ // Indicate the EE is the shut down phase.
+ g_fEEShutDown |= ShutDown_Start;
+
+ fFinalizeOK = TRUE;
+
+ // Terminate the BBSweep thread
+ g_BBSweep.ShutdownBBSweepThread();
+
+ // We perform the final GC only if the user has requested it through the GC class.
+ // We should never do the final GC for a process detach
+ if (!g_fProcessDetach && !g_fFastExitProcess)
+ {
+ g_fEEShutDown |= ShutDown_Finalize1;
+ FinalizerThread::EnableFinalization();
+ fFinalizeOK = FinalizerThread::FinalizerThreadWatchDog();
+ }
+
+#ifndef FEATURE_CORECLR
+ if (!g_fFastExitProcess)
+ {
+ // Log usage data to disk. (Only do this in normal shutdown scenarios, and not involving ngen)
+ if (!IsCompilationProcess())
+ AssemblyUsageLogManager::GenerateLog(AssemblyUsageLogManager::GENERATE_LOG_FLAGS_NONE);
+ }
+#endif
+
+ // Ok. Let's stop the EE.
+ if (!g_fProcessDetach)
+ {
+ // Convert key locks into "shutdown" mode. A lock in shutdown mode means:
+ // - Only the finalizer/helper/shutdown threads will be able to take the the lock.
+ // - Any other thread that tries takes it will just get redirected to an endless WaitForEndOfShutdown().
+ //
+ // The only managed code that should run after this point is the finalizers for shutdown.
+ // We convert locks needed for running + debugging such finalizers. Since such locks may need to be
+ // juggled between multiple threads (finalizer/helper/shutdown), no single thread can take the
+ // lock and not give it up.
+ //
+ // Each lock needs its own shutdown flag (they can't all be converted at once).
+ // To avoid deadlocks, we need to convert locks in order of crst level (biggest first).
+
+ // Notify the debugger that we're going into shutdown to convert debugger-lock to shutdown.
+ if (g_pDebugInterface != NULL)
+ {
+ g_pDebugInterface->LockDebuggerForShutdown();
+ }
+
+ // This call will convert the ThreadStoreLock into "shutdown" mode, just like the debugger lock above.
+ g_fEEShutDown |= ShutDown_Finalize2;
+ if (fFinalizeOK)
+ {
+ fFinalizeOK = FinalizerThread::FinalizerThreadWatchDog();
+ }
+
+ if (!fFinalizeOK)
+ {
+ // One of the calls to FinalizerThreadWatchDog failed due to timeout, so we need to prevent
+ // any thread from running managed code, including the finalizer.
+ ThreadSuspend::SuspendEE(ThreadSuspend::SUSPEND_FOR_SHUTDOWN);
+ g_fSuspendOnShutdown = TRUE;
+ g_fSuspendFinalizerOnShutdown = TRUE;
+ ThreadStore::TrapReturningThreads(TRUE);
+ ThreadSuspend::RestartEE(FALSE, TRUE);
+ }
+ }
+
+#ifdef FEATURE_EVENT_TRACE
+ // Flush managed object allocation logging data.
+ // We do this after finalization is complete and returning threads have been trapped, so that
+ // no there will be no more managed allocations and no more GCs which will manipulate the
+ // allocation sampling data structures.
+ ETW::TypeSystemLog::FlushObjectAllocationEvents();
+#endif // FEATURE_EVENT_TRACE
+
+#ifdef FEATURE_PERFMAP
+ // Flush and close the perf map file.
+ PerfMap::Destroy();
+#endif
+
+#ifdef FEATURE_PREJIT
+ // If we're doing basic block profiling, we need to write the log files to disk.
+
+ static BOOL fIBCLoggingDone = FALSE;
+ if (!fIBCLoggingDone)
+ {
+ if (g_IBCLogger.InstrEnabled())
+ Module::WriteAllModuleProfileData(true);
+
+ fIBCLoggingDone = TRUE;
+ }
+
+#endif // FEATURE_PREJIT
+
+ ceeInf.JitProcessShutdownWork(); // Do anything JIT-related that needs to happen at shutdown.
+
+#ifdef FEATURE_INTERPRETER
+ // This will check a flag and do nothing if not enabled.
+ Interpreter::PrintPostMortemData();
+#endif // FEATURE_INTERPRETER
+
+ FastInterlockExchange((LONG*)&g_fForbidEnterEE, TRUE);
+
+ if (g_fProcessDetach)
+ {
+ ThreadStore::TrapReturningThreads(TRUE);
+ }
+
+ if (!g_fProcessDetach && !fFinalizeOK)
+ {
+ goto lDone;
+ }
+
+#ifdef PROFILING_SUPPORTED
+ // If profiling is enabled, then notify of shutdown first so that the
+ // profiler can make any last calls it needs to. Do this only if we
+ // are not detaching
+
+ if (CORProfilerPresent())
+ {
+ // If EEShutdown is not being called due to a ProcessDetach event, so
+ // the profiler should still be present
+ if (!g_fProcessDetach)
+ {
+ BEGIN_PIN_PROFILER(CORProfilerPresent());
+ GCX_PREEMP();
+ g_profControlBlock.pProfInterface->Shutdown();
+ END_PIN_PROFILER();
+ }
+
+ g_fEEShutDown |= ShutDown_Profiler;
+
+ // Free the interface objects.
+ ProfilingAPIUtility::TerminateProfiling();
+ }
+#endif // PROFILING_SUPPORTED
+
+#ifndef FEATURE_CORECLR
+ // CoEEShutDownCOM moved to
+ // the Finalizer thread. See bug 87809
+ if (!g_fProcessDetach && !g_fFastExitProcess)
+ {
+ g_fEEShutDown |= ShutDown_COM;
+ if (fFinalizeOK)
+ {
+ FinalizerThread::FinalizerThreadWatchDog();
+ }
+ }
+#ifdef _DEBUG
+ else
+ g_fEEShutDown |= ShutDown_COM;
+#endif
+#endif //FEATURE_CORECLR
+
+#ifdef _DEBUG
+ g_fEEShutDown |= ShutDown_SyncBlock;
+#endif
+ {
+ // From here on out we might call stuff that violates mode requirements, but we ignore these
+ // because we are shutting down.
+ CONTRACT_VIOLATION(ModeViolation);
+
+#ifdef FEATURE_CAS_POLICY
+ // Save the security policy cache as necessary.
+ if (!g_fProcessDetach || pThisThread != NULL)
+ {
+ // If process shutdown has started, it is not safe to create Thread object which is needed
+ // by the following call.
+ Security::SaveCache();
+ }
+#endif
+#ifdef FEATURE_COMINTEROP
+ // We need to call CoUninitialize in part one to ensure orderly shutdown of COM dlls.
+ if (!g_fFastExitProcess)
+ {
+ if (pThisThread!= NULL)
+ {
+ pThisThread->CoUninitialize();
+ }
+ }
+#endif // FEATURE_COMINTEROP
+ }
+
+ // This is the end of Part 1.
+
+part2:
+ // If process shutdown is in progress and Crst locks to be used in shutdown phase 2
+ // are already in use, then skip phase 2. This will happen only when those locks
+ // are orphaned. In Vista, the penalty for attempting to enter such locks is
+ // instant process termination.
+ if (g_fProcessDetach)
+ {
+ // The assert below is a bit too aggresive and has generally brought cases that have been race conditions
+ // and not easily reproed to validate a bug. A typical race scenario is when there are two threads,
+ // T1 and T2, with T2 having taken a lock (e.g. SystemDomain lock), the OS terminates
+ // T2 for some reason. Later, when we enter the shutdown thread, we would assert on such
+ // a lock leak, but there is not much we can do since the OS wont notify us prior to thread
+ // termination. And this is not even a user bug.
+ //
+ // Converting it to a STRESS LOG to reduce noise, yet keep things in radar if they need
+ // to be investigated.
+ //_ASSERTE_MSG(g_ShutdownCrstUsageCount == 0, "Some locks to be taken during shutdown may already be orphaned!");
+ if (g_ShutdownCrstUsageCount > 0)
+ {
+ STRESS_LOG0(LF_STARTUP, LL_INFO10, "Some locks to be taken during shutdown may already be orphaned!");
+ goto lDone;
+ }
+ }
+
+ {
+ CONTRACT_VIOLATION(ModeViolation);
+
+ // On the new plan, we only do the tear-down under the protection of the loader
+ // lock -- after the OS has stopped all other threads.
+ if (fIsDllUnloading && (g_fEEShutDown & ShutDown_Phase2) == 0)
+ {
+ g_fEEShutDown |= ShutDown_Phase2;
+
+ // Shutdown finalizer before we suspend all background threads. Otherwise we
+ // never get to finalize anything. Obviously.
+
+#ifdef _DEBUG
+ if (_DbgBreakCount)
+ _ASSERTE(!"An assert was hit After Finalizer run");
+#endif
+
+ // No longer process exceptions
+ g_fNoExceptions = true;
+
+ //
+ // Remove our global exception filter. If it was NULL before, we want it to be null now.
+ //
+ UninstallUnhandledExceptionFilter();
+
+ // <TODO>@TODO: This does things which shouldn't occur in part 2. Namely,
+ // calling managed dll main callbacks (AppDomain::SignalProcessDetach), and
+ // RemoveAppDomainFromIPC.
+ //
+ // (If we move those things to earlier, this can be called only if fShouldWeCleanup.)</TODO>
+ if (!g_fFastExitProcess)
+ {
+ SystemDomain::DetachBegin();
+ }
+
+
+#ifdef DEBUGGING_SUPPORTED
+ // Terminate the debugging services.
+ TerminateDebugger();
+#endif // DEBUGGING_SUPPORTED
+
+ StubManager::TerminateStubManagers();
+
+#ifdef FEATURE_INTERPRETER
+ Interpreter::Terminate();
+#endif // FEATURE_INTERPRETER
+
+#ifdef SHOULD_WE_CLEANUP
+ if (!g_fFastExitProcess)
+ {
+ Ref_Shutdown(); // shut down the handle table
+ }
+#endif /* SHOULD_WE_CLEANUP */
+
+#ifdef ENABLE_PERF_COUNTERS
+ // Terminate Perf Counters as late as we can (to get the most data)
+ PerfCounters::Terminate();
+#endif // ENABLE_PERF_COUNTERS
+
+ //@TODO: find the right place for this
+ VirtualCallStubManager::UninitStatic();
+
+#ifdef FEATURE_IPCMAN
+ // Terminate the InterProcess Communications with COM+
+ TerminateIPCManager();
+#endif // FEATURE_IPCMAN
+
+#ifdef ENABLE_PERF_LOG
+ PerfLog::PerfLogDone();
+#endif //ENABLE_PERF_LOG
+
+#ifdef FEATURE_IPCMAN
+ // Give PerfMon a chance to hook up to us
+ // Have perfmon resync list *after* we close IPC so that it will remove
+ // this process
+ IPCFuncCallSource::DoThreadSafeCall();
+#endif // FEATURE_IPCMAN
+
+ Frame::Term();
+
+ if (!g_fFastExitProcess)
+ {
+ SystemDomain::DetachEnd();
+ }
+
+ TerminateStackProbes();
+
+ // Unregister our vectored exception and continue handlers from the OS.
+ // This will ensure that if any other DLL unload (after ours) has an exception,
+ // we wont attempt to process that exception (which could lead to various
+ // issues including AV in the runtime).
+ //
+ // This should be done:
+ //
+ // 1) As the last action during the shutdown so that any unexpected AVs
+ // in the runtime during shutdown do result in FailFast in VEH.
+ //
+ // 2) Only when the runtime is processing DLL_PROCESS_DETACH.
+ CLRRemoveVectoredHandlers();
+
+#if USE_DISASSEMBLER
+ Disassembler::StaticClose();
+#endif // USE_DISASSEMBLER
+
+#ifdef _DEBUG
+ if (_DbgBreakCount)
+ _ASSERTE(!"EE Shutting down after an assert");
+#endif
+
+
+#ifdef LOGGING
+ extern unsigned FcallTimeHist[11];
+#endif
+ LOG((LF_STUBS, LL_INFO10, "FcallHist %3d %3d %3d %3d %3d %3d %3d %3d %3d %3d %3d\n",
+ FcallTimeHist[0], FcallTimeHist[1], FcallTimeHist[2], FcallTimeHist[3],
+ FcallTimeHist[4], FcallTimeHist[5], FcallTimeHist[6], FcallTimeHist[7],
+ FcallTimeHist[8], FcallTimeHist[9], FcallTimeHist[10]));
+
+ WriteJitHelperCountToSTRESSLOG();
+
+ STRESS_LOG0(LF_STARTUP, LL_INFO10, "EEShutdown shutting down logging");
+
+#if 0 // Dont clean up the stress log, so that even at process exit we have a log (after all the process is going away
+ if (!g_fFastExitProcess)
+ StressLog::Terminate(TRUE);
+#endif
+
+ if (g_pConfig != NULL)
+ g_pConfig->Cleanup();
+
+#ifdef LOGGING
+ ShutdownLogging();
+#endif
+ }
+ }
+
+ lDone: ;
+ }
+ EX_CATCH
+ {
+ }
+ EX_END_CATCH(SwallowAllExceptions);
+
+ ClrFlsClearThreadType(ThreadType_Shutdown);
+ if (!g_fProcessDetach)
+ {
+ g_pEEShutDownEvent->Set();
+ }
+}
+
+
+#ifdef FEATURE_COMINTEROP
+
+BOOL IsThreadInSTA()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_TRIGGERS;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ // If ole32.dll is not loaded
+ if (WszGetModuleHandle(W("ole32.dll")) == NULL)
+ {
+ return FALSE;
+ }
+
+ BOOL fInSTA = TRUE;
+ // To be conservative, check if finalizer thread is around
+ EX_TRY
+ {
+ Thread *pFinalizerThread = FinalizerThread::GetFinalizerThread();
+ if (!pFinalizerThread || pFinalizerThread->Join(0, FALSE) != WAIT_TIMEOUT)
+ {
+ fInSTA = FALSE;
+ }
+ }
+ EX_CATCH
+ {
+ }
+ EX_END_CATCH(SwallowAllExceptions);
+
+ if (!fInSTA)
+ {
+ return FALSE;
+ }
+
+ THDTYPE type;
+ HRESULT hr = S_OK;
+
+ hr = GetCurrentThreadTypeNT5(&type);
+ if (hr == S_OK)
+ {
+ fInSTA = (type == THDTYPE_PROCESSMESSAGES) ? TRUE : FALSE;
+
+ // If we get back THDTYPE_PROCESSMESSAGES, we are guaranteed to
+ // be an STA thread. If not, we are an MTA thread, however
+ // we can't know if the thread has been explicitly set to MTA
+ // (via a call to CoInitializeEx) or if it has been implicitly
+ // made MTA (if it hasn't been CoInitializeEx'd but CoInitialize
+ // has already been called on some other thread in the process.
+ }
+ else
+ {
+ // CoInitialize hasn't been called in the process yet so assume the current thread
+ // is MTA.
+ fInSTA = FALSE;
+ }
+
+ return fInSTA;
+}
+#endif
+
+BOOL g_fWeOwnProcess = FALSE;
+
+static LONG s_ActiveShutdownThreadCount = 0;
+
+// ---------------------------------------------------------------------------
+// Function: EEShutDownProcForSTAThread(LPVOID lpParameter)
+//
+// Parameters:
+// LPVOID lpParameter: unused
+//
+// Description:
+// When EEShutDown decides that the shut down logic must occur on another thread,
+// EEShutDown creates a new thread, and this function acts as the thread proc. See
+// code:#STAShutDown for details.
+//
+DWORD WINAPI EEShutDownProcForSTAThread(LPVOID lpParameter)
+{
+ STATIC_CONTRACT_SO_INTOLERANT;;
+
+
+ ClrFlsSetThreadType(ThreadType_ShutdownHelper);
+
+ EEShutDownHelper(FALSE);
+ for (int i = 0; i < 10; i ++)
+ {
+ if (s_ActiveShutdownThreadCount)
+ {
+ return 0;
+ }
+ __SwitchToThread(20, CALLER_LIMITS_SPINNING);
+ }
+
+ EPolicyAction action = GetEEPolicy()->GetDefaultAction(OPR_ProcessExit, NULL);
+ if (action < eRudeExitProcess)
+ {
+ action = eRudeExitProcess;
+ }
+ UINT exitCode;
+ if (g_fWeOwnProcess)
+ {
+ exitCode = GetLatchedExitCode();
+ }
+ else
+ {
+ exitCode = HOST_E_EXITPROCESS_TIMEOUT;
+ }
+ EEPolicy::HandleExitProcessFromEscalation(action, exitCode);
+
+ return 0;
+}
+
+// ---------------------------------------------------------------------------
+// #EEShutDown
+//
+// Function: EEShutDown(BOOL fIsDllUnloading)
+//
+// Parameters:
+// BOOL fIsDllUnloading:
+// * TRUE => Called from CLR's DllMain (DLL_PROCESS_DETACH). Not safe point for
+// full cleanup
+// * FALSE => Called some other way (e.g., end of the CLR's main). Safe to do
+// full cleanup.
+//
+// Description:
+//
+// All ee shutdown stuff should be done here. EEShutDown is generally called in one
+// of two ways:
+// * 1. From code:EEPolicy::HandleExitProcess (via HandleExitProcessHelper), with
+// fIsDllUnloading == FALSE. This code path is typically invoked by the CLR's
+// main just falling through to the end. Full cleanup can be performed when
+// EEShutDown is called this way.
+// * 2. From CLR's DllMain (DLL_PROCESS_DETACH), with fIsDllUnloading == TRUE. When
+// called this way, much cleanup code is unsafe to run, and is thus skipped.
+//
+// Actual shut down logic is factored out to EEShutDownHelper which may be called
+// directly by EEShutDown, or indirectly on another thread (see code:#STAShutDown).
+//
+// In order that callees may also know the value of fIsDllUnloading, EEShutDownHelper
+// sets g_fProcessDetach = fIsDllUnloading, and g_fProcessDetach may then be retrieved
+// via code:IsAtProcessExit.
+//
+// NOTE 1: Actually, g_fProcessDetach is set to TRUE if fIsDllUnloading is TRUE. But
+// g_fProcessDetach doesn't appear to be explicitly set to FALSE. (Apparently
+// g_fProcessDetach is implicitly initialized to FALSE as clr.dll is loaded.)
+//
+// NOTE 2: EEDllMain(DLL_PROCESS_DETACH) already sets g_fProcessDetach to TRUE, so it
+// appears EEShutDownHelper doesn't have to.
+//
+void STDMETHODCALLTYPE EEShutDown(BOOL fIsDllUnloading)
+{
+ CONTRACTL {
+ NOTHROW;
+ GC_TRIGGERS;
+ MODE_ANY;
+ SO_TOLERANT; // we don't need to cleanup 'cus we're shutting down
+ PRECONDITION(g_fEEStarted);
+ } CONTRACTL_END;
+
+ // If we have not started runtime successfully, it is not safe to call EEShutDown.
+ if (!g_fEEStarted || g_fFastExitProcess == 2)
+ {
+ return;
+ }
+
+ // Stop stack probing and asserts right away. Once we're shutting down, we can do no more.
+ // And we don't want to SO-protect anything at this point anyway. This really only has impact
+ // on a debug build.
+ TerminateStackProbes();
+
+ // The process is shutting down. No need to check SO contract.
+ SO_NOT_MAINLINE_FUNCTION;
+
+ // We only do the first part of the shutdown once.
+ static LONG OnlyOne = -1;
+
+ if (!fIsDllUnloading)
+ {
+ if (FastInterlockIncrement(&OnlyOne) != 0)
+ {
+ // I'm in a regular shutdown -- but another thread got here first.
+ // It's a race if I return from here -- I'll call ExitProcess next, and
+ // rip things down while the first thread is half-way through a
+ // nice cleanup. Rather than do that, I should just wait until the
+ // first thread calls ExitProcess(). I'll die a nice death when that
+ // happens.
+ GCX_PREEMP_NO_DTOR();
+ WaitForEndOfShutdown();
+ return;
+ }
+
+#ifdef FEATURE_MULTICOREJIT
+ if (!AppX::IsAppXProcess()) // When running as Appx, make the delayed timer driven writing be the only option
+ {
+ MulticoreJitManager::StopProfileAll();
+ }
+#endif
+ }
+
+#ifdef FEATURE_COMINTEROP
+ if (!fIsDllUnloading && IsThreadInSTA())
+ {
+ // #STAShutDown
+ //
+ // During shutdown, we may need to release STA interface on the shutdown thread.
+ // It is possible that the shutdown thread may deadlock. During shutdown, all
+ // threads are blocked, except the shutdown thread and finalizer thread. If a
+ // lock is held by one of these suspended threads, it can deadlock the process if
+ // the shutdown thread tries to enter the lock. To mitigate this risk, create
+ // another thread (B) to do shutdown activities (i.e., EEShutDownHelper), while
+ // this thread (A) waits. If B deadlocks, A will time out and immediately return
+ // from EEShutDown. A will then eventually call the OS's ExitProcess, which will
+ // kill the deadlocked thread (and all other threads).
+ //
+ // Many Windows Forms-based apps will also execute the code below to shift shut
+ // down logic to a separate thread, even if they don't use COM objects. Reason
+ // being that they will typically use a main UI thread to pump all Windows
+ // messages (including messages that facilitate cross-thread COM calls to STA COM
+ // objects), and will set that thread up as an STA thread just in case there are
+ // such cross-thread COM calls to contend with. In fact, when you use VS's
+ // File.New.Project to make a new Windows Forms project, VS will mark Main() with
+ // [STAThread]
+ DWORD thread_id = 0;
+ if (CreateThread(NULL,0,EEShutDownProcForSTAThread,NULL,0,&thread_id))
+ {
+ GCX_PREEMP_NO_DTOR();
+
+ ClrFlsSetThreadType(ThreadType_Shutdown);
+ WaitForEndOfShutdown();
+ FastInterlockIncrement(&s_ActiveShutdownThreadCount);
+ ClrFlsClearThreadType(ThreadType_Shutdown);
+ }
+ }
+ else
+ // Otherwise, this thread calls EEShutDownHelper directly. First switch to
+ // cooperative mode if this is a managed thread
+#endif
+ if (GetThread())
+ {
+ GCX_COOP();
+ EEShutDownHelper(fIsDllUnloading);
+ if (!fIsDllUnloading)
+ {
+ FastInterlockIncrement(&s_ActiveShutdownThreadCount);
+ }
+ }
+ else
+ {
+ EEShutDownHelper(fIsDllUnloading);
+ if (!fIsDllUnloading)
+ {
+ FastInterlockIncrement(&s_ActiveShutdownThreadCount);
+ }
+ }
+}
+
+// ---------------------------------------------------------------------------
+// %%Function: IsRuntimeActive()
+//
+// Parameters:
+// none
+//
+// Returns:
+// TRUE or FALSE
+//
+// Description: Indicates if the runtime is active or not. "Active" implies
+// that the runtime has started and is in a position to run
+// managed code. If either of these conditions are false, the
+// function return FALSE.
+//
+// Why couldnt we add !g_fEEStarted check in CanRunManagedCode?
+//
+//
+// ExecuteDLL in ceemain.cpp could start the runtime
+// (due to DLL_PROCESS_ATTACH) after invoking CanRunManagedCode.
+// If the function were to be modified, then this scenario could fail.
+// Hence, I have built over CanRunManagedCode in IsRuntimeActive.
+
+// ---------------------------------------------------------------------------
+BOOL IsRuntimeActive()
+{
+ // If the runtime has started AND we can run managed code,
+ // then runtime is considered "active".
+ BOOL fCanRunManagedCode = CanRunManagedCode();
+ return (g_fEEStarted && fCanRunManagedCode);
+}
+
+// ---------------------------------------------------------------------------
+// %%Function: CanRunManagedCode()
+//
+// Parameters:
+// none
+//
+// Returns:
+// true or false
+//
+// Description: Indicates if one is currently allowed to run managed code.
+// ---------------------------------------------------------------------------
+NOINLINE BOOL CanRunManagedCodeRare(LoaderLockCheck::kind checkKind, HINSTANCE hInst /*= 0*/)
+{
+ CONTRACTL {
+ NOTHROW;
+ if (checkKind == LoaderLockCheck::ForMDA) { GC_TRIGGERS; } else { GC_NOTRIGGER; }; // because of the CustomerDebugProbe
+ MODE_ANY;
+ SO_TOLERANT;
+ } CONTRACTL_END;
+
+ // If we are shutting down the runtime, then we cannot run code.
+ if (g_fForbidEnterEE)
+ return FALSE;
+
+ // If pre-loaded objects are not present, then no way.
+ if (g_pPreallocatedOutOfMemoryException == NULL)
+ return FALSE;
+
+ // If we are finaling live objects or processing ExitProcess event,
+ // we can not allow managed method to run unless the current thread
+ // is the finalizer thread
+ if ((g_fEEShutDown & ShutDown_Finalize2) && !FinalizerThread::IsCurrentThreadFinalizer())
+ return FALSE;
+
+#if defined(FEATURE_COMINTEROP) && defined(MDA_SUPPORTED)
+ if ((checkKind == LoaderLockCheck::ForMDA) && (NULL == MDA_GET_ASSISTANT(LoaderLock)))
+ return TRUE;
+
+ if (checkKind == LoaderLockCheck::None)
+ return TRUE;
+
+ // If we are checking whether the OS loader lock is held by the current thread, then
+ // it better not be. Note that ShouldCheckLoaderLock is a cached test for whether
+ // we are checking this probe. So we can call AuxUlibIsDLLSynchronizationHeld before
+ // verifying that the probe is still enabled.
+ //
+ // What's the difference between ignoreLoaderLock & ShouldCheckLoaderLock?
+ // ShouldCheckLoaderLock is a process-wide flag. In a few places where we
+ // *know* we are in the loader lock but haven't quite reached the dangerous
+ // point, we call CanRunManagedCode suppressing/deferring this check.
+ BOOL IsHeld;
+
+ if (ShouldCheckLoaderLock(FALSE) &&
+ AuxUlibIsDLLSynchronizationHeld(&IsHeld) &&
+ IsHeld)
+ {
+ if (checkKind == LoaderLockCheck::ForMDA)
+ {
+ MDA_TRIGGER_ASSISTANT(LoaderLock, ReportViolation(hInst));
+ }
+ else
+ {
+ return FALSE;
+ }
+ }
+#endif // defined(FEATURE_COMINTEROP) && defined(MDA_SUPPORTED)
+
+ return TRUE;
+}
+
+#include <optsmallperfcritical.h>
+BOOL CanRunManagedCode(LoaderLockCheck::kind checkKind, HINSTANCE hInst /*= 0*/)
+{
+ CONTRACTL {
+ NOTHROW;
+ if (checkKind == LoaderLockCheck::ForMDA) { GC_TRIGGERS; } else { GC_NOTRIGGER; }; // because of the CustomerDebugProbe
+ MODE_ANY;
+ SO_TOLERANT;
+ } CONTRACTL_END;
+
+ // Special-case the common success cases
+ // (Try not to make any calls here so that we don't have to spill our incoming arg regs)
+ if (!g_fForbidEnterEE
+ && (g_pPreallocatedOutOfMemoryException != NULL)
+ && !(g_fEEShutDown & ShutDown_Finalize2)
+ && (((checkKind == LoaderLockCheck::ForMDA)
+#ifdef MDA_SUPPORTED
+ && (NULL == MDA_GET_ASSISTANT(LoaderLock))
+#endif // MDA_SUPPORTED
+ ) || (checkKind == LoaderLockCheck::None)))
+ {
+ return TRUE;
+ }
+
+ // Then call a helper for everything else.
+ return CanRunManagedCodeRare(checkKind, hInst);
+}
+#include <optdefault.h>
+
+
+// ---------------------------------------------------------------------------
+// %%Function: CoInitializeEE(DWORD fFlags)
+//
+// Parameters:
+// fFlags - Initialization flags for the engine. See the
+// COINITIEE enumerator for valid values.
+//
+// Returns:
+// Nothing
+//
+// Description:
+// Initializes the EE if it hasn't already been initialized. This function
+// no longer maintains a ref count since the EE doesn't support being
+// unloaded and re-loaded. It simply ensures the EE has been started.
+// ---------------------------------------------------------------------------
+HRESULT STDMETHODCALLTYPE CoInitializeEE(DWORD fFlags)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_TRIGGERS;
+ MODE_PREEMPTIVE;
+ SO_TOLERANT;
+ }
+ CONTRACTL_END;
+
+ HRESULT hr = S_OK;
+ BEGIN_ENTRYPOINT_NOTHROW;
+ hr = InitializeEE((COINITIEE)fFlags);
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+}
+
+// ---------------------------------------------------------------------------
+// %%Function: CoUninitializeEE
+//
+// Parameters:
+// BOOL fIsDllUnloading :: is it safe point for full cleanup
+//
+// Returns:
+// Nothing
+//
+// Description:
+// Must be called by client on shut down in order to free up the system.
+// ---------------------------------------------------------------------------
+void STDMETHODCALLTYPE CoUninitializeEE(BOOL fIsDllUnloading)
+{
+ LIMITED_METHOD_CONTRACT;
+ //BEGIN_ENTRYPOINT_VOIDRET;
+
+ // This API is unfortunately publicly exported so we cannot get rid
+ // of it. However since the EE doesn't currently support being unloaded
+ // and re-loaded, it is useless to do any ref counting here or to pretend
+ // to unload it. The proper way to shutdown the EE is to call CorExitProcess.
+ //END_ENTRYPOINT_VOIDRET;
+
+}
+
+#ifndef FEATURE_CORECLR
+//*****************************************************************************
+// This entry point is called from the native DllMain of the loaded image.
+// This gives the COM+ loader the chance to dispatch the loader event. The
+// first call will cause the loader to look for the entry point in the user
+// image. Subsequent calls will dispatch to either the user's DllMain or
+// their Module derived class.
+//*****************************************************************************
+BOOL STDMETHODCALLTYPE _CorDllMain( // TRUE on success, FALSE on error.
+ HINSTANCE hInst, // Instance handle of the loaded module.
+ DWORD dwReason, // Reason for loading.
+ LPVOID lpReserved // Unused.
+ )
+{
+ STATIC_CONTRACT_NOTHROW;
+ //STATIC_CONTRACT_GC_TRIGGERS;
+ STATIC_CONTRACT_ENTRY_POINT;
+
+ //BEGIN_ENTRYPOINT_NOTHROW;
+
+ struct Param
+ {
+ HINSTANCE hInst;
+ DWORD dwReason;
+ LPVOID lpReserved;
+ BOOL retval;
+ } param;
+ param.hInst = hInst;
+ param.dwReason = dwReason;
+ param.lpReserved = lpReserved;
+ param.retval = FALSE;
+
+ // Can't use PAL_TRY/EX_TRY here as they access the ClrDebugState which gets blown away as part of the
+ // PROCESS_DETACH path. Must use special PAL_TRY_FOR_DLLMAIN, passing the reason were in the DllMain.
+ PAL_TRY_FOR_DLLMAIN(Param *, pParam, &param, pParam->dwReason)
+ {
+#ifdef _DEBUG
+ if (CLRTaskHosted() &&
+ ((pParam->dwReason == DLL_PROCESS_ATTACH && pParam->lpReserved == NULL) || // LoadLibrary of a managed dll
+ (pParam->dwReason == DLL_PROCESS_DETACH && pParam->lpReserved == NULL) // FreeLibrary of a managed dll
+ )) {
+ // OS loader lock is being held by the current thread. We can not allow the fiber
+ // to be rescheduled here while processing DllMain for managed dll.
+#ifdef FEATURE_INCLUDE_ALL_INTERFACES
+ IHostTask *pTask = GetCurrentHostTask();
+ if (pTask) {
+ Thread *pThread = GetThread();
+ _ASSERTE (pThread);
+ _ASSERTE (pThread->HasThreadAffinity());
+ }
+#endif // FEATURE_INCLUDE_ALL_INTERFACES
+ }
+#endif
+ // Since we're in _CorDllMain, we know that we were not called because of a
+ // bootstrap thunk, since they will call CorDllMainForThunk. Because of this,
+ // we can pass FALSE for the fFromThunk parameter.
+ pParam->retval = ExecuteDLL(pParam->hInst,pParam->dwReason,pParam->lpReserved, FALSE);
+ }
+ PAL_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+ {
+ }
+ PAL_ENDTRY;
+
+ //END_ENTRYPOINT_NOTHROW;
+
+ return param.retval;
+}
+
+#endif // !FEATURE_CORECLR
+
+#ifdef FEATURE_MIXEDMODE
+//*****************************************************************************
+void STDMETHODCALLTYPE CorDllMainForThunk(HINSTANCE hInst, HINSTANCE hInstShim)
+{
+
+ STATIC_CONTRACT_THROWS;
+ STATIC_CONTRACT_GC_TRIGGERS;
+
+
+ g_fEEIJWStartup = TRUE;
+
+ {
+
+ // If no managed thread exists, then we need to call the prepare method
+ // to try and startup the runtime and/or create a managed thread object
+ // so that installing an unwind and continue handler below is possible.
+ // If we fail to startup or create a thread, we'll raise the basic
+ // EXCEPTION_COMPLUS exception.
+ if (GetThread() == NULL)
+ {
+ HRESULT hr;
+ // Since this method is only called if a bootstrap thunk is invoked, we
+ // know that passing TRUE for fFromThunk is the correct value.
+ if (FAILED(hr = PrepareExecuteDLLForThunk(hInst, 0, NULL)))
+ {
+ RaiseComPlusException();
+ }
+ }
+
+ }
+
+ INSTALL_UNWIND_AND_CONTINUE_HANDLER;
+
+ // We're actually going to run some managed code and we're inside the loader lock.
+ // There may be a customer debug probe enabled that prevents this.
+ CanRunManagedCode(hInst);
+
+ // Since this method is only called if a bootstrap thunk is invoked, we
+ // know that passing TRUE for fFromThunk is the correct value.
+ ExecuteDLL(hInst, 0, NULL, TRUE);
+
+ UNINSTALL_UNWIND_AND_CONTINUE_HANDLER;
+
+}
+#endif // FEATURE_MIXEDMODE
+
+
+#ifndef FEATURE_CORECLR
+
+// This function will do some additional PE Checks to make sure everything looks good.
+// We must do these before we run any managed code (that's why we can't do them in PEVerifier, as
+// managed code is used to determine the policy settings)
+HRESULT DoAdditionalPEChecks(HINSTANCE hInst)
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_TRIGGERS;
+
+ struct Param
+ {
+ HINSTANCE hInst;
+ HRESULT hr;
+ } param;
+ param.hInst = hInst;
+ param.hr = S_OK;
+
+ PAL_TRY(Param *, pParam, &param)
+ {
+ PEDecoder pe(pParam->hInst);
+
+ if (!pe.CheckWillCreateGuardPage())
+ pParam->hr = COR_E_BADIMAGEFORMAT;
+ }
+ PAL_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+ {
+ }
+ PAL_ENDTRY
+
+ return param.hr;
+}
+
+//*****************************************************************************
+// This entry point is called from the native entry point of the loaded
+// executable image. This simply calls into _CorExeMainInternal, the real
+// entry point inside a filter to trigger unhandled exception processing in the
+// event an exception goes unhandled, independent of the OS UEF mechanism.
+//*****************************************************************************
+__int32 STDMETHODCALLTYPE _CorExeMain( // Executable exit code.
+ )
+{
+ STATIC_CONTRACT_GC_TRIGGERS;
+ STATIC_CONTRACT_THROWS;
+
+ // We really have nothing to share with our filter at this point.
+ struct Param
+ {
+ PVOID pData;
+ } param;
+ param.pData = NULL;
+
+ PAL_TRY(Param*, _pParam, &param)
+ {
+ // Call the real function that will invoke the managed entry point
+ _CorExeMainInternal();
+ }
+ PAL_EXCEPT_FILTER(EntryPointFilter)
+ {
+ LOG((LF_STARTUP, LL_INFO10, "EntryPointFilter returned EXCEPTION_EXECUTE_HANDLER!"));
+ }
+ PAL_ENDTRY;
+
+ return 0;
+}
+
+//*****************************************************************************
+// This entry point is called from _CorExeMain. If an exception goes unhandled
+// from here, we will trigger unhandled exception processing in _CorExeMain.
+//
+// The command line arguments and other entry point data
+// will be gathered here. The entry point for the user image will be found
+// and handled accordingly.
+//*****************************************************************************
+__int32 STDMETHODCALLTYPE _CorExeMainInternal( // Executable exit code.
+ )
+{
+ STATIC_CONTRACT_GC_TRIGGERS;
+ STATIC_CONTRACT_THROWS;
+ STATIC_CONTRACT_ENTRY_POINT;
+
+ // Yes, CorExeMain needs throws. If an exception passes through here, it will cause the
+ // "The application has generated an unhandled exception" dialog and offer to debug.
+
+ BEGIN_ENTRYPOINT_THROWS;
+
+ // Make sure PE file looks ok
+ HRESULT hr;
+ {
+ // We are early in the process, if we get an SO here we will just rip
+ CONTRACT_VIOLATION(SOToleranceViolation);
+ if (FAILED(hr = DoAdditionalPEChecks(WszGetModuleHandle(NULL))))
+ {
+ GCX_PREEMP();
+ VMDumpCOMErrors(hr);
+ SetLatchedExitCode (-1);
+ goto exit;
+ }
+ }
+
+ g_fEEManagedEXEStartup = TRUE;
+ // Before we initialize the EE, make sure we've snooped for all EE-specific
+ // command line arguments that might guide our startup.
+ WCHAR *pCmdLine = WszGetCommandLine();
+ HRESULT result = CorCommandLine::SetArgvW(pCmdLine);
+
+ if (SUCCEEDED(result))
+ {
+ g_fWeOwnProcess = TRUE;
+ result = EnsureEEStarted(COINITEE_MAIN);
+ }
+
+ if (FAILED(result))
+ {
+ g_fWeOwnProcess = FALSE;
+ GCX_PREEMP();
+ VMDumpCOMErrors(result);
+ SetLatchedExitCode (-1);
+ goto exit;
+ }
+
+ INSTALL_UNWIND_AND_CONTINUE_HANDLER;
+
+ // This will be called from a EXE so this is a self referential file so I am going to call
+ // ExecuteEXE which will do the work to make a EXE load.
+
+ BOOL bretval = 0;
+
+ bretval = ExecuteEXE(WszGetModuleHandle(NULL));
+ if (!bretval) {
+ // The only reason I've seen this type of error in the wild is bad
+ // metadata file format versions and inadequate error handling for
+ // partially signed assemblies. While this may happen during
+ // development, our customers should not get here. This is a back-stop
+ // to catch CLR bugs. If you see this, please try to find a better way
+ // to handle your error, like throwing an unhandled exception.
+ EEMessageBoxCatastrophic(IDS_EE_COREXEMAIN_FAILED_TEXT, IDS_EE_COREXEMAIN_FAILED_TITLE);
+ SetLatchedExitCode (-1);
+ }
+
+ UNINSTALL_UNWIND_AND_CONTINUE_HANDLER;
+
+exit:
+ STRESS_LOG1(LF_STARTUP, LL_ALWAYS, "Program exiting: return code = %d", GetLatchedExitCode());
+
+ STRESS_LOG0(LF_STARTUP, LL_INFO10, "EEShutDown invoked from _CorExeMainInternal");
+
+ EEPolicy::HandleExitProcess();
+
+ END_ENTRYPOINT_THROWS;
+
+ return 0;
+}
+
+
+static BOOL CacheCommandLine(__in LPWSTR pCmdLine, __in_opt LPWSTR* ArgvW)
+{
+ CONTRACTL {
+ THROWS;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ PRECONDITION(CheckPointer(pCmdLine));
+ PRECONDITION(CheckPointer(ArgvW));
+ } CONTRACTL_END;
+
+ if (pCmdLine) {
+ size_t len = wcslen(pCmdLine);
+
+ _ASSERT(g_pCachedCommandLine== NULL);
+ g_pCachedCommandLine = new WCHAR[len+1];
+ wcscpy_s(g_pCachedCommandLine, len+1, pCmdLine);
+ }
+
+ if (ArgvW != NULL && ArgvW[0] != NULL) {
+ PathString wszModuleName;
+ PathString wszCurDir;
+ if (!WszGetCurrentDirectory(wszCurDir))
+ return FALSE;
+
+#ifdef _PREFAST_
+#pragma warning(push)
+#pragma warning(disable:25025)
+#endif
+
+ // usage of PathCombine is safe if we ensure that buffer specified by
+ // parameter1 can accomodate buffers specified by paramater2, parameter3
+ // and one path separator
+ COUNT_T wszModuleName_len = wszCurDir.GetCount() + lstrlenW(ArgvW[0]);
+ WCHAR* wszModuleName_buf = wszModuleName.OpenUnicodeBuffer(wszModuleName_len);
+
+ if (PathCombine(wszModuleName_buf, wszCurDir, ArgvW[0]) == NULL)
+ return FALSE;
+ wszModuleName.CloseBuffer();
+#ifdef _PREFAST_
+#pragma warning(pop)
+#endif
+
+ size_t len = wszModuleName.GetCount();
+ _ASSERT(g_pCachedModuleFileName== NULL);
+ g_pCachedModuleFileName = new WCHAR[len+1];
+ wcscpy_s(g_pCachedModuleFileName, len+1, wszModuleName);
+ }
+
+ return TRUE;
+}
+
+//*****************************************************************************
+// This entry point is called from the native entry point of the loaded
+// executable image. The command line arguments and other entry point data
+// will be gathered here. The entry point for the user image will be found
+// and handled accordingly.
+//*****************************************************************************
+__int32 STDMETHODCALLTYPE _CorExeMain2( // Executable exit code.
+ PBYTE pUnmappedPE, // -> memory mapped code
+ DWORD cUnmappedPE, // Size of memory mapped code
+ __in LPWSTR pImageNameIn, // -> Executable Name
+ __in LPWSTR pLoadersFileName, // -> Loaders Name
+ __in LPWSTR pCmdLine) // -> Command Line
+{
+
+ // This entry point is used by clix
+ BOOL bRetVal = 0;
+
+ BEGIN_ENTRYPOINT_VOIDRET;
+ {
+ // Before we initialize the EE, make sure we've snooped for all EE-specific
+ // command line arguments that might guide our startup.
+ HRESULT result = CorCommandLine::SetArgvW(pCmdLine);
+
+ if (!CacheCommandLine(pCmdLine, CorCommandLine::GetArgvW(NULL))) {
+ LOG((LF_STARTUP, LL_INFO10, "Program exiting - CacheCommandLine failed\n"));
+ bRetVal = -1;
+ goto exit;
+ }
+
+ if (SUCCEEDED(result))
+ result = InitializeEE(COINITEE_MAIN);
+
+ if (FAILED(result)) {
+ VMDumpCOMErrors(result);
+ SetLatchedExitCode (-1);
+ goto exit;
+ }
+
+ // Load the executable
+ bRetVal = ExecuteEXE(pImageNameIn);
+
+ if (!bRetVal) {
+ // The only reason I've seen this type of error in the wild is bad
+ // metadata file format versions and inadequate error handling for
+ // partially signed assemblies. While this may happen during
+ // development, our customers should not get here. This is a back-stop
+ // to catch CLR bugs. If you see this, please try to find a better way
+ // to handle your error, like throwing an unhandled exception.
+ EEMessageBoxCatastrophic(IDS_EE_COREXEMAIN2_FAILED_TEXT, IDS_EE_COREXEMAIN2_FAILED_TITLE);
+ SetLatchedExitCode (-1);
+ }
+
+exit:
+ STRESS_LOG1(LF_STARTUP, LL_ALWAYS, "Program exiting: return code = %d", GetLatchedExitCode());
+
+ STRESS_LOG0(LF_STARTUP, LL_INFO10, "EEShutDown invoked from _CorExeMain2");
+
+ EEPolicy::HandleExitProcess();
+ }
+ END_ENTRYPOINT_VOIDRET;
+
+ return bRetVal;
+}
+
+//*****************************************************************************
+// This is the call point to wire up an EXE. In this case we have the HMODULE
+// and just need to make sure we do to correct self referantial things.
+//*****************************************************************************
+
+
+BOOL STDMETHODCALLTYPE ExecuteEXE(HMODULE hMod)
+{
+ STATIC_CONTRACT_GC_TRIGGERS;
+
+ _ASSERTE(hMod);
+ if (!hMod)
+ return FALSE;
+
+ ETWFireEvent(ExecExe_V1);
+
+ struct Param
+ {
+ HMODULE hMod;
+ } param;
+ param.hMod = hMod;
+
+ EX_TRY_NOCATCH(Param *, pParam, &param)
+ {
+ // Executables are part of the system domain
+ SystemDomain::ExecuteMainMethod(pParam->hMod);
+ }
+ EX_END_NOCATCH;
+
+ ETWFireEvent(ExecExeEnd_V1);
+
+ return TRUE;
+}
+
+BOOL STDMETHODCALLTYPE ExecuteEXE(__in LPWSTR pImageNameIn)
+{
+ STATIC_CONTRACT_GC_TRIGGERS;
+
+ EX_TRY_NOCATCH(LPWSTR, pImageNameInner, pImageNameIn)
+ {
+ WCHAR wzPath[MAX_LONGPATH];
+ DWORD dwPathLength = 0;
+
+ // get the path of executable
+ dwPathLength = WszGetFullPathName(pImageNameInner, MAX_LONGPATH, wzPath, NULL);
+
+ if (!dwPathLength || dwPathLength > MAX_LONGPATH)
+ {
+ ThrowWin32( !dwPathLength ? GetLastError() : ERROR_FILENAME_EXCED_RANGE);
+ }
+
+ SystemDomain::ExecuteMainMethod( NULL, (WCHAR *)wzPath );
+ }
+ EX_END_NOCATCH;
+
+ return TRUE;
+}
+#endif // FEATURE_CORECLR
+
+#ifdef FEATURE_MIXEDMODE
+
+LONG RunDllMainFilter(EXCEPTION_POINTERS* ep, LPVOID pv)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_TRIGGERS;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ BOOL useLastThrownObject = UpdateCurrentThrowable(ep->ExceptionRecord);
+ DefaultCatchHandler(ep, NULL, useLastThrownObject, FALSE);
+
+ DefaultCatchFilterParam param(COMPLUS_EXCEPTION_EXECUTE_HANDLER);
+ return DefaultCatchFilter(ep, &param);
+}
+
+// <TODO>@Todo: For M10, this only runs unmanaged native classic entry points for
+// the IJW mc++ case.</TODO>
+HRESULT RunDllMain(MethodDesc *pMD, HINSTANCE hInst, DWORD dwReason, LPVOID lpReserved)
+{
+ STATIC_CONTRACT_NOTHROW;
+
+ _ASSERTE(!GetAppDomain()->IsPassiveDomain());
+
+ if (!pMD) {
+ _ASSERTE(!"Must have a valid function to call!");
+ return E_INVALIDARG;
+ }
+
+ if (pMD->IsIntrospectionOnly())
+ return S_OK;
+
+ struct Param
+ {
+ MethodDesc *pMD;
+ HINSTANCE hInst;
+ DWORD dwReason;
+ LPVOID lpReserved;
+ HRESULT hr;
+ }; Param param;
+ param.pMD = pMD;
+ param.hInst = hInst;
+ param.dwReason = dwReason;
+ param.lpReserved = lpReserved;
+ param.hr = S_OK;
+
+ PAL_TRY(Param *, pParamOuter, &param)
+ {
+ EX_TRY_NOCATCH(Param *, pParam, pParamOuter)
+ {
+ HRESULT hr;
+
+ // This call is inherently unverifiable entry point.
+ if (!Security::CanSkipVerification(pParam->pMD)) {
+ hr = SECURITY_E_UNVERIFIABLE;
+ goto Done;
+ }
+
+ {
+ SigPointer sig(pParam->pMD->GetSigPointer());
+
+ ULONG data = 0;
+ CorElementType eType = ELEMENT_TYPE_END;
+ CorElementType eType2 = ELEMENT_TYPE_END;
+
+ IfFailGoto(sig.GetData(&data), Done);
+ if (data != IMAGE_CEE_CS_CALLCONV_DEFAULT) {
+ hr = COR_E_METHODACCESS;
+ goto Done;
+ }
+
+ IfFailGoto(sig.GetData(&data), Done);
+ if (data != 3) {
+ hr = COR_E_METHODACCESS;
+ goto Done;
+ }
+
+ IfFailGoto(sig.GetElemType(&eType), Done);
+ if (eType != ELEMENT_TYPE_I4) { // return type = int32
+ hr = COR_E_METHODACCESS;
+ goto Done;
+ }
+
+ IfFailGoto(sig.GetElemType(&eType), Done);
+ if (eType == ELEMENT_TYPE_PTR)
+ IfFailGoto(sig.GetElemType(&eType2), Done);
+
+ if (eType!= ELEMENT_TYPE_PTR || eType2 != ELEMENT_TYPE_VOID) { // arg1 = void*
+ hr = COR_E_METHODACCESS;
+ goto Done;
+ }
+
+ IfFailGoto(sig.GetElemType(&eType), Done);
+ if (eType != ELEMENT_TYPE_U4) { // arg2 = uint32
+ hr = COR_E_METHODACCESS;
+ goto Done;
+ }
+
+ IfFailGoto(sig.GetElemType(&eType), Done);
+ if (eType == ELEMENT_TYPE_PTR)
+ IfFailGoto(sig.GetElemType(&eType2), Done);
+
+ if (eType != ELEMENT_TYPE_PTR || eType2 != ELEMENT_TYPE_VOID) { // arg3 = void*
+ hr = COR_E_METHODACCESS;
+ goto Done;
+ }
+ }
+
+ {
+ MethodDescCallSite dllMain(pParam->pMD);
+
+ // Set up a callstack with the values from the OS in the argument array
+ ARG_SLOT stackVar[3];
+ stackVar[0] = PtrToArgSlot(pParam->hInst);
+ stackVar[1] = (ARG_SLOT) pParam->dwReason;
+ stackVar[2] = PtrToArgSlot(pParam->lpReserved);
+
+ // Call the method in question with the arguments.
+ if((dllMain.Call_RetI4(&stackVar[0]) == 0)
+ &&(pParam->dwReason==DLL_PROCESS_ATTACH)
+ && (CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_IgnoreDllMainReturn) != 1))
+ {
+ hr = COR_E_INVALIDPROGRAM;
+#ifdef MDA_SUPPORTED
+ MdaDllMainReturnsFalse* pProbe = MDA_GET_ASSISTANT(DllMainReturnsFalse);
+ if(pProbe != NULL) pProbe->ReportError();
+#endif
+ }
+ }
+Done:
+ pParam->hr = hr;
+ }
+ EX_END_NOCATCH
+ }
+ //@TODO: Revisit why this is here, and if it's still necessary.
+ PAL_EXCEPT_FILTER(RunDllMainFilter)
+ {
+ // switch to COOPERATIVE
+ GCX_COOP_NO_DTOR();
+ // don't do anything - just want to catch it
+ }
+ PAL_ENDTRY
+
+ return param.hr;
+}
+
+//*****************************************************************************
+// fFromThunk indicates that a dependency is calling through the Import Export table,
+// and calling indirect through the IJW vtfixup slot.
+//
+// fFromThunk=FALSE means that we are running DllMain during LoadLibrary while
+// holding the loader lock.
+//
+HRESULT ExecuteDLLForAttach(HINSTANCE hInst,
+ DWORD dwReason,
+ LPVOID lpReserved,
+ BOOL fFromThunk)
+{
+ CONTRACTL{
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ PRECONDITION(CheckPointer(lpReserved, NULL_OK));
+ } CONTRACTL_END;
+
+ PEDecoder pe(hInst);
+
+ // Note that ILOnly DLLs can have a managed entry point. This will
+ // be called when the assembly is activated by the CLR loader,
+ // and it need not be run here.
+
+ if (pe.IsILOnly())
+ {
+ _ASSERTE(!fFromThunk);
+ return S_OK;
+ }
+
+ if (!pe.HasManagedEntryPoint() && !fFromThunk)
+ return S_OK;
+
+ // We need to prep the managed assembly for execution.
+
+ AppDomain *pDomain = GetAppDomain();
+
+ // First we must find the DomainFile associated with this HMODULE. There are basically 3
+ // interesting cases:
+ //
+ // 1. The file is being loaded. In this case we have a DomainFile in existence but
+ // (very inconveniently) it does not have its HMODULE set yet. Most likely if we
+ // were to look at the state of this thread up the stack, we'd see that file is
+ // currently being loaded right above us. However, we cannot rely on this because
+ // A. We may be in case 2 (e.g. a static DLL dependency is being loaded first)
+ // B. _CorDllMain may have been called on a different thread.
+ //
+ // 2. The file has never been seen before. In this case we are basically in the dark; we
+ // simply attempt to load the file as an assembly. (If it is not an assembly we will
+ // fail.)
+ //
+ // 3. The file has been loaded but we are getting called anyway in a race. (This should not
+ // happen in the loader lock case, only when we are getting called from thunks).
+ //
+ // So, we:
+ // A. Use the current thread's LoadingFile as a hint. We will rely on this only if it has
+ // the same path as the HMOD.
+ // B. Search the app domain for a DomainFile with a matching base address, or failing that, path.
+ // C. We have no information, so assume it is a new assembly being loaded.
+
+ // A: check the loading file
+
+ StackSString path;
+ PEImage::GetPathFromDll(hInst, path);
+
+ DomainFile *pLoadingFile = GetThread()->GetLoadingFile();
+ GetThread()->ClearLoadingFile();
+
+ if (pLoadingFile != NULL)
+ {
+ if (!PEImage::PathEquals(pLoadingFile->GetFile()->GetPath(), path))
+ {
+ pLoadingFile = NULL;
+ }
+ else
+ {
+ pLoadingFile->GetFile()->SetLoadedHMODULE(hInst);
+ }
+ }
+
+ // B: look for a loading IJW module
+
+ if (pLoadingFile == NULL)
+ {
+ pLoadingFile = pDomain->FindIJWDomainFile(hInst, path);
+ }
+
+ // C: nothing else worked, we require it is an assembly with a manifest in this situation
+ if (pLoadingFile == NULL)
+ {
+ pLoadingFile = pDomain->LoadExplicitAssembly(hInst, FALSE)->GetDomainAssembly();
+ }
+
+ // There are two cases here, loading from thunks emitted from the shim, and being called
+ // inside the loader lock for the legacy IJW dll case.
+
+ if (fFromThunk)
+ {
+ pLoadingFile->EnsureActive();
+ return S_OK;
+ }
+
+ _ASSERTE(!pe.IsILOnly() && pe.HasManagedEntryPoint());
+ // Get the entry point for the IJW module
+ Module *pModule = pLoadingFile->GetCurrentModule();
+ mdMethodDef tkEntry = pModule->GetEntryPointToken();
+
+ BOOL hasEntryPoint = (TypeFromToken(tkEntry) == mdtMethodDef &&
+ !IsNilToken(tkEntry));
+
+ if (!hasEntryPoint)
+ {
+ return S_OK;
+ }
+
+ if (pDomain->IsPassiveDomain())
+ {
+ // Running managed code while holding the loader lock can cause deadlocks.
+ // These deadlocks might happen when this assembly gets executed. However,
+ // we should avoid those deadlocks if we are in a passive AppDomain.
+ // Also, managed entry point is now legacy, and has should be replaced
+ // with Module .cctor.
+ //
+ // We also rely on Module::CanExecuteCode() to prevent
+ // any further code from being executed from this assembly.
+ _ASSERTE(pLoadingFile && pLoadingFile->GetFile() && pLoadingFile->GetFile()->GetILimage());
+ pLoadingFile->GetFile()->GetILimage()->SetPassiveDomainOnly();
+ return S_OK;
+ }
+
+ // We're actually going to run some managed code and we're inside the loader lock.
+ // There may be a customer debug probe enabled that prevents this.
+ CanRunManagedCode(hInst);
+
+ // If we are not being called from thunks, we are inside the loader lock
+ // & have this single opportunity to run our dll main.
+ // Since we are in deadlock danger anyway (note this is the problematic legacy
+ // case only!) we disable our file loading and type loading reentrancy protection & allow
+ // loads to fully proceed.
+
+ // class level override is needed for the entire operation, not just EnsureActive
+ OVERRIDE_TYPE_LOAD_LEVEL_LIMIT(CLASS_LOADED);
+
+ {
+ OVERRIDE_LOAD_LEVEL_LIMIT(FILE_ACTIVE);
+
+ // Complete the load as necessary
+ pLoadingFile->EnsureActive();
+ }
+
+ MethodDesc *pMD = pModule->FindMethodThrowing(tkEntry);
+ CONSISTENCY_CHECK(CheckPointer(pMD));
+
+ pModule->SetDllEntryPoint(pMD);
+
+ GCX_COOP();
+
+ // pModule may be for a different domain.
+ PEFile *pPEFile = pMD->GetModule()->GetFile();
+ if (!pPEFile->CheckLoaded())
+ {
+ pPEFile->SetLoadedHMODULE(hInst);
+ }
+
+ // Call the managed entry point
+ HRESULT hr = RunDllMain(pMD, hInst, dwReason, lpReserved);
+
+ return hr;
+}
+
+#endif // FEATURE_MIXEDMODE
+
+//*****************************************************************************
+BOOL ExecuteDLL_ReturnOrThrow(HRESULT hr, BOOL fFromThunk)
+{
+ CONTRACTL {
+ if (fFromThunk) THROWS; else NOTHROW;
+ WRAPPER(GC_TRIGGERS);
+ MODE_ANY;
+ SO_TOLERANT;
+ } CONTRACTL_END;
+
+ // If we have a failure result, and we're called from a thunk,
+ // then we need to throw an exception to communicate the error.
+ if (FAILED(hr) && fFromThunk)
+ {
+ COMPlusThrowHR(hr);
+ }
+ return SUCCEEDED(hr);
+}
+
+#if !defined(FEATURE_CORECLR) && defined(_DEBUG)
+//*****************************************************************************
+// Factor some common debug code.
+//*****************************************************************************
+static void EnsureManagedThreadExistsForHostedThread()
+{
+ CONTRACTL {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ } CONTRACTL_END;
+
+ if (CLRTaskHosted()) {
+ // If CLR is hosted, and this is on a thread that a host controls,
+ // we must have created Thread object.
+#ifdef FEATURE_INCLUDE_ALL_INTERFACES
+ IHostTask *pHostTask = GetCurrentHostTask();
+ if (pHostTask)
+ {
+ CONSISTENCY_CHECK(CheckPointer(GetThread()));
+ }
+#endif // FEATURE_INCLUDE_ALL_INTERFACES
+ }
+}
+#endif // !FEATURE_CORECLR && _DEBUG
+
+#ifdef FEATURE_MIXEDMODE
+//*****************************************************************************
+// This ensure that the runtime is started and an EEThread object is created
+// for the current thread. This functionality is duplicated in ExecuteDLL,
+// except that this code will not throw.
+//*****************************************************************************
+HRESULT PrepareExecuteDLLForThunk(HINSTANCE hInst,
+ DWORD dwReason,
+ LPVOID lpReserved)
+{
+ CONTRACTL {
+ NOTHROW;
+ WRAPPER(GC_TRIGGERS);
+ MODE_ANY;
+ PRECONDITION(CheckPointer(lpReserved, NULL_OK));
+ PRECONDITION(CheckPointer(hInst));
+ } CONTRACTL_END;
+
+
+ HRESULT hr = S_OK;
+ Thread *pThread = GetThread();
+
+ INDEBUG(EnsureManagedThreadExistsForHostedThread();)
+
+ if (pThread == NULL)
+ {
+ // If necessary, start the runtime and create a managed thread object.
+ hr = EnsureEEStarted(COINITEE_DLL);
+ if (FAILED(hr))
+ {
+ return hr;
+ }
+ if ((pThread = SetupThreadNoThrow(&hr)) == NULL)
+ {
+ return hr;
+ }
+ }
+
+ CONSISTENCY_CHECK(CheckPointer(pThread));
+
+ return S_OK;
+}
+
+#endif // FEATURE_MIXEDMODE
+
+#ifndef FEATURE_CORECLR
+//*****************************************************************************
+// This is the call point to make a DLL that is already loaded into our address
+// space run. There will be other code to actually have us load a DLL due to a
+// class reference.
+//*****************************************************************************
+BOOL STDMETHODCALLTYPE ExecuteDLL(HINSTANCE hInst,
+ DWORD dwReason,
+ LPVOID lpReserved,
+ BOOL fFromThunk)
+{
+
+ CONTRACTL{
+ THROWS;
+ WRAPPER(GC_TRIGGERS);
+ MODE_ANY;
+ ENTRY_POINT;
+ PRECONDITION(CheckPointer(lpReserved, NULL_OK));
+ PRECONDITION(CheckPointer(hInst));
+ PRECONDITION(GetThread() != NULL || !fFromThunk);
+ } CONTRACTL_END;
+
+ HRESULT hr = S_OK;
+ BOOL fRetValue = FALSE;
+
+ // This needs to be before the BEGIN_ENTRYPOINT_THROWS since
+ // we can't call ReportStackOverflow if we're almost done with
+ // shutdown and can't run managed code.
+ if (!CanRunManagedCode(LoaderLockCheck::None))
+ {
+ return fRetValue;
+ }
+
+ BEGIN_ENTRYPOINT_THROWS;
+
+ Thread *pThread = GetThread();
+
+ if (!hInst)
+ {
+ fRetValue = ExecuteDLL_ReturnOrThrow(E_FAIL, fFromThunk);
+ goto Exit;
+ }
+
+ // Note that we always check fFromThunk before checking the dwReason value.
+ // This is because the dwReason value is undefined in the case that we're
+ // being invoked due to a bootstrap (because that is by definition outside
+ // of the loader lock and there is no appropriate dwReason value).
+ if (fFromThunk ||
+ dwReason == DLL_PROCESS_ATTACH ||
+ dwReason == DLL_THREAD_ATTACH)
+ {
+ INDEBUG(EnsureManagedThreadExistsForHostedThread();)
+
+
+ // If necessary, start the runtime and create a managed thread object.
+ if (fFromThunk || dwReason == DLL_PROCESS_ATTACH)
+ {
+ hr = EnsureEEStarted(COINITEE_DLL);
+
+ if (SUCCEEDED(hr) && pThread == NULL)
+ {
+ pThread = SetupThreadNoThrow(&hr);
+ }
+
+ if(FAILED(hr))
+ {
+ fRetValue = ExecuteDLL_ReturnOrThrow(hr, fFromThunk);
+ goto Exit;
+ }
+ }
+
+ // IJW assemblies cause the thread doing the process attach to
+ // re-enter ExecuteDLL and do a thread attach. This happens when
+ // CoInitializeEE() above executed
+ else if (!(pThread &&
+ pThread->GetDomain() &&
+ CanRunManagedCode(LoaderLockCheck::None)))
+ {
+ fRetValue = ExecuteDLL_ReturnOrThrow(S_OK, fFromThunk);
+ goto Exit;
+ }
+
+ // we now have a thread setup - either the 1st if set it up, or
+ // the else if ran if we didn't have a thread setup.
+
+#ifdef FEATURE_MIXEDMODE
+
+ EX_TRY
+ {
+ hr = ExecuteDLLForAttach(hInst, dwReason, lpReserved, fFromThunk);
+ }
+ EX_CATCH
+ {
+ // We rethrow directly here instead of using ExecuteDLL_ReturnOrThrow() to
+ // preserve the full exception information, rather than just the HRESULT
+ if (fFromThunk)
+ {
+ EX_RETHROW;
+ }
+ else
+ {
+ hr = GET_EXCEPTION()->GetHR();
+ }
+ }
+ EX_END_CATCH(SwallowAllExceptions);
+
+ if (FAILED(hr))
+ {
+ fRetValue = ExecuteDLL_ReturnOrThrow(hr, fFromThunk);
+ goto Exit;
+ }
+#endif // FEATURE_MIXEDMODE
+ }
+ else
+ {
+ PEDecoder pe(hInst);
+ if (pe.HasManagedEntryPoint())
+ {
+ // If the EE is still intact, then run user entry points. Otherwise
+ // detach was handled when the app domain was stopped.
+ //
+ // Checks for the loader lock will occur within RunDllMain, if that's
+ FAULT_NOT_FATAL();
+ if (CanRunManagedCode(LoaderLockCheck::None))
+ {
+ hr = SystemDomain::RunDllMain(hInst, dwReason, lpReserved);
+ }
+ }
+ // This does need to match the attach. We will only unload dll's
+ // at the end and CoUninitialize will just bounce at 0. WHEN and IF we
+ // get around to unloading IL DLL's during execution prior to
+ // shutdown we will need to bump the reference one to compensate
+ // for this call.
+ if (dwReason == DLL_PROCESS_DETACH && !g_fForbidEnterEE)
+ {
+#ifdef FEATURE_MIXEDMODE
+ // If we're in a decent state, we need to free the memory associated
+ // with the IJW thunk fixups.
+ // we are not in a decent state if the process is terminating (lpReserved!=NULL)
+ if (g_fEEStarted && !g_fEEShutDown && !lpReserved)
+ {
+ PEImage::UnloadIJWModule(hInst);
+ }
+#endif // FEATURE_MIXEDMODE
+ }
+ }
+
+ fRetValue = ExecuteDLL_ReturnOrThrow(hr, fFromThunk);
+
+Exit:
+
+ END_ENTRYPOINT_THROWS;
+ return fRetValue;
+}
+#endif // !FEATURE_CORECLR
+
+
+Volatile<BOOL> g_bIsGarbageCollectorFullyInitialized = FALSE;
+
+void SetGarbageCollectorFullyInitialized()
+{
+ LIMITED_METHOD_CONTRACT;
+
+ g_bIsGarbageCollectorFullyInitialized = TRUE;
+}
+
+// Tells whether the garbage collector is fully initialized
+// Stronger than IsGCHeapInitialized
+BOOL IsGarbageCollectorFullyInitialized()
+{
+ LIMITED_METHOD_CONTRACT;
+
+ return g_bIsGarbageCollectorFullyInitialized;
+}
+
+//
+// Initialize the Garbage Collector
+//
+
+void InitializeGarbageCollector()
+{
+ CONTRACTL{
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ } CONTRACTL_END;
+
+ HRESULT hr;
+
+ // Build the special Free Object used by the Generational GC
+ _ASSERT(g_pFreeObjectMethodTable == NULL);
+ g_pFreeObjectMethodTable = (MethodTable *) new BYTE[sizeof(MethodTable)];
+ ZeroMemory(g_pFreeObjectMethodTable, sizeof(MethodTable));
+
+ // As the flags in the method table indicate there are no pointers
+ // in the object, there is no gc descriptor, and thus no need to adjust
+ // the pointer to skip the gc descriptor.
+
+ g_pFreeObjectMethodTable->SetBaseSize(ObjSizeOf (ArrayBase));
+ g_pFreeObjectMethodTable->SetComponentSize(1);
+
+ GCHeap *pGCHeap = GCHeap::CreateGCHeap();
+ if (!pGCHeap)
+ ThrowOutOfMemory();
+
+ hr = pGCHeap->Initialize();
+ IfFailThrow(hr);
+
+ // Thread for running finalizers...
+ FinalizerThread::FinalizerThreadCreate();
+
+ // Now we really have fully initialized the garbage collector
+ SetGarbageCollectorFullyInitialized();
+}
+
+/*****************************************************************************/
+/* This is here only so that if we get an exception we stop before we catch it */
+LONG DllMainFilter(PEXCEPTION_POINTERS p, PVOID pv)
+{
+ LIMITED_METHOD_CONTRACT;
+ _ASSERTE(!"Exception happened in mscorwks!DllMain!");
+ return EXCEPTION_EXECUTE_HANDLER;
+}
+
+//*****************************************************************************
+// This is the part of the old-style DllMain that initializes the
+// stuff that the EE team works on. It's called from the real DllMain
+// up in MSCOREE land. Separating the DllMain tasks is simply for
+// convenience due to the dual build trees.
+//*****************************************************************************
+BOOL STDMETHODCALLTYPE EEDllMain( // TRUE on success, FALSE on error.
+ HINSTANCE hInst, // Instance handle of the loaded module.
+ DWORD dwReason, // Reason for loading.
+ LPVOID lpReserved) // Unused.
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_TRIGGERS;
+
+ // this runs at the top of a thread, SO is not a concern here...
+ STATIC_CONTRACT_SO_NOT_MAINLINE;
+
+
+ // HRESULT hr;
+ // BEGIN_EXTERNAL_ENTRYPOINT(&hr);
+ // EE isn't spun up enough to use this macro
+
+ struct Param
+ {
+ HINSTANCE hInst;
+ DWORD dwReason;
+ LPVOID lpReserved;
+ void **pTlsData;
+ } param;
+ param.hInst = hInst;
+ param.dwReason = dwReason;
+ param.lpReserved = lpReserved;
+ param.pTlsData = NULL;
+
+ // Can't use PAL_TRY/EX_TRY here as they access the ClrDebugState which gets blown away as part of the
+ // PROCESS_DETACH path. Must use special PAL_TRY_FOR_DLLMAIN, passing the reason were in the DllMain.
+ PAL_TRY_FOR_DLLMAIN(Param *, pParam, &param, pParam->dwReason)
+ {
+
+ switch (pParam->dwReason)
+ {
+ case DLL_PROCESS_ATTACH:
+ {
+ // We cache the SystemInfo for anyone to use throughout the
+ // life of the DLL.
+ GetSystemInfo(&g_SystemInfo);
+
+ // Remember module instance
+ g_pMSCorEE = pParam->hInst;
+
+#ifndef FEATURE_CORECLR
+ CoreClrCallbacks cccallbacks;
+ cccallbacks.m_hmodCoreCLR = (HINSTANCE)g_pMSCorEE;
+ cccallbacks.m_pfnIEE = IEE;
+ cccallbacks.m_pfnGetCORSystemDirectory = GetCORSystemDirectoryInternaL;
+ cccallbacks.m_pfnGetCLRFunction = GetCLRFunction;
+
+ InitUtilcode(cccallbacks);
+#endif // !FEATURE_CORECLR
+
+ // Set callbacks so that LoadStringRC knows which language our
+ // threads are in so that it can return the proper localized string.
+ // TODO: This shouldn't rely on the LCID (id), but only the name
+ SetResourceCultureCallbacks(GetThreadUICultureNames,
+ GetThreadUICultureId);
+
+ InitEEPolicy();
+ InitHostProtectionManager();
+
+ break;
+ }
+
+ case DLL_PROCESS_DETACH:
+ {
+ // lpReserved is NULL if we're here because someone called FreeLibrary
+ // and non-null if we're here because the process is exiting.
+ // Since nobody should ever be calling FreeLibrary on mscorwks.dll, lpReserved
+ // should always be non NULL.
+ _ASSERTE(pParam->lpReserved || !g_fEEStarted);
+ g_fProcessDetach = TRUE;
+
+#if defined(ENABLE_CONTRACTS_IMPL) && defined(FEATURE_STACK_PROBE)
+ // We are shutting down process. No need to check SO contract.
+ // And it is impossible to enforce SO contract in global dtor, like ModIntPairList.
+ g_EnableDefaultRWValidation = FALSE;
+#endif
+
+ if (g_fEEStarted)
+ {
+ // GetThread() may be set to NULL for Win9x during shutdown.
+ Thread *pThread = GetThread();
+ if (GCHeap::IsGCInProgress() &&
+ ( (pThread && (pThread != ThreadSuspend::GetSuspensionThread() ))
+ || !g_fSuspendOnShutdown))
+ {
+ g_fEEShutDown |= ShutDown_Phase2;
+ break;
+ }
+
+ LOG((LF_STARTUP, INFO3, "EEShutDown invoked from EEDllMain"));
+ EEShutDown(TRUE); // shut down EE if it was started up
+ }
+ else
+ {
+ CLRRemoveVectoredHandlers();
+ }
+ break;
+ }
+
+ case DLL_THREAD_DETACH:
+ {
+ // Don't destroy threads here if we're in shutdown (shutdown will
+ // clean up for us instead).
+
+ // Store the TLS data; we'll need it later and we might NULL the slot in DetachThread.
+ // This would be problematic because we can't depend on the FLS still existing.
+ pParam->pTlsData = CExecutionEngine::CheckThreadStateNoCreate(0
+#ifdef _DEBUG
+ // When we get here, OS has destroyed FLS, so FlsGetValue returns NULL now.
+ // We have validation code in CExecutionEngine::CheckThreadStateNoCreate to ensure that
+ // our TLS and FLS data are consistent, but since FLS has been destroyed, we need
+ // to silent the check there. The extra arg for check build is for this purpose.
+ , TRUE
+#endif
+ );
+#ifdef FEATURE_IMPLICIT_TLS
+ Thread* thread = GetThread();
+#else
+ // Don't use GetThread because perhaps we didn't initialize yet, or we
+ // have already shutdown the EE. Note that there is a race here. We
+ // might ask for TLS from a slot we just released. We are assuming that
+ // nobody re-allocates that same slot while we are doing this. It just
+ // isn't worth locking for such an obscure case.
+ DWORD tlsVal = GetThreadTLSIndex();
+ Thread *thread = (tlsVal != (DWORD)-1)?(Thread *) UnsafeTlsGetValue(tlsVal):NULL;
+#endif
+ if (thread)
+ {
+#ifdef FEATURE_COMINTEROP
+ // reset the CoInitialize state
+ // so we don't call CoUninitialize during thread detach
+ thread->ResetCoInitialized();
+#endif // FEATURE_COMINTEROP
+ // For case where thread calls ExitThread directly, we need to reset the
+ // frame pointer. Otherwise stackwalk would AV. We need to do it in cooperative mode.
+ // We need to set m_GCOnTransitionsOK so this thread won't trigger GC when toggle GC mode
+ if (thread->m_pFrame != FRAME_TOP)
+ {
+#ifdef _DEBUG
+ thread->m_GCOnTransitionsOK = FALSE;
+#endif
+ GCX_COOP_NO_DTOR();
+ thread->m_pFrame = FRAME_TOP;
+ GCX_COOP_NO_DTOR_END();
+ }
+ thread->DetachThread(TRUE);
+ }
+ }
+ }
+
+ }
+ PAL_EXCEPT_FILTER(DllMainFilter)
+ {
+ }
+ PAL_ENDTRY;
+
+ if (dwReason == DLL_THREAD_DETACH || dwReason == DLL_PROCESS_DETACH)
+ {
+ if (CLRMemoryHosted())
+ {
+ // A host may not support memory operation inside OS loader lock.
+ // We will free these memory on finalizer thread.
+ CExecutionEngine::DetachTlsInfo(param.pTlsData);
+ }
+ else
+ {
+ CExecutionEngine::ThreadDetaching(param.pTlsData);
+ }
+ }
+ return TRUE;
+}
+
+#ifdef FEATURE_COMINTEROP_REGISTRATION
+//*****************************************************************************
+// Helper function to call the managed registration services.
+//*****************************************************************************
+enum EnumRegServicesMethods
+{
+ RegServicesMethods_RegisterAssembly = 0,
+ RegServicesMethods_UnregisterAssembly,
+ RegServicesMethods_LastMember
+};
+
+void InvokeRegServicesMethod(EnumRegServicesMethods Method, HMODULE hMod)
+{
+ CONTRACT_VOID
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ PRECONDITION(Method == RegServicesMethods_RegisterAssembly ||
+ Method == RegServicesMethods_UnregisterAssembly);
+ }
+ CONTRACT_END;
+
+ GCX_PREEMP();
+ Assembly *pAssembly = GetAppDomain()->LoadExplicitAssembly(hMod, TRUE);
+
+ {
+ GCX_COOP();
+
+ // The names of the RegistrationServices methods.
+ static const BinderMethodID aMethods[] =
+ {
+ METHOD__REGISTRATION_SERVICES__REGISTER_ASSEMBLY,
+ METHOD__REGISTRATION_SERVICES__UNREGISTER_ASSEMBLY
+ };
+
+ // Allocate the RegistrationServices object.
+ OBJECTREF RegServicesObj = AllocateObject(MscorlibBinder::GetClass(CLASS__REGISTRATION_SERVICES));
+ GCPROTECT_BEGIN(RegServicesObj)
+ {
+ MethodDescCallSite registrationMethod(aMethods[Method], &RegServicesObj);
+
+ ARG_SLOT Args[] =
+ {
+ ObjToArgSlot(RegServicesObj),
+ ObjToArgSlot(pAssembly->GetExposedObject()),
+ 0 // unused by UnregisterAssembly
+ };
+
+ registrationMethod.Call(Args);
+ }
+ GCPROTECT_END();
+ }
+ RETURN;
+}
+
+//*****************************************************************************
+// This entry point is called to register the classes contained inside a
+// COM+ assembly.
+//*****************************************************************************
+STDAPI EEDllRegisterServer(HMODULE hMod)
+{
+
+ CONTRACTL{
+ NOTHROW;
+ GC_TRIGGERS;
+ ENTRY_POINT;
+ MODE_PREEMPTIVE;
+ } CONTRACTL_END;
+
+ HRESULT hr = S_OK;
+
+ // Start up the runtime since we are going to use managed code to actually
+ // do the registration.
+ IfFailGo(EnsureEEStarted(COINITEE_DEFAULT));
+
+ BEGIN_EXTERNAL_ENTRYPOINT(&hr)
+ {
+ InvokeRegServicesMethod(RegServicesMethods_RegisterAssembly, hMod);
+ }
+ END_EXTERNAL_ENTRYPOINT;
+
+ErrExit:
+
+
+ return hr;
+}
+
+//*****************************************************************************
+// This entry point is called to unregister the classes contained inside a
+// COM+ assembly.
+//*****************************************************************************
+STDAPI EEDllUnregisterServer(HMODULE hMod)
+{
+
+ CONTRACTL{
+ NOTHROW;
+ GC_TRIGGERS;
+ MODE_PREEMPTIVE;
+ ENTRY_POINT;
+ } CONTRACTL_END;
+
+ HRESULT hr = S_OK;
+
+ // Start up the runtime since we are going to use managed code to actually
+ // do the registration.
+ IfFailGo(EnsureEEStarted(COINITEE_DEFAULT));
+
+ BEGIN_EXTERNAL_ENTRYPOINT(&hr)
+ {
+ InvokeRegServicesMethod(RegServicesMethods_UnregisterAssembly, hMod);
+ }
+ END_EXTERNAL_ENTRYPOINT;
+ErrExit:
+
+
+ return hr;
+}
+#endif // FEATURE_COMINTEROP_REGISTRATION
+
+#ifdef FEATURE_IPCMAN
+extern CCLRSecurityAttributeManager s_CLRSecurityAttributeManager;
+#endif // FEATURE_IPCMAN
+
+
+#ifdef DEBUGGING_SUPPORTED
+//
+// InitializeDebugger initialized the Runtime-side COM+ Debugging Services
+//
+static void InitializeDebugger(void)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ // Ensure that if we throw, we'll call TerminateDebugger to cleanup.
+ // This makes our Init more atomic by avoiding partially-init states.
+ class EnsureCleanup {
+ BOOL fNeedCleanup;
+ public:
+ EnsureCleanup()
+ {
+ fNeedCleanup = TRUE;
+ }
+
+ void SuppressCleanup()
+ {
+ fNeedCleanup = FALSE;
+ }
+
+ ~EnsureCleanup()
+ {
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_MODE_ANY;
+
+ if (fNeedCleanup)
+ {
+ TerminateDebugger();
+ }
+ }
+ } hCleanup;
+
+ HRESULT hr = S_OK;
+
+ LOG((LF_CORDB, LL_INFO10, "Initializing left-side debugging services.\n"));
+
+ FARPROC gi = (FARPROC) &CorDBGetInterface;
+
+ // Init the interface the EE provides to the debugger,
+ // ask the debugger for its interface, and if all goes
+ // well call Startup on the debugger.
+ EEDbgInterfaceImpl::Init();
+ _ASSERTE(g_pEEDbgInterfaceImpl != NULL); // throws on OOM
+
+ // This allocates the Debugger object.
+ typedef HRESULT __cdecl CORDBGETINTERFACE(DebugInterface**);
+ hr = ((CORDBGETINTERFACE*)gi)(&g_pDebugInterface);
+ IfFailThrow(hr);
+
+ g_pDebugInterface->SetEEInterface(g_pEEDbgInterfaceImpl);
+
+ {
+ hr = g_pDebugInterface->Startup(); // throw on error
+ _ASSERTE(SUCCEEDED(hr));
+
+#ifdef FEATURE_CORECLR
+ //
+ // If the debug pack is not installed, Startup will return S_FALSE
+ // and we should cleanup and proceed without debugging support.
+ //
+ if (hr != S_OK)
+ {
+ return;
+ }
+#endif // FEATURE_CORECLR
+ }
+
+#if !defined(FEATURE_CORECLR) // simple hosting
+ // If there's a DebuggerThreadControl interface, then we
+ // need to update the DebuggerSpecialThread list.
+ if (CorHost::GetDebuggerThreadControl())
+ {
+ hr = CorHost::RefreshDebuggerSpecialThreadList();
+ _ASSERTE((SUCCEEDED(hr)) && (hr != S_FALSE));
+
+ // So we don't think this will ever fail, but just in case...
+ IfFailThrow(hr);
+ }
+
+ // If there is a DebuggerThreadControl interface, then it was set before the debugger
+ // was initialized and we need to provide this interface now. If debugging is already
+ // initialized then the IDTC pointer is passed in when it is set through CorHost
+ IDebuggerThreadControl *pDTC = CorHost::GetDebuggerThreadControl();
+
+ if (pDTC != NULL)
+ {
+ g_pDebugInterface->SetIDbgThreadControl(pDTC);
+ }
+#endif // !defined(FEATURE_CORECLR)
+
+ LOG((LF_CORDB, LL_INFO10, "Left-side debugging services setup.\n"));
+
+ hCleanup.SuppressCleanup();
+
+ return;
+}
+
+
+//
+// TerminateDebugger shuts down the Runtime-side COM+ Debugging Services
+// InitializeDebugger will call this if it fails.
+// This may be called even if the debugger is partially initialized.
+// This can be called multiple times.
+//
+static void TerminateDebugger(void)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ LOG((LF_CORDB, LL_INFO10, "Shutting down left-side debugger services.\n"));
+
+ // If initialized failed really early, then we didn't even get the Debugger object.
+ if (g_pDebugInterface != NULL)
+ {
+ // Notify the out-of-process debugger that shutdown of the in-process debugging support has begun. This is only
+ // really used in interop debugging scenarios.
+ g_pDebugInterface->ShutdownBegun();
+
+ // This will kill the helper thread, delete the Debugger object, and free all resources.
+ g_pDebugInterface->StopDebugger();
+ }
+
+ g_CORDebuggerControlFlags = DBCF_NORMAL_OPERATION;
+
+#if !defined(FEATURE_CORECLR) // simple hosting
+ CorHost::CleanupDebuggerThreadControl();
+#endif // !defined(FEATURE_CORECLR)
+}
+
+
+#ifdef FEATURE_IPCMAN
+// ---------------------------------------------------------------------------
+// Initialize InterProcess Communications for COM+
+// 1. Allocate an IPCManager Implementation and hook it up to our interface *
+// 2. Call proper init functions to activate relevant portions of IPC block
+// ---------------------------------------------------------------------------
+static HRESULT InitializeIPCManager(void)
+{
+ CONTRACTL{
+ NOTHROW;
+ GC_TRIGGERS;
+ MODE_ANY;
+ } CONTRACTL_END;
+
+ HRESULT hr = S_OK;
+ HINSTANCE hInstIPCBlockOwner = 0;
+
+ DWORD pid = 0;
+ // Allocate the Implementation. Everyone else will work through the interface
+ g_pIPCManagerInterface = new (nothrow) IPCWriterInterface();
+
+ if (g_pIPCManagerInterface == NULL)
+ {
+ hr = E_OUTOFMEMORY;
+ goto errExit;
+ }
+
+ pid = GetCurrentProcessId();
+
+
+ // Do general init
+ hr = g_pIPCManagerInterface->Init();
+
+ if (!SUCCEEDED(hr))
+ {
+ goto errExit;
+ }
+
+ // Generate private IPCBlock for our PID. Note that for the other side of the debugger,
+ // they'll hook up to the debuggee's pid (and not their own). So we still
+ // have to pass the PID in.
+ EX_TRY
+ {
+ // <TODO>This should go away in the future.</TODO>
+ hr = g_pIPCManagerInterface->CreateLegacyPrivateBlockTempV4OnPid(pid, FALSE, &hInstIPCBlockOwner);
+ }
+ EX_CATCH_HRESULT(hr);
+
+ if (hr == HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS))
+ {
+ // We failed to create the IPC block because it has already been created. This means that
+ // two mscoree's have been loaded into the process.
+ PathString strFirstModule;
+ PathString strSecondModule;
+ EX_TRY
+ {
+ // Get the name and path of the first loaded MSCOREE.DLL.
+ if (!hInstIPCBlockOwner || !WszGetModuleFileName(hInstIPCBlockOwner, strFirstModule))
+ strFirstModule.Set(W("<Unknown>"));
+
+ // Get the name and path of the second loaded MSCOREE.DLL.
+ if (!WszGetModuleFileName(g_pMSCorEE, strSecondModule))
+ strSecondModule.Set(W("<Unknown>"));
+ }
+ EX_CATCH_HRESULT(hr);
+ // Load the format strings for the title and the message body.
+ EEMessageBoxCatastrophic(IDS_EE_TWO_LOADED_MSCOREE_MSG, IDS_EE_TWO_LOADED_MSCOREE_TITLE, strFirstModule, strSecondModule);
+ goto errExit;
+ }
+ else
+ {
+ PathString temp;
+ if (!WszGetModuleFileName(GetModuleInst(),
+ temp
+ ))
+ {
+ hr = HRESULT_FROM_GetLastErrorNA();
+ }
+ else
+ {
+ EX_TRY
+ {
+ if (temp.GetCount() + 1 > MAX_LONGPATH)
+ {
+ hr = E_FAIL;
+ }
+ else
+ {
+ wcscpy_s((PWSTR)g_pIPCManagerInterface->GetInstancePath(),temp.GetCount() + 1,temp);
+ }
+ }
+ EX_CATCH_HRESULT(hr);
+ }
+ }
+
+ // Generate public IPCBlock for our PID.
+ EX_TRY
+ {
+ hr = g_pIPCManagerInterface->CreateSxSPublicBlockOnPid(pid);
+ }
+ EX_CATCH_HRESULT(hr);
+
+
+errExit:
+ // If any failure, shut everything down.
+ if (!SUCCEEDED(hr))
+ TerminateIPCManager();
+
+ return hr;
+}
+#endif // FEATURE_IPCMAN
+
+#endif // DEBUGGING_SUPPORTED
+
+
+// ---------------------------------------------------------------------------
+// Marks the IPC block as initialized so that other processes know that the
+// block is safe to read
+// ---------------------------------------------------------------------------
+#ifdef FEATURE_IPCMAN
+static void PublishIPCManager(void)
+{
+ CONTRACTL{
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ } CONTRACTL_END;
+
+ if (g_pIPCManagerInterface != NULL)
+ g_pIPCManagerInterface->Publish();
+}
+#endif // FEATURE_IPCMAN
+
+
+
+#ifdef FEATURE_IPCMAN
+// ---------------------------------------------------------------------------
+// Terminate all InterProcess operations
+// ---------------------------------------------------------------------------
+static void TerminateIPCManager(void)
+{
+ CONTRACTL{
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ } CONTRACTL_END;
+
+ if (g_pIPCManagerInterface != NULL)
+ {
+ g_pIPCManagerInterface->Terminate();
+ delete g_pIPCManagerInterface;
+ g_pIPCManagerInterface = NULL;
+ }
+
+}
+#endif // FEATURE_IPCMAN
+
+#ifndef LOCALE_SPARENT
+#define LOCALE_SPARENT 0x0000006d
+#endif
+
+// ---------------------------------------------------------------------------
+// Impl for UtilLoadStringRC Callback: In VM, we let the thread decide culture
+// copy culture name into szBuffer and return length
+// ---------------------------------------------------------------------------
+static HRESULT GetThreadUICultureNames(__inout StringArrayList* pCultureNames)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ PRECONDITION(CheckPointer(pCultureNames));
+ SO_INTOLERANT;
+ }
+ CONTRACTL_END;
+
+ HRESULT hr = S_OK;
+
+ EX_TRY
+ {
+ InlineSString<LOCALE_NAME_MAX_LENGTH> sCulture;
+ InlineSString<LOCALE_NAME_MAX_LENGTH> sParentCulture;
+
+
+ Thread * pThread = GetThread();
+
+ if (pThread != NULL) {
+
+ // Switch to cooperative mode, since we'll be looking at managed objects
+ // and we don't want them moving on us.
+ GCX_COOP();
+
+ THREADBASEREF pThreadBase = (THREADBASEREF)pThread->GetExposedObjectRaw();
+
+ if (pThreadBase != NULL)
+ {
+ CULTUREINFOBASEREF pCurrentCulture = pThreadBase->GetCurrentUICulture();
+
+ if (pCurrentCulture != NULL)
+ {
+ STRINGREF cultureName = pCurrentCulture->GetName();
+
+ if (cultureName != NULL)
+ {
+ sCulture.Set(cultureName->GetBuffer(),cultureName->GetStringLength());
+ }
+
+ CULTUREINFOBASEREF pParentCulture = pCurrentCulture->GetParent();
+
+ if (pParentCulture != NULL)
+ {
+ STRINGREF parentCultureName = pParentCulture->GetName();
+
+ if (parentCultureName != NULL)
+ {
+ sParentCulture.Set(parentCultureName->GetBuffer(),parentCultureName->GetStringLength());
+ }
+
+ }
+ }
+ }
+ }
+ // If the lazily-initialized cultureinfo structures aren't initialized yet, we'll
+ // need to do the lookup the hard way.
+ if (sCulture.IsEmpty() || sParentCulture.IsEmpty())
+ {
+ LocaleIDValue id ;
+ int tmp; tmp = GetThreadUICultureId(&id); // TODO: We should use the name instead
+ _ASSERTE(tmp!=0 && id != UICULTUREID_DONTCARE);
+ SIZE_T cchParentCultureName=LOCALE_NAME_MAX_LENGTH;
+#ifdef FEATURE_USE_LCID
+ SIZE_T cchCultureName=LOCALE_NAME_MAX_LENGTH;
+ if (!NewApis::LCIDToLocaleName(id, sCulture.OpenUnicodeBuffer(static_cast<COUNT_T>(cchCultureName)), static_cast<int>(cchCultureName), 0))
+ {
+ hr = HRESULT_FROM_GetLastError();
+ }
+ sCulture.CloseBuffer();
+#else
+ sCulture.Set(id);
+#endif
+
+#ifndef FEATURE_PAL
+ if (!NewApis::GetLocaleInfoEx((LPCWSTR)sCulture, LOCALE_SPARENT, sParentCulture.OpenUnicodeBuffer(static_cast<COUNT_T>(cchParentCultureName)),static_cast<int>(cchParentCultureName)))
+ {
+ hr = HRESULT_FROM_GetLastError();
+ }
+ sParentCulture.CloseBuffer();
+#else // !FEATURE_PAL
+ sParentCulture = sCulture;
+#endif // !FEATURE_PAL
+ }
+ // (LPCWSTR) to restrict the size to null terminated size
+ pCultureNames->AppendIfNotThere((LPCWSTR)sCulture);
+ // Disabling for Dev10 for consistency with managed resource lookup (see AppCompat bug notes in ResourceFallbackManager.cs)
+ // Also, this is in the wrong order - put after the parent culture chain.
+ //AddThreadPreferredUILanguages(pCultureNames);
+ pCultureNames->AppendIfNotThere((LPCWSTR)sParentCulture);
+ pCultureNames->Append(SString::Empty());
+ }
+ EX_CATCH
+ {
+ hr=E_OUTOFMEMORY;
+ }
+ EX_END_CATCH(SwallowAllExceptions);
+
+ return hr;
+}
+
+// The exit code for the process is communicated in one of two ways. If the
+// entrypoint returns an 'int' we take that. Otherwise we take a latched
+// process exit code. This can be modified by the app via System.SetExitCode().
+static INT32 LatchedExitCode;
+
+void SetLatchedExitCode (INT32 code)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ SO_TOLERANT;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ STRESS_LOG1(LF_SYNC, LL_INFO10, "SetLatchedExitCode = %d\n", code);
+ LatchedExitCode = code;
+}
+
+INT32 GetLatchedExitCode (void)
+{
+ LIMITED_METHOD_CONTRACT;
+ return LatchedExitCode;
+}
+
+
+// ---------------------------------------------------------------------------
+// Impl for UtilLoadStringRC Callback: In VM, we let the thread decide culture
+// Return an int uniquely describing which language this thread is using for ui.
+// ---------------------------------------------------------------------------
+// TODO: Callers should use names, not LCIDs
+#ifdef FEATURE_USE_LCID
+static int GetThreadUICultureId(__out LocaleIDValue* pLocale)
+{
+ CONTRACTL{
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ SO_INTOLERANT;;
+ } CONTRACTL_END;
+
+
+
+ int Result = UICULTUREID_DONTCARE;
+
+ Thread * pThread = GetThread();
+
+ if (pThread != NULL) {
+
+ // Switch to cooperative mode, since we'll be looking at managed objects
+ // and we don't want them moving on us.
+ GCX_COOP();
+
+ THREADBASEREF pThreadBase = (THREADBASEREF)pThread->GetExposedObjectRaw();
+ if (pThreadBase != NULL)
+ {
+ CULTUREINFOBASEREF pCurrentCulture = pThreadBase->GetCurrentUICulture();
+
+ if (pCurrentCulture != NULL)
+ {
+ STRINGREF cultureName = pCurrentCulture->GetName();
+ _ASSERT(cultureName != NULL);
+
+ if ((Result = NewApis::LocaleNameToLCID(cultureName->GetBuffer(), 0)) == 0)
+ Result = (int)UICULTUREID_DONTCARE;
+ }
+ }
+ }
+
+ if (Result == (int)UICULTUREID_DONTCARE)
+ {
+ // This thread isn't set up to use a non-default culture. Let's grab the default
+ // one and return that.
+
+ Result = COMNlsInfo::CallGetUserDefaultUILanguage();
+
+ if (Result == 0 || Result == (int)UICULTUREID_DONTCARE)
+ Result = GetUserDefaultLangID();
+
+ _ASSERTE(Result != 0);
+ if (Result == 0)
+ {
+ Result = (int)UICULTUREID_DONTCARE;
+ }
+
+ }
+ *pLocale=Result;
+ return Result;
+}
+#else
+// TODO: Callers should use names, not LCIDs
+static int GetThreadUICultureId(__out LocaleIDValue* pLocale)
+{
+ CONTRACTL{
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ SO_INTOLERANT;;
+ } CONTRACTL_END;
+
+ _ASSERTE(sizeof(LocaleIDValue)/sizeof(WCHAR) >= LOCALE_NAME_MAX_LENGTH);
+
+ int Result = 0;
+
+ Thread * pThread = GetThread();
+
+ if (pThread != NULL) {
+
+ // Switch to cooperative mode, since we'll be looking at managed objects
+ // and we don't want them moving on us.
+ GCX_COOP();
+
+ THREADBASEREF pThreadBase = (THREADBASEREF)pThread->GetExposedObjectRaw();
+ if (pThreadBase != NULL)
+ {
+ CULTUREINFOBASEREF pCurrentCulture = pThreadBase->GetCurrentUICulture();
+
+ if (pCurrentCulture != NULL)
+ {
+ STRINGREF currentCultureName = pCurrentCulture->GetName();
+
+ if (currentCultureName != NULL)
+ {
+ int cchCurrentCultureNameResult = currentCultureName->GetStringLength();
+ if (cchCurrentCultureNameResult < LOCALE_NAME_MAX_LENGTH)
+ {
+ memcpy(*pLocale, currentCultureName->GetBuffer(), cchCurrentCultureNameResult*sizeof(WCHAR));
+ (*pLocale)[cchCurrentCultureNameResult]='\0';
+ Result=cchCurrentCultureNameResult;
+ }
+ }
+
+ }
+ }
+ }
+ if (Result == 0)
+ {
+#ifndef FEATURE_PAL
+ // This thread isn't set up to use a non-default culture. Let's grab the default
+ // one and return that.
+
+ Result = NewApis::GetUserDefaultLocaleName(*pLocale, LOCALE_NAME_MAX_LENGTH);
+
+ _ASSERTE(Result != 0);
+#else // !FEATURE_PAL
+ static const WCHAR enUS[] = W("en-US");
+ memcpy(*pLocale, enUS, sizeof(enUS));
+ Result = sizeof(enUS);
+#endif // !FEATURE_PAL
+ }
+ return Result;
+}
+
+#endif // FEATURE_USE_LCID
+// ---------------------------------------------------------------------------
+// Export shared logging code for JIT, et.al.
+// ---------------------------------------------------------------------------
+#ifdef _DEBUG
+
+extern VOID LogAssert( LPCSTR szFile, int iLine, LPCSTR expr);
+extern "C"
+//__declspec(dllexport)
+VOID STDMETHODCALLTYPE LogHelp_LogAssert( LPCSTR szFile, int iLine, LPCSTR expr)
+{
+ CONTRACTL {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ ENTRY_POINT;
+ PRECONDITION(CheckPointer(szFile));
+ PRECONDITION(CheckPointer(expr));
+ } CONTRACTL_END;
+
+ BEGIN_ENTRYPOINT_VOIDRET;
+ LogAssert(szFile, iLine, expr);
+ END_ENTRYPOINT_VOIDRET;
+
+}
+
+extern BOOL NoGuiOnAssert();
+extern "C"
+//__declspec(dllexport)
+BOOL STDMETHODCALLTYPE LogHelp_NoGuiOnAssert()
+{
+ LIMITED_METHOD_CONTRACT;
+ BOOL fRet = FALSE;
+ BEGIN_ENTRYPOINT_VOIDRET;
+ fRet = NoGuiOnAssert();
+ END_ENTRYPOINT_VOIDRET;
+ return fRet;
+}
+
+extern VOID TerminateOnAssert();
+extern "C"
+//__declspec(dllexport)
+VOID STDMETHODCALLTYPE LogHelp_TerminateOnAssert()
+{
+ LIMITED_METHOD_CONTRACT;
+ BEGIN_ENTRYPOINT_VOIDRET;
+// __asm int 3;
+ TerminateOnAssert();
+ END_ENTRYPOINT_VOIDRET;
+
+}
+
+#else // !_DEBUG
+
+extern "C"
+//__declspec(dllexport)
+VOID STDMETHODCALLTYPE LogHelp_LogAssert( LPCSTR szFile, int iLine, LPCSTR expr) {
+ LIMITED_METHOD_CONTRACT;
+
+ //BEGIN_ENTRYPOINT_VOIDRET;
+ //END_ENTRYPOINT_VOIDRET;
+}
+
+extern "C"
+//__declspec(dllexport)
+BOOL STDMETHODCALLTYPE LogHelp_NoGuiOnAssert() {
+ LIMITED_METHOD_CONTRACT;
+
+ //BEGIN_ENTRYPOINT_VOIDRET;
+ //END_ENTRYPOINT_VOIDRET;
+
+ return FALSE;
+}
+
+extern "C"
+//__declspec(dllexport)
+VOID STDMETHODCALLTYPE LogHelp_TerminateOnAssert() {
+ LIMITED_METHOD_CONTRACT;
+
+ //BEGIN_ENTRYPOINT_VOIDRET;
+ //END_ENTRYPOINT_VOIDRET;
+
+}
+
+#endif // _DEBUG
+
+
+#ifndef ENABLE_PERF_COUNTERS
+//
+// perf counter stubs for builds which don't have perf counter support
+// These are needed because we export these functions in our DLL
+
+
+Perf_Contexts* STDMETHODCALLTYPE GetPrivateContextsPerfCounters()
+{
+ LIMITED_METHOD_CONTRACT;
+
+ //BEGIN_ENTRYPOINT_VOIDRET;
+ //END_ENTRYPOINT_VOIDRET;
+
+ return NULL;
+}
+
+#endif
+
+
+#ifdef ENABLE_CONTRACTS_IMPL
+
+// Returns TRUE if any contract violation suppressions are in effect.
+BOOL AreAnyViolationBitsOn()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ SO_TOLERANT;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+ UINT_PTR violationMask = GetClrDebugState()->ViolationMask();
+ violationMask &= ~((UINT_PTR)CanFreeMe); //CanFreeMe is a borrowed bit and has nothing to do with violations
+ if (violationMask & ((UINT_PTR)BadDebugState))
+ {
+ return FALSE;
+ }
+
+ return violationMask != 0;
+}
+
+
+// This function is intentionally invoked inside a big CONTRACT_VIOLATION that turns on every violation
+// bit on the map. The dynamic contract at the beginning *should* turn off those violation bits.
+// The body of this function tests to see that it did exactly that. This is to prevent the VSWhidbey B#564831 fiasco
+// from ever recurring.
+void ContractRegressionCheckInner()
+{
+ // DO NOT TURN THIS CONTRACT INTO A STATIC CONTRACT!!! The very purpose of this function
+ // is to ensure that dynamic contracts disable outstanding contract violation bits.
+ // This code only runs once at process startup so it's not going pooch the checked build perf.
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ FORBID_FAULT;
+ LOADS_TYPE(CLASS_LOAD_BEGIN);
+ CANNOT_TAKE_LOCK;
+ }
+ CONTRACTL_END
+
+ if (AreAnyViolationBitsOn())
+ {
+ // If we got here, the contract above FAILED to turn off one or more violation bits. This is a
+ // huge diagnostics hole and must be fixed immediately.
+ _ASSERTE(!("WARNING: mscorwks has detected an internal error that may indicate contracts are"
+ " being silently disabled across the runtime. Do not ignore this assert!"));
+ }
+}
+
+// This function executes once per process to ensure our CONTRACT_VIOLATION() mechanism
+// is properly scope-limited by nested contracts.
+void ContractRegressionCheck()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ {
+ // DO NOT "FIX" THIS CONTRACT_VIOLATION!!!
+ // The existence of this CONTRACT_VIOLATION is not a bug. This is debug-only code specifically written
+ // to test the CONTRACT_VIOLATION mechanism itself. This is needed to prevent a regression of
+ // B#564831 (which left a huge swath of contracts silently disabled for over six months)
+ PERMANENT_CONTRACT_VIOLATION(ThrowsViolation
+ | GCViolation
+ | FaultViolation
+ | LoadsTypeViolation
+ | TakesLockViolation
+ , ReasonContractInfrastructure
+ );
+ {
+ FAULT_NOT_FATAL();
+ ContractRegressionCheckInner();
+ }
+ }
+
+ if (AreAnyViolationBitsOn())
+ {
+ // If we got here, the CONTRACT_VIOLATION() holder left one or more violation bits turned ON
+ // after we left its scope. This is a huge diagnostic hole and must be fixed immediately.
+ _ASSERTE(!("WARNING: mscorwks has detected an internal error that may indicate contracts are"
+ " being silently disabled across the runtime. Do not ignore this assert!"));
+ }
+
+}
+
+#endif // ENABLE_CONTRACTS_IMPL
+
+#ifndef FEATURE_CORECLR
+//-------------------------------------------------------------------------
+// CorCommandLine state and methods
+//-------------------------------------------------------------------------
+// Class to encapsulate Cor Command line processing
+
+// Statics for the CorCommandLine class
+DWORD CorCommandLine::m_NumArgs = 0;
+LPWSTR *CorCommandLine::m_ArgvW = 0;
+
+LPWSTR CorCommandLine::m_pwszAppFullName = NULL;
+DWORD CorCommandLine::m_dwManifestPaths = 0;
+LPWSTR *CorCommandLine::m_ppwszManifestPaths = NULL;
+DWORD CorCommandLine::m_dwActivationData = 0;
+LPWSTR *CorCommandLine::m_ppwszActivationData = NULL;
+
+#ifdef _DEBUG
+LPCWSTR g_CommandLine;
+#endif
+
+// Set argvw from command line
+/* static */
+HRESULT CorCommandLine::SetArgvW(LPCWSTR lpCommandLine)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ INJECT_FAULT(return E_OUTOFMEMORY;);
+
+ PRECONDITION(CheckPointer(lpCommandLine));
+ }
+ CONTRACTL_END
+
+ HRESULT hr = S_OK;
+ if(!m_ArgvW) {
+ INDEBUG(g_CommandLine = lpCommandLine);
+
+ InitializeLogging(); // This is so early, we may not be initialized
+ LOG((LF_ALWAYS, LL_INFO10, "Executing program with command line '%S'\n", lpCommandLine));
+
+ m_ArgvW = SegmentCommandLine(lpCommandLine, &m_NumArgs);
+
+ if (!m_ArgvW)
+ return E_OUTOFMEMORY;
+
+ // Click once specific parsing
+ hr = ReadClickOnceEnvVariables();
+ }
+
+ return hr;
+}
+
+// Retrieve the command line
+/* static */
+LPWSTR* CorCommandLine::GetArgvW(DWORD *pNumArgs)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ if (pNumArgs != 0)
+ *pNumArgs = m_NumArgs;
+
+ return m_ArgvW;
+}
+
+HRESULT CorCommandLine::ReadClickOnceEnvVariables()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ } CONTRACTL_END;
+
+ HRESULT hr = S_OK;
+
+ BEGIN_SO_INTOLERANT_CODE_NO_THROW_CHECK_THREAD(return COR_E_STACKOVERFLOW);
+
+ EX_TRY
+ {
+ // Find out if this is a ClickOnce application being activated.
+ PathString m_pwszAppFullNameHolder;
+ DWORD cAppFullName = WszGetEnvironmentVariable(g_pwzClickOnceEnv_FullName, m_pwszAppFullNameHolder);
+ if (cAppFullName > 0) {
+ // get the application full name.
+ m_pwszAppFullName = m_pwszAppFullNameHolder.GetCopyOfUnicodeString();
+
+ // reset the variable now that we read it so child processes
+ // do not think they are a clickonce app.
+ WszSetEnvironmentVariable(g_pwzClickOnceEnv_FullName, NULL);
+
+ // see if we have application manifest files.
+ DWORD dwManifestPaths = 0;
+ while (1) {
+ StackSString manifestFile(g_pwzClickOnceEnv_Manifest);
+ StackSString buf;
+ COUNT_T size = buf.GetUnicodeAllocation();
+ _itow_s(dwManifestPaths, buf.OpenUnicodeBuffer(size), size, 10);
+ buf.CloseBuffer();
+ manifestFile.Append(buf);
+ SString temp;
+ if (WszGetEnvironmentVariable(manifestFile.GetUnicode(), temp) > 0)
+ dwManifestPaths++;
+ else
+ break;
+ }
+ m_ppwszManifestPaths = new LPWSTR[dwManifestPaths];
+ for (DWORD i=0; i<dwManifestPaths; i++) {
+ StackSString manifestFile(g_pwzClickOnceEnv_Manifest);
+ StackSString buf;
+ COUNT_T size = buf.GetUnicodeAllocation();
+ _itow_s(i, buf.OpenUnicodeBuffer(size), size, 10);
+ buf.CloseBuffer();
+ manifestFile.Append(buf);
+ PathString m_ppwszManifestPathsHolder;
+ DWORD cManifestPath = WszGetEnvironmentVariable(manifestFile.GetUnicode(), m_ppwszManifestPathsHolder);
+ if (cManifestPath > 0) {
+
+ m_ppwszManifestPaths[i] = m_ppwszManifestPathsHolder.GetCopyOfUnicodeString();
+ WszSetEnvironmentVariable(manifestFile.GetUnicode(), NULL); // reset the env. variable.
+ }
+ }
+ m_dwManifestPaths = dwManifestPaths;
+
+ // see if we have activation data arguments.
+ DWORD dwActivationData = 0;
+ while (1) {
+ StackSString activationData(g_pwzClickOnceEnv_Parameter);
+ StackSString buf;
+ COUNT_T size = buf.GetUnicodeAllocation();
+ _itow_s(dwActivationData, buf.OpenUnicodeBuffer(size), size, 10);
+ buf.CloseBuffer();
+ activationData.Append(buf);
+ SString temp;
+ if (WszGetEnvironmentVariable(activationData.GetUnicode(), temp) > 0)
+ dwActivationData++;
+ else
+ break;
+ }
+ m_ppwszActivationData = new LPWSTR[dwActivationData];
+ for (DWORD i=0; i<dwActivationData; i++) {
+ StackSString activationData(g_pwzClickOnceEnv_Parameter);
+ StackSString buf;
+ COUNT_T size = buf.GetUnicodeAllocation();
+ _itow_s(i, buf.OpenUnicodeBuffer(size), size, 10);
+ buf.CloseBuffer();
+ activationData.Append(buf);
+ PathString m_ppwszActivationDataHolder;
+ DWORD cActivationData = WszGetEnvironmentVariable(activationData.GetUnicode(), m_ppwszActivationDataHolder);
+ if (cActivationData > 0) {
+ m_ppwszActivationData[i] = m_ppwszActivationDataHolder.GetCopyOfUnicodeString();
+ WszSetEnvironmentVariable(activationData.GetUnicode(), NULL); // reset the env. variable.
+ }
+ }
+ m_dwActivationData = dwActivationData;
+ }
+ }
+ EX_CATCH_HRESULT(hr);
+
+ END_SO_INTOLERANT_CODE;
+
+ return hr;
+}
+
+#endif // !FEATURE_CORECLR
+
+#endif // CROSSGEN_COMPILE
+
+
+//
+// GetOSVersion - Gets the real OS version bypassing the OS compatibility shim
+// Mscoree.dll resides in System32 dir and is always excluded from compat shim.
+// This function calls mscoree!shim function via mscoreei ICLRRuntimeHostInternal interface
+// to get the OS version. We do not do this PAL or coreclr..we direclty call the OS
+// in that case.
+//
+BOOL GetOSVersion(LPOSVERSIONINFO lposVer)
+{
+#if !defined(FEATURE_CORECLR) && !defined(CROSSGEN_COMPILE)
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ SO_TOLERANT;
+ }
+ CONTRACTL_END;
+
+
+ //declared static to cache the version info
+ static OSVERSIONINFOEX osvi = {0};
+ BOOL ret = TRUE;
+
+ BEGIN_SO_INTOLERANT_CODE_NOTHROW(GetThread(), return FALSE);
+
+ //If not yet cached get the OS version info
+ if(osvi.dwMajorVersion == 0)
+ {
+ osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
+
+ ReleaseHolder<ICLRRuntimeHostInternal> pRuntimeHostInternal;
+ //Get the interface
+ HRESULT hr = g_pCLRRuntime->GetInterface(CLSID_CLRRuntimeHostInternal,
+ IID_ICLRRuntimeHostInternal,
+ &pRuntimeHostInternal);
+
+ _ASSERT(SUCCEEDED(hr));
+
+ //Call mscoree!GetVersionExWrapper() through mscoreei interface method
+ hr = pRuntimeHostInternal->GetTrueOSVersion((LPOSVERSIONINFO)&osvi);
+ if(!SUCCEEDED(hr))
+ {
+ osvi.dwMajorVersion = 0;
+ ret = FALSE;
+ goto FUNCEND;
+ }
+ }
+
+ if(lposVer->dwOSVersionInfoSize==sizeof(OSVERSIONINFOEX)||lposVer->dwOSVersionInfoSize==sizeof(OSVERSIONINFO))
+ {
+ //Copy the cached version info to the return memory location
+ memcpy(lposVer,&osvi, lposVer->dwOSVersionInfoSize);
+ }
+ else
+ {
+ //return failure if dwOSVersionInfoSize not set properly
+ ret = FALSE;
+ }
+
+FUNCEND:
+ END_SO_INTOLERANT_CODE;
+
+ return ret;
+#else
+// Fix for warnings when building against WinBlue build 9444.0.130614-1739
+// warning C4996: 'GetVersionExW': was declared deprecated
+// externalapis\windows\winblue\sdk\inc\sysinfoapi.h(442)
+// Deprecated. Use VerifyVersionInfo* or IsWindows* macros from VersionHelpers.
+#pragma warning( disable : 4996 )
+ return WszGetVersionEx(lposVer);
+#pragma warning( default : 4996 )
+#endif
+}