// 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. //***************************************************************************** // CorHost.cpp // // Implementation for the meta data dispenser code. // //***************************************************************************** #include "common.h" #include "mscoree.h" #include "corhost.h" #include "excep.h" #include "threads.h" #include "jitinterface.h" #include "eeconfig.h" #include "dbginterface.h" #include "ceemain.h" #include "hosting.h" #include "eepolicy.h" #include "clrex.h" #include "comcallablewrapper.h" #include "invokeutil.h" #include "appdomain.inl" #include "vars.hpp" #include "comdelegate.h" #include "dllimportcallback.h" #include "eventtrace.h" #include "win32threadpool.h" #include "eventtrace.h" #include "finalizerthread.h" #include "threadsuspend.h" #ifndef FEATURE_PAL #include "dwreport.h" #endif // !FEATURE_PAL #include "stringarraylist.h" #ifdef FEATURE_PERFTRACING #include "eventpipe.h" #endif // FEATURE_PERFTRACING #ifdef FEATURE_COMINTEROP #include "winrttypenameconverter.h" #endif GVAL_IMPL_INIT(DWORD, g_fHostConfig, 0); #ifndef __GNUC__ EXTERN_C __declspec(thread) ThreadLocalInfo gCurrentThreadInfo; #else // !__GNUC__ EXTERN_C __thread ThreadLocalInfo gCurrentThreadInfo; #endif // !__GNUC__ #ifndef FEATURE_PAL EXTERN_C UINT32 _tls_index; #else // FEATURE_PAL UINT32 _tls_index = 0; #endif // FEATURE_PAL #if defined(FEATURE_WINDOWSPHONE) SVAL_IMPL_INIT(ECustomDumpFlavor, CCLRErrorReportingManager, g_ECustomDumpFlavor, DUMP_FLAVOR_Default); #endif #ifndef DACCESS_COMPILE extern void STDMETHODCALLTYPE EEShutDown(BOOL fIsDllUnloading); extern HRESULT STDAPICALLTYPE CoInitializeEE(DWORD fFlags); extern void PrintToStdOutA(const char *pszString); extern void PrintToStdOutW(const WCHAR *pwzString); extern BOOL g_fEEHostedStartup; //*************************************************************************** ULONG CorRuntimeHostBase::m_Version = 0; #if defined(FEATURE_WINDOWSPHONE) CCLRErrorReportingManager g_CLRErrorReportingManager; #endif // defined(FEATURE_WINDOWSPHONE) #endif // !DAC typedef DPTR(CONNID) PTR_CONNID; // Keep track connection id and name #ifndef DACCESS_COMPILE // *** ICorRuntimeHost methods *** CorHost2::CorHost2() : m_fFirstToLoadCLR(FALSE), m_fStarted(FALSE), m_fAppDomainCreated(FALSE) { LIMITED_METHOD_CONTRACT; } static DangerousNonHostedSpinLock lockOnlyOneToInvokeStart; STDMETHODIMP CorHost2::Start() { CONTRACTL { NOTHROW; GC_TRIGGERS; ENTRY_POINT; }CONTRACTL_END; HRESULT hr; BEGIN_ENTRYPOINT_NOTHROW; // Ensure that only one thread at a time gets in here DangerousNonHostedSpinLockHolder lockHolder(&lockOnlyOneToInvokeStart); // To provide the complete semantic of Start/Stop in context of a given host, we check m_fStarted and let // them invoke the Start only if they have not already. Likewise, they can invoke the Stop method // only if they have invoked Start prior to that. // // This prevents a host from invoking Stop twice and hitting the refCount to zero, when another // host is using the CLR, as CLR instance sharing across hosts is a scenario for CoreCLR. if (g_fEEStarted) { hr = S_OK; // CoreCLR is already running - but was Start already invoked by this host? if (m_fStarted) { // This host had already invoked the Start method - return them an error hr = HOST_E_INVALIDOPERATION; } else { // Increment the global (and dynamic) refCount... FastInterlockIncrement(&m_RefCount); // And set our flag that this host has invoked the Start... m_fStarted = TRUE; } } else { // Using managed C++ libraries, its possible that when the runtime is already running, // MC++ will use CorBindToRuntimeEx to make callbacks into specific appdomain of its // choice. Now, CorBindToRuntimeEx results in CorHost2::CreateObject being invoked // that will set runtime hosted flag "g_fHostConfig |= CLRHOSTED". // // For the case when managed code started without CLR hosting and MC++ does a // CorBindToRuntimeEx, setting the CLR hosted flag is incorrect. // // Thus, before we attempt to start the runtime, we save the status of it being // already running or not. Next, if we are able to successfully start the runtime // and ONLY if it was not started earlier will we set the hosted flag below. if (!g_fEEStarted) { g_fHostConfig |= CLRHOSTED; } hr = CorRuntimeHostBase::Start(); if (SUCCEEDED(hr)) { // Set our flag that this host invoked the Start method. m_fStarted = TRUE; // And they also loaded the CoreCLR DLL in the memory (for this version). // This is a special flag as the host that has got this flag set will be allowed // to repeatedly invoke Stop method (without corresponding Start method invocations). // This is to support scenarios like that of Office where they need to bring down // the CLR at any cost. // // So, if you want to do that, just make sure you are the first host to load the // specific version of CLR in memory AND start it. m_fFirstToLoadCLR = TRUE; FastInterlockIncrement(&m_RefCount); } } END_ENTRYPOINT_NOTHROW; return hr; } // Starts the runtime. This is equivalent to CoInitializeEE(); HRESULT CorRuntimeHostBase::Start() { CONTRACTL { NOTHROW; DISABLED(GC_TRIGGERS); ENTRY_POINT; } CONTRACTL_END; HRESULT hr = S_OK; BEGIN_ENTRYPOINT_NOTHROW; { m_Started = TRUE; #ifdef FEATURE_EVENT_TRACE g_fEEHostedStartup = TRUE; #endif // FEATURE_EVENT_TRACE hr = InitializeEE(COINITEE_DEFAULT); } END_ENTRYPOINT_NOTHROW; return hr; } HRESULT CorHost2::Stop() { CONTRACTL { NOTHROW; ENTRY_POINT; // We're bringing the EE down, so no point in probing if (GetThread()) {GC_TRIGGERS;} else {DISABLED(GC_NOTRIGGER);} } CONTRACTL_END; if (!g_fEEStarted) { return E_UNEXPECTED; } HRESULT hr=S_OK; BEGIN_ENTRYPOINT_NOTHROW; // Is this host eligible to invoke the Stop method? if ((!m_fStarted) && (!m_fFirstToLoadCLR)) { // Well - since this host never invoked Start, it is not eligible to invoke Stop. // Semantically, for such a host, CLR is not available in the process. The only // exception to this condition is the host that first loaded this version of the // CLR and invoked Start method. For details, refer to comments in CorHost2::Start implementation. hr = HOST_E_CLRNOTAVAILABLE; } else { while (TRUE) { LONG refCount = m_RefCount; if (refCount == 0) { hr = HOST_E_CLRNOTAVAILABLE; break; } else if (FastInterlockCompareExchange(&m_RefCount, refCount - 1, refCount) == refCount) { // Indicate that we have got a Stop for a corresponding Start call from the // Host. Semantically, CoreCLR has stopped for them. m_fStarted = FALSE; if (refCount > 1) { hr=S_FALSE; break; } else { break; } } } } END_ENTRYPOINT_NOTHROW; return hr; } HRESULT CorHost2::GetCurrentAppDomainId(DWORD *pdwAppDomainId) { CONTRACTL { NOTHROW; GC_NOTRIGGER; ENTRY_POINT; } CONTRACTL_END; // No point going further if the runtime is not running... // We use CanRunManagedCode() instead of IsRuntimeActive() because this allows us // to specify test using the form that does not trigger a GC. if (!(g_fEEStarted && CanRunManagedCode(LoaderLockCheck::None))) { return HOST_E_CLRNOTAVAILABLE; } HRESULT hr = S_OK; BEGIN_ENTRYPOINT_NOTHROW; if(pdwAppDomainId == NULL) { hr = E_POINTER; } else { Thread *pThread = GetThread(); if (!pThread) { hr = E_UNEXPECTED; } else { *pdwAppDomainId = SystemDomain::GetCurrentDomain()->GetId().m_dwId; } } END_ENTRYPOINT_NOTHROW; return hr; } HRESULT CorHost2::ExecuteApplication(LPCWSTR pwzAppFullName, DWORD dwManifestPaths, LPCWSTR *ppwzManifestPaths, DWORD dwActivationData, LPCWSTR *ppwzActivationData, int *pReturnValue) { return E_NOTIMPL; } /* * This method processes the arguments sent to the host which are then used * to invoke the main method. * Note - * [0] - points to the assemblyName that has been sent by the host. * The rest are the arguments sent to the assembly. * Also note, this might not always return the exact same identity as the cmdLine * used to invoke the method. * * For example :- * ActualCmdLine - Foo arg1 arg2. * (Host1) - Full_path_to_Foo arg1 arg2 */ void SetCommandLineArgs(LPCWSTR pwzAssemblyPath, int argc, LPCWSTR* argv) { CONTRACTL { THROWS; GC_TRIGGERS; MODE_COOPERATIVE; } CONTRACTL_END; // Send the command line to EventPipe. #ifdef FEATURE_PERFTRACING EventPipe::SaveCommandLine(pwzAssemblyPath, argc, argv); #endif // FEATURE_PERFTRACING // Send the command line to System.Environment. struct _gc { PTRARRAYREF cmdLineArgs; } gc; ZeroMemory(&gc, sizeof(gc)); GCPROTECT_BEGIN(gc); gc.cmdLineArgs = (PTRARRAYREF)AllocateObjectArray(argc + 1 /* arg[0] should be the exe name*/, g_pStringClass); OBJECTREF orAssemblyPath = StringObject::NewString(pwzAssemblyPath); gc.cmdLineArgs->SetAt(0, orAssemblyPath); for (int i = 0; i < argc; ++i) { OBJECTREF argument = StringObject::NewString(argv[i]); gc.cmdLineArgs->SetAt(i + 1, argument); } MethodDescCallSite setCmdLineArgs(METHOD__ENVIRONMENT__SET_COMMAND_LINE_ARGS); ARG_SLOT args[] = { ObjToArgSlot(gc.cmdLineArgs), }; setCmdLineArgs.Call(args); GCPROTECT_END(); } HRESULT CorHost2::ExecuteAssembly(DWORD dwAppDomainId, LPCWSTR pwzAssemblyPath, int argc, LPCWSTR* argv, DWORD *pReturnValue) { CONTRACTL { THROWS; // Throws...as we do not want it to swallow the managed exception ENTRY_POINT; } CONTRACTL_END; // This is currently supported in default domain only if (dwAppDomainId != DefaultADID) return HOST_E_INVALIDOPERATION; // No point going further if the runtime is not running... if (!IsRuntimeActive()) { return HOST_E_CLRNOTAVAILABLE; } if(!pwzAssemblyPath) return E_POINTER; if(argc < 0) { return E_INVALIDARG; } if(argc > 0 && argv == NULL) { return E_INVALIDARG; } HRESULT hr = S_OK; AppDomain *pCurDomain = SystemDomain::GetCurrentDomain(); Thread *pThread = GetThread(); if (pThread == NULL) { pThread = SetupThreadNoThrow(&hr); if (pThread == NULL) { goto ErrExit; } } if(pCurDomain->GetId().m_dwId != DefaultADID) { return HOST_E_INVALIDOPERATION; } INSTALL_UNHANDLED_MANAGED_EXCEPTION_TRAP; INSTALL_UNWIND_AND_CONTINUE_HANDLER; _ASSERTE (!pThread->PreemptiveGCDisabled()); Assembly *pAssembly = AssemblySpec::LoadAssembly(pwzAssemblyPath); #if defined(FEATURE_MULTICOREJIT) pCurDomain->GetMulticoreJitManager().AutoStartProfile(pCurDomain); #endif // defined(FEATURE_MULTICOREJIT) { GCX_COOP(); // Here we call the managed method that gets the cmdLineArgs array. SetCommandLineArgs(pwzAssemblyPath, argc, argv); PTRARRAYREF arguments = NULL; GCPROTECT_BEGIN(arguments); arguments = (PTRARRAYREF)AllocateObjectArray(argc, g_pStringClass); for (int i = 0; i < argc; ++i) { STRINGREF argument = StringObject::NewString(argv[i]); arguments->SetAt(i, argument); } DWORD retval = pAssembly->ExecuteMainMethod(&arguments, TRUE /* waitForOtherThreads */); if (pReturnValue) { *pReturnValue = retval; } GCPROTECT_END(); } UNINSTALL_UNWIND_AND_CONTINUE_HANDLER; UNINSTALL_UNHANDLED_MANAGED_EXCEPTION_TRAP; ErrExit: return hr; } HRESULT CorHost2::ExecuteInDefaultAppDomain(LPCWSTR pwzAssemblyPath, LPCWSTR pwzTypeName, LPCWSTR pwzMethodName, LPCWSTR pwzArgument, DWORD *pReturnValue) { CONTRACTL { NOTHROW; ENTRY_POINT; } CONTRACTL_END; // No point going further if the runtime is not running... if (!IsRuntimeActive()) { return HOST_E_CLRNOTAVAILABLE; } if(! (pwzAssemblyPath && pwzTypeName && pwzMethodName) ) return E_POINTER; HRESULT hr = S_OK; BEGIN_ENTRYPOINT_NOTHROW; Thread *pThread = GetThread(); if (pThread == NULL) { pThread = SetupThreadNoThrow(&hr); if (pThread == NULL) { goto ErrExit; } } _ASSERTE (!pThread->PreemptiveGCDisabled()); _ASSERTE (SystemDomain::GetCurrentDomain()->GetId().m_dwId == DefaultADID); INSTALL_UNHANDLED_MANAGED_EXCEPTION_TRAP; INSTALL_UNWIND_AND_CONTINUE_HANDLER; EX_TRY { Assembly *pAssembly = AssemblySpec::LoadAssembly(pwzAssemblyPath); SString szTypeName(pwzTypeName); StackScratchBuffer buff1; const char* szTypeNameUTF8 = szTypeName.GetUTF8(buff1); MethodTable *pMT = ClassLoader::LoadTypeByNameThrowing(pAssembly, NULL, szTypeNameUTF8).AsMethodTable(); SString szMethodName(pwzMethodName); StackScratchBuffer buff; const char* szMethodNameUTF8 = szMethodName.GetUTF8(buff); MethodDesc *pMethodMD = MemberLoader::FindMethod(pMT, szMethodNameUTF8, &gsig_SM_Str_RetInt); if (!pMethodMD) { hr = COR_E_MISSINGMETHOD; } else { GCX_COOP(); MethodDescCallSite method(pMethodMD); STRINGREF sref = NULL; GCPROTECT_BEGIN(sref); if (pwzArgument) sref = StringObject::NewString(pwzArgument); ARG_SLOT MethodArgs[] = { ObjToArgSlot(sref) }; DWORD retval = method.Call_RetI4(MethodArgs); if (pReturnValue) { *pReturnValue = retval; } GCPROTECT_END(); } } EX_CATCH_HRESULT(hr); UNINSTALL_UNWIND_AND_CONTINUE_HANDLER; UNINSTALL_UNHANDLED_MANAGED_EXCEPTION_TRAP; ErrExit: END_ENTRYPOINT_NOTHROW; return hr; } HRESULT ExecuteInAppDomainHelper(FExecuteInAppDomainCallback pCallback, void * cookie) { STATIC_CONTRACT_THROWS; return pCallback(cookie); } HRESULT CorHost2::ExecuteInAppDomain(DWORD dwAppDomainId, FExecuteInAppDomainCallback pCallback, void * cookie) { // No point going further if the runtime is not running... if (!IsRuntimeActive()) { return HOST_E_CLRNOTAVAILABLE; } // Moved this here since no point validating the pointer // if the basic checks [above] fail if( pCallback == NULL) return E_POINTER; CONTRACTL { NOTHROW; if (GetThread()) {GC_TRIGGERS;} else {DISABLED(GC_NOTRIGGER);} ENTRY_POINT; // This is called by a host. } CONTRACTL_END; HRESULT hr = S_OK; BEGIN_ENTRYPOINT_NOTHROW; BEGIN_EXTERNAL_ENTRYPOINT(&hr); GCX_COOP_THREAD_EXISTS(GET_THREAD()); ENTER_DOMAIN_ID(ADID(dwAppDomainId)) { // We are calling an unmanaged function pointer, either an unmanaged function, or a marshaled out delegate. // The thread should be in preemptive mode. GCX_PREEMP(); hr=ExecuteInAppDomainHelper (pCallback, cookie); } END_DOMAIN_TRANSITION; END_EXTERNAL_ENTRYPOINT; END_ENTRYPOINT_NOTHROW; return hr; } #define EMPTY_STRING_TO_NULL(s) {if(s && s[0] == 0) {s=NULL;};} HRESULT CorHost2::_CreateAppDomain( LPCWSTR wszFriendlyName, DWORD dwFlags, LPCWSTR wszAppDomainManagerAssemblyName, LPCWSTR wszAppDomainManagerTypeName, int nProperties, LPCWSTR* pPropertyNames, LPCWSTR* pPropertyValues, DWORD* pAppDomainID) { CONTRACTL { NOTHROW; if (GetThread()) {GC_TRIGGERS;} else {DISABLED(GC_NOTRIGGER);} ENTRY_POINT; // This is called by a host. } CONTRACTL_END; HRESULT hr=S_OK; //cannot call the function more than once when single appDomain is allowed if (m_fAppDomainCreated) { return HOST_E_INVALIDOPERATION; } //normalize empty strings EMPTY_STRING_TO_NULL(wszFriendlyName); EMPTY_STRING_TO_NULL(wszAppDomainManagerAssemblyName); EMPTY_STRING_TO_NULL(wszAppDomainManagerTypeName); if (pAppDomainID==NULL) return E_POINTER; if (!m_fStarted) return HOST_E_INVALIDOPERATION; if (wszFriendlyName == NULL) return E_INVALIDARG; if ((wszAppDomainManagerAssemblyName != NULL) || (wszAppDomainManagerTypeName != NULL)) return E_INVALIDARG; BEGIN_ENTRYPOINT_NOTHROW; BEGIN_EXTERNAL_ENTRYPOINT(&hr); AppDomain* pDomain = SystemDomain::System()->DefaultDomain(); pDomain->SetFriendlyName(wszFriendlyName); ETW::LoaderLog::DomainLoad(pDomain, (LPWSTR)wszFriendlyName); if (dwFlags & APPDOMAIN_IGNORE_UNHANDLED_EXCEPTIONS) pDomain->SetIgnoreUnhandledExceptions(); if (dwFlags & APPDOMAIN_FORCE_TRIVIAL_WAIT_OPERATIONS) pDomain->SetForceTrivialWaitOperations(); pDomain->CreateFusionContext(); { GCX_COOP(); MethodDescCallSite setup(METHOD__APPCONTEXT__SETUP); ARG_SLOT args[3]; args[0] = PtrToArgSlot(pPropertyNames); args[1] = PtrToArgSlot(pPropertyValues); args[2] = PtrToArgSlot(nProperties); setup.Call(args); } LPCWSTR pwzNativeDllSearchDirectories = NULL; LPCWSTR pwzTrustedPlatformAssemblies = NULL; LPCWSTR pwzPlatformResourceRoots = NULL; LPCWSTR pwzAppPaths = NULL; LPCWSTR pwzAppNiPaths = NULL; #ifdef FEATURE_COMINTEROP LPCWSTR pwzAppLocalWinMD = NULL; #endif for (int i = 0; i < nProperties; i++) { if (wcscmp(pPropertyNames[i], W("NATIVE_DLL_SEARCH_DIRECTORIES")) == 0) { pwzNativeDllSearchDirectories = pPropertyValues[i]; } else if (wcscmp(pPropertyNames[i], W("TRUSTED_PLATFORM_ASSEMBLIES")) == 0) { pwzTrustedPlatformAssemblies = pPropertyValues[i]; } else if (wcscmp(pPropertyNames[i], W("PLATFORM_RESOURCE_ROOTS")) == 0) { pwzPlatformResourceRoots = pPropertyValues[i]; } else if (wcscmp(pPropertyNames[i], W("APP_PATHS")) == 0) { pwzAppPaths = pPropertyValues[i]; } else if (wcscmp(pPropertyNames[i], W("APP_NI_PATHS")) == 0) { pwzAppNiPaths = pPropertyValues[i]; } #ifdef FEATURE_COMINTEROP else if (wcscmp(pPropertyNames[i], W("APP_LOCAL_WINMETADATA")) == 0) { pwzAppLocalWinMD = pPropertyValues[i]; } #endif } pDomain->SetNativeDllSearchDirectories(pwzNativeDllSearchDirectories); { SString sTrustedPlatformAssemblies(pwzTrustedPlatformAssemblies); SString sPlatformResourceRoots(pwzPlatformResourceRoots); SString sAppPaths(pwzAppPaths); SString sAppNiPaths(pwzAppNiPaths); CLRPrivBinderCoreCLR *pBinder = pDomain->GetTPABinderContext(); _ASSERTE(pBinder != NULL); IfFailThrow(pBinder->SetupBindingPaths( sTrustedPlatformAssemblies, sPlatformResourceRoots, sAppPaths, sAppNiPaths)); } #ifdef FEATURE_COMINTEROP if (WinRTSupported()) { pDomain->SetWinrtApplicationContext(pwzAppLocalWinMD); } #endif *pAppDomainID=pDomain->GetId().m_dwId; m_fAppDomainCreated = TRUE; END_EXTERNAL_ENTRYPOINT; END_ENTRYPOINT_NOTHROW; return hr; } HRESULT CorHost2::_CreateDelegate( DWORD appDomainID, LPCWSTR wszAssemblyName, LPCWSTR wszClassName, LPCWSTR wszMethodName, INT_PTR* fnPtr) { CONTRACTL { NOTHROW; if (GetThread()) {GC_TRIGGERS;} else {DISABLED(GC_NOTRIGGER);} ENTRY_POINT; // This is called by a host. } CONTRACTL_END; HRESULT hr=S_OK; EMPTY_STRING_TO_NULL(wszAssemblyName); EMPTY_STRING_TO_NULL(wszClassName); EMPTY_STRING_TO_NULL(wszMethodName); if (fnPtr == NULL) return E_POINTER; *fnPtr = NULL; if(wszAssemblyName == NULL) return E_INVALIDARG; if(wszClassName == NULL) return E_INVALIDARG; if(wszMethodName == NULL) return E_INVALIDARG; BEGIN_ENTRYPOINT_NOTHROW; BEGIN_EXTERNAL_ENTRYPOINT(&hr); GCX_COOP_THREAD_EXISTS(GET_THREAD()); MAKE_UTF8PTR_FROMWIDE(szAssemblyName, wszAssemblyName); MAKE_UTF8PTR_FROMWIDE(szClassName, wszClassName); MAKE_UTF8PTR_FROMWIDE(szMethodName, wszMethodName); ADID id; id.m_dwId=appDomainID; ENTER_DOMAIN_ID(id) GCX_PREEMP(); AssemblySpec spec; spec.Init(szAssemblyName); Assembly* pAsm=spec.LoadAssembly(FILE_ACTIVE); TypeHandle th=pAsm->GetLoader()->LoadTypeByNameThrowing(pAsm,NULL,szClassName); MethodDesc* pMD=NULL; if (!th.IsTypeDesc()) { pMD = MemberLoader::FindMethodByName(th.GetMethodTable(), szMethodName, MemberLoader::FM_Unique); if (pMD == NULL) { // try again without the FM_Unique flag (error path) pMD = MemberLoader::FindMethodByName(th.GetMethodTable(), szMethodName, MemberLoader::FM_Default); if (pMD != NULL) { // the method exists but is overloaded ThrowHR(COR_E_AMBIGUOUSMATCH); } } } if (pMD==NULL || !pMD->IsStatic() || pMD->ContainsGenericVariables()) ThrowHR(COR_E_MISSINGMETHOD); UMEntryThunk *pUMEntryThunk = pMD->GetLoaderAllocator()->GetUMEntryThunkCache()->GetUMEntryThunk(pMD); *fnPtr = (INT_PTR)pUMEntryThunk->GetCode(); END_DOMAIN_TRANSITION; END_EXTERNAL_ENTRYPOINT; END_ENTRYPOINT_NOTHROW; return hr; } HRESULT CorHost2::CreateAppDomainWithManager( LPCWSTR wszFriendlyName, DWORD dwFlags, LPCWSTR wszAppDomainManagerAssemblyName, LPCWSTR wszAppDomainManagerTypeName, int nProperties, LPCWSTR* pPropertyNames, LPCWSTR* pPropertyValues, DWORD* pAppDomainID) { WRAPPER_NO_CONTRACT; return _CreateAppDomain( wszFriendlyName, dwFlags, wszAppDomainManagerAssemblyName, wszAppDomainManagerTypeName, nProperties, pPropertyNames, pPropertyValues, pAppDomainID); } HRESULT CorHost2::CreateDelegate( DWORD appDomainID, LPCWSTR wszAssemblyName, LPCWSTR wszClassName, LPCWSTR wszMethodName, INT_PTR* fnPtr) { WRAPPER_NO_CONTRACT; return _CreateDelegate(appDomainID, wszAssemblyName, wszClassName, wszMethodName, fnPtr); } HRESULT CorHost2::Authenticate(ULONGLONG authKey) { CONTRACTL { NOTHROW; if (GetThread()) {GC_TRIGGERS;} else {DISABLED(GC_NOTRIGGER);} ENTRY_POINT; // This is called by a host. } CONTRACTL_END; // Host authentication was used by Silverlight. It is no longer relevant for CoreCLR. return S_OK; } HRESULT CorHost2::RegisterMacEHPort() { CONTRACTL { NOTHROW; if (GetThread()) {GC_TRIGGERS;} else {DISABLED(GC_NOTRIGGER);} ENTRY_POINT; // This is called by a host. } CONTRACTL_END; return S_OK; } HRESULT CorHost2::SetStartupFlags(STARTUP_FLAGS flag) { CONTRACTL { NOTHROW; if (GetThread()) {GC_TRIGGERS;} else {DISABLED(GC_NOTRIGGER);} ENTRY_POINT; // This is called by a host. } CONTRACTL_END; if (g_fEEStarted) { return HOST_E_INVALIDOPERATION; } m_dwStartupFlags = flag; return S_OK; } #endif //!DACCESS_COMPILE #ifndef DACCESS_COMPILE SVAL_IMPL(STARTUP_FLAGS, CorHost2, m_dwStartupFlags = STARTUP_CONCURRENT_GC); #else SVAL_IMPL(STARTUP_FLAGS, CorHost2, m_dwStartupFlags); #endif STARTUP_FLAGS CorHost2::GetStartupFlags() { return m_dwStartupFlags; } #ifndef DACCESS_COMPILE #ifdef FEATURE_COMINTEROP // Enumerate currently existing domains. HRESULT CorRuntimeHostBase::EnumDomains(HDOMAINENUM *hEnum) { CONTRACTL { NOTHROW; MODE_PREEMPTIVE; WRAPPER(GC_TRIGGERS); ENTRY_POINT; } CONTRACTL_END; if(hEnum == NULL) return E_POINTER; // Thread setup happens in BEGIN_EXTERNAL_ENTRYPOINT below. // If the runtime has not started, we have nothing to do. if (!g_fEEStarted) { return HOST_E_CLRNOTAVAILABLE; } HRESULT hr = E_OUTOFMEMORY; *hEnum = NULL; BEGIN_ENTRYPOINT_NOTHROW; BEGIN_EXTERNAL_ENTRYPOINT(&hr) AppDomainIterator *pEnum = new (nothrow) AppDomainIterator(FALSE); if(pEnum) { *hEnum = (HDOMAINENUM) pEnum; hr = S_OK; } END_EXTERNAL_ENTRYPOINT; END_ENTRYPOINT_NOTHROW; return hr; } #endif // FEATURE_COMINTEROP extern "C" DLLEXPORT HRESULT GetCLRRuntimeHost(REFIID riid, IUnknown **ppUnk) { WRAPPER_NO_CONTRACT; return CorHost2::CreateObject(riid, (void**)ppUnk); } STDMETHODIMP CorHost2::UnloadAppDomain(DWORD dwDomainId, BOOL fWaitUntilDone) { return UnloadAppDomain2(dwDomainId, fWaitUntilDone, nullptr); } STDMETHODIMP CorHost2::UnloadAppDomain2(DWORD dwDomainId, BOOL fWaitUntilDone, int *pLatchedExitCode) { WRAPPER_NO_CONTRACT; if (!m_fStarted) return HOST_E_INVALIDOPERATION; if (!g_fEEStarted) { return HOST_E_CLRNOTAVAILABLE; } if(!m_fAppDomainCreated) { return HOST_E_INVALIDOPERATION; } HRESULT hr=S_OK; BEGIN_ENTRYPOINT_NOTHROW; if (!m_fFirstToLoadCLR) { _ASSERTE(!"Not reachable"); hr = HOST_E_CLRNOTAVAILABLE; } else { LONG refCount = m_RefCount; if (refCount == 0) { hr = HOST_E_CLRNOTAVAILABLE; } else if (1 == refCount) { // Stop coreclr on unload. EEShutDown(FALSE); } else { _ASSERTE(!"Not reachable"); hr = S_FALSE; } } END_ENTRYPOINT_NOTHROW; if (pLatchedExitCode) { *pLatchedExitCode = GetLatchedExitCode(); } return hr; } HRESULT CorRuntimeHostBase::UnloadAppDomain(DWORD dwDomainId, BOOL fWaitUntilDone) { return UnloadAppDomain2(dwDomainId, fWaitUntilDone, nullptr); } HRESULT CorRuntimeHostBase::UnloadAppDomain2(DWORD dwDomainId, BOOL fWaitUntilDone, int *pLatchedExitCode) { CONTRACTL { NOTHROW; GC_TRIGGERS; MODE_ANY; FORBID_FAULT; // Unloading domains cannot fail due to OOM ENTRY_POINT; } CONTRACTL_END; return COR_E_CANNOTUNLOADAPPDOMAIN; } //***************************************************************************** // Fiber Methods //***************************************************************************** HRESULT CorRuntimeHostBase::LocksHeldByLogicalThread(DWORD *pCount) { if (!pCount) return E_POINTER; CONTRACTL { NOTHROW; GC_NOTRIGGER; ENTRY_POINT; } CONTRACTL_END; BEGIN_ENTRYPOINT_NOTHROW; Thread* pThread = GetThread(); if (pThread == NULL) *pCount = 0; else *pCount = pThread->m_dwLockCount; END_ENTRYPOINT_NOTHROW; return S_OK; } //***************************************************************************** // ICorConfiguration //***************************************************************************** //***************************************************************************** // IUnknown //***************************************************************************** ULONG CorRuntimeHostBase::AddRef() { CONTRACTL { WRAPPER(THROWS); WRAPPER(GC_TRIGGERS); } CONTRACTL_END; return InterlockedIncrement(&m_cRef); } ULONG CorHost2::Release() { LIMITED_METHOD_CONTRACT; ULONG cRef = InterlockedDecrement(&m_cRef); if (!cRef) { delete this; } return (cRef); } HRESULT CorHost2::QueryInterface(REFIID riid, void **ppUnk) { if (!ppUnk) return E_POINTER; CONTRACTL { NOTHROW; GC_NOTRIGGER; } CONTRACTL_END; if (ppUnk == NULL) { return E_POINTER; } *ppUnk = 0; // Deliberately do NOT hand out ICorConfiguration. They must explicitly call // GetConfiguration to obtain that interface. if (riid == IID_IUnknown) { *ppUnk = static_cast(static_cast(this)); } else if (riid == IID_ICLRRuntimeHost) { *ppUnk = static_cast(this); } else if (riid == IID_ICLRRuntimeHost2) { ULONG version = 2; if (m_Version == 0) FastInterlockCompareExchange((LONG*)&m_Version, version, 0); *ppUnk = static_cast(this); } else if (riid == IID_ICLRRuntimeHost4) { ULONG version = 4; if (m_Version == 0) FastInterlockCompareExchange((LONG*)&m_Version, version, 0); *ppUnk = static_cast(this); } #ifndef FEATURE_PAL else if (riid == IID_IPrivateManagedExceptionReporting) { *ppUnk = static_cast(this); } #endif // !FEATURE_PAL else return (E_NOINTERFACE); AddRef(); return (S_OK); } #ifndef FEATURE_PAL HRESULT CorHost2::GetBucketParametersForCurrentException(BucketParameters *pParams) { CONTRACTL { NOTHROW; GC_NOTRIGGER; } CONTRACTL_END; HRESULT hr = S_OK; BEGIN_ENTRYPOINT_NOTHROW; // To avoid confusion, clear the buckets. memset(pParams, 0, sizeof(BucketParameters)); // Defer to Watson helper. hr = ::GetBucketParametersForCurrentException(pParams); END_ENTRYPOINT_NOTHROW; return hr; } #endif // !FEATURE_PAL HRESULT CorHost2::CreateObject(REFIID riid, void **ppUnk) { CONTRACTL { NOTHROW; GC_NOTRIGGER; } CONTRACTL_END; HRESULT hr = S_OK; CorHost2 *pCorHost = new (nothrow) CorHost2(); if (!pCorHost) { hr = E_OUTOFMEMORY; } else { hr = pCorHost->QueryInterface(riid, ppUnk); if (FAILED(hr)) delete pCorHost; } return (hr); } //----------------------------------------------------------------------------- // MapFile - Maps a file into the runtime in a non-standard way //----------------------------------------------------------------------------- static PEImage *MapFileHelper(HANDLE hFile) { CONTRACTL { THROWS; GC_TRIGGERS; MODE_ANY; } CONTRACTL_END; GCX_PREEMP(); HandleHolder hFileMap(WszCreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL)); if (hFileMap == NULL) ThrowLastError(); CLRMapViewHolder base(CLRMapViewOfFile(hFileMap, FILE_MAP_READ, 0, 0, 0)); if (base == NULL) ThrowLastError(); DWORD dwSize = SafeGetFileSize(hFile, NULL); if (dwSize == 0xffffffff && GetLastError() != NOERROR) { ThrowLastError(); } PEImageHolder pImage(PEImage::LoadFlat(base, dwSize)); return pImage.Extract(); } HRESULT CorRuntimeHostBase::MapFile(HANDLE hFile, HMODULE* phHandle) { CONTRACTL { NOTHROW; GC_TRIGGERS; MODE_PREEMPTIVE; ENTRY_POINT; } CONTRACTL_END; HRESULT hr; BEGIN_ENTRYPOINT_NOTHROW; BEGIN_EXTERNAL_ENTRYPOINT(&hr) { *phHandle = (HMODULE) (MapFileHelper(hFile)->GetLoadedLayout()->GetBase()); } END_EXTERNAL_ENTRYPOINT; END_ENTRYPOINT_NOTHROW; return hr; } /////////////////////////////////////////////////////////////////////////////// // IDebuggerInfo::IsDebuggerAttached LONG CorHost2::m_RefCount = 0; IHostControl *CorHost2::m_HostControl = NULL; #ifdef _DEBUG extern void ValidateHostInterface(); #endif static Volatile fOneOnly = 0; /////////////////////////////////////////////////////////////////////////////// // ICLRRuntimeHost::SetHostControl /////////////////////////////////////////////////////////////////////////////// HRESULT CorHost2::SetHostControl(IHostControl* pHostControl) { CONTRACTL { NOTHROW; GC_NOTRIGGER; ENTRY_POINT; } CONTRACTL_END; if (m_Version < 2) // CLR is hosted with v1 hosting interface. Some part of v2 hosting API are disabled. return HOST_E_INVALIDOPERATION; if (pHostControl == 0) return E_INVALIDARG; // If Runtime has been started, do not allow setting HostMemoryManager if (g_fEEStarted) return E_ACCESSDENIED; HRESULT hr = S_OK; BEGIN_ENTRYPOINT_NOTHROW; DWORD dwSwitchCount = 0; while (FastInterlockExchange((LONG*)&fOneOnly, 1) == 1) { __SwitchToThread(0, ++dwSwitchCount); } if (m_HostControl == NULL) { m_HostControl = pHostControl; m_HostControl->AddRef(); } goto ErrExit; ErrExit: fOneOnly = 0; END_ENTRYPOINT_NOTHROW; return hr; } class CCLRPolicyManager: public ICLRPolicyManager { public: virtual HRESULT STDMETHODCALLTYPE SetDefaultAction(EClrOperation operation, EPolicyAction action) { LIMITED_METHOD_CONTRACT; return E_NOTIMPL; } virtual HRESULT STDMETHODCALLTYPE SetTimeout(EClrOperation operation, DWORD dwMilliseconds) { LIMITED_METHOD_CONTRACT; return E_NOTIMPL; } virtual HRESULT STDMETHODCALLTYPE SetActionOnTimeout(EClrOperation operation, EPolicyAction action) { LIMITED_METHOD_CONTRACT; return E_NOTIMPL; } virtual HRESULT STDMETHODCALLTYPE SetTimeoutAndAction(EClrOperation operation, DWORD dwMilliseconds, EPolicyAction action) { LIMITED_METHOD_CONTRACT; return E_NOTIMPL; } virtual HRESULT STDMETHODCALLTYPE SetActionOnFailure(EClrFailure failure, EPolicyAction action) { // This is enabled for CoreCLR since a host can use this to // specify action for handling AV. STATIC_CONTRACT_ENTRY_POINT; LIMITED_METHOD_CONTRACT; HRESULT hr; // For CoreCLR, this method just supports FAIL_AccessViolation as a valid // failure input arg. The validation of the specified action for the failure // will be done in EEPolicy::IsValidActionForFailure. if (failure != FAIL_AccessViolation) { return E_INVALIDARG; } BEGIN_ENTRYPOINT_NOTHROW; hr = GetEEPolicy()->SetActionOnFailure(failure,action); END_ENTRYPOINT_NOTHROW; return hr; } virtual HRESULT STDMETHODCALLTYPE SetUnhandledExceptionPolicy(EClrUnhandledException policy) { LIMITED_METHOD_CONTRACT; return E_NOTIMPL; } virtual ULONG STDMETHODCALLTYPE AddRef(void) { LIMITED_METHOD_CONTRACT; return 1; } virtual ULONG STDMETHODCALLTYPE Release(void) { LIMITED_METHOD_CONTRACT; return 1; } BEGIN_INTERFACE HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject) { LIMITED_METHOD_CONTRACT; if (riid != IID_ICLRPolicyManager && riid != IID_IUnknown) return (E_NOINTERFACE); // Ensure that the out going pointer is not null if (ppvObject == NULL) return E_POINTER; *ppvObject = this; return S_OK; } }; static CCLRPolicyManager s_PolicyManager; void ProcessEventForHost(EClrEvent event, void *data) { } // We do not call ProcessEventForHost for stack overflow, since we have limit stack // and we should avoid calling GCX_PREEMPT void ProcessSOEventForHost(EXCEPTION_POINTERS *pExceptionInfo, BOOL fInSoTolerant) { } BOOL IsHostRegisteredForEvent(EClrEvent event) { WRAPPER_NO_CONTRACT; return FALSE; } inline size_t SizeInKBytes(size_t cbSize) { LIMITED_METHOD_CONTRACT; size_t cb = (cbSize % 1024) ? 1 : 0; return ((cbSize / 1024) + cb); } SIZE_T Host_SegmentSize = 0; SIZE_T Host_MaxGen0Size = 0; BOOL Host_fSegmentSizeSet = FALSE; BOOL Host_fMaxGen0SizeSet = FALSE; void UpdateGCSettingFromHost () { WRAPPER_NO_CONTRACT; _ASSERTE (g_pConfig); if (Host_fSegmentSizeSet) { g_pConfig->SetSegmentSize(Host_SegmentSize); } if (Host_fMaxGen0SizeSet) { g_pConfig->SetGCgen0size(Host_MaxGen0Size); } } #if defined(FEATURE_WINDOWSPHONE) class CCLRGCManager: public ICLRGCManager2 { public: virtual HRESULT STDMETHODCALLTYPE Collect(LONG Generation) { CONTRACTL { NOTHROW; GC_TRIGGERS; ENTRY_POINT; } CONTRACTL_END; HRESULT hr = S_OK; if (Generation > (int) GCHeapUtilities::GetGCHeap()->GetMaxGeneration()) hr = E_INVALIDARG; if (SUCCEEDED(hr)) { // Set up a Thread object if this is called on a native thread. Thread *pThread; pThread = GetThread(); if (pThread == NULL) pThread = SetupThreadNoThrow(&hr); if (pThread != NULL) { BEGIN_ENTRYPOINT_NOTHROW_WITH_THREAD(pThread); GCX_COOP(); EX_TRY { STRESS_LOG0(LF_GC, LL_INFO100, "Host triggers GC\n"); hr = GCHeapUtilities::GetGCHeap()->GarbageCollect(Generation); } EX_CATCH { hr = GET_EXCEPTION()->GetHR(); } EX_END_CATCH(SwallowAllExceptions); END_ENTRYPOINT_NOTHROW_WITH_THREAD; } } return (hr); } virtual HRESULT STDMETHODCALLTYPE GetStats(COR_GC_STATS *pStats) { CONTRACTL { NOTHROW; GC_NOTRIGGER; ENTRY_POINT; } CONTRACTL_END; return E_NOTIMPL; } virtual HRESULT STDMETHODCALLTYPE SetGCStartupLimits( DWORD SegmentSize, DWORD MaxGen0Size) { CONTRACTL { NOTHROW; GC_NOTRIGGER; ENTRY_POINT; } CONTRACTL_END; HRESULT hr = S_OK; BEGIN_ENTRYPOINT_NOTHROW; // Set default overrides if specified by caller. if (SegmentSize != (DWORD) ~0 && SegmentSize > 0) { hr = _SetGCSegmentSize(SegmentSize); } if (SUCCEEDED(hr) && MaxGen0Size != (DWORD) ~0 && MaxGen0Size > 0) { hr = _SetGCMaxGen0Size(MaxGen0Size); } END_ENTRYPOINT_NOTHROW; return (hr); } virtual HRESULT STDMETHODCALLTYPE SetGCStartupLimitsEx( SIZE_T SegmentSize, SIZE_T MaxGen0Size) { CONTRACTL { NOTHROW; GC_NOTRIGGER; ENTRY_POINT; } CONTRACTL_END; HRESULT hr = S_OK; BEGIN_ENTRYPOINT_NOTHROW; // Set default overrides if specified by caller. if (SegmentSize != (SIZE_T) ~0 && SegmentSize > 0) { hr = _SetGCSegmentSize(SegmentSize); } if (SUCCEEDED(hr) && MaxGen0Size != (SIZE_T) ~0 && MaxGen0Size > 0) { hr = _SetGCMaxGen0Size(MaxGen0Size); } END_ENTRYPOINT_NOTHROW; return (hr); } virtual ULONG STDMETHODCALLTYPE AddRef(void) { LIMITED_METHOD_CONTRACT; return 1; } virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, OUT PVOID *ppUnk) { LIMITED_METHOD_CONTRACT; if (riid != IID_ICLRGCManager && riid != IID_ICLRGCManager2 && riid != IID_IUnknown) return (E_NOINTERFACE); *ppUnk = this; return S_OK; } virtual ULONG STDMETHODCALLTYPE Release(void) { LIMITED_METHOD_CONTRACT; return 1; } private: HRESULT _SetGCSegmentSize(SIZE_T SegmentSize); HRESULT _SetGCMaxGen0Size(SIZE_T MaxGen0Size); }; HRESULT CCLRGCManager::_SetGCSegmentSize(SIZE_T SegmentSize) { CONTRACTL { NOTHROW; GC_NOTRIGGER; } CONTRACTL_END; HRESULT hr = S_OK; // Sanity check the value, it must be a power of two and big enough. if (!GCHeapUtilities::GetGCHeap()->IsValidSegmentSize(SegmentSize)) { hr = E_INVALIDARG; } else { Host_SegmentSize = SegmentSize; Host_fSegmentSizeSet = TRUE; } return (hr); } HRESULT CCLRGCManager::_SetGCMaxGen0Size(SIZE_T MaxGen0Size) { CONTRACTL { NOTHROW; GC_NOTRIGGER; } CONTRACTL_END; HRESULT hr = S_OK; // Sanity check the value is at least large enough. if (!GCHeapUtilities::GetGCHeap()->IsValidGen0MaxSize(MaxGen0Size)) { hr = E_INVALIDARG; } else { Host_MaxGen0Size = MaxGen0Size; Host_fMaxGen0SizeSet = TRUE; } return (hr); } static CCLRGCManager s_GCManager; #endif //FEATURE_WINDOWSPHONE #ifdef FEATURE_APPDOMAIN_RESOURCE_MONITORING class CCLRAppDomainResourceMonitor : public ICLRAppDomainResourceMonitor { public: virtual HRESULT STDMETHODCALLTYPE GetCurrentAllocated(DWORD dwAppDomainId, ULONGLONG* pBytesAllocated) { CONTRACTL { NOTHROW; GC_NOTRIGGER; ENTRY_POINT; } CONTRACTL_END; HRESULT hr = S_OK; BEGIN_ENTRYPOINT_NOTHROW; SystemDomain::LockHolder lh; AppDomain* pAppDomain = SystemDomain::GetAppDomainFromId((ADID)dwAppDomainId, ADV_CURRENTAD); if (pBytesAllocated) { *pBytesAllocated = pAppDomain->GetAllocBytes(); } END_ENTRYPOINT_NOTHROW; return (hr); } virtual HRESULT STDMETHODCALLTYPE GetCurrentSurvived(DWORD dwAppDomainId, ULONGLONG* pAppDomainBytesSurvived, ULONGLONG* pTotalBytesSurvived) { CONTRACTL { NOTHROW; GC_NOTRIGGER; ENTRY_POINT; } CONTRACTL_END; HRESULT hr = S_OK; BEGIN_ENTRYPOINT_NOTHROW; SystemDomain::LockHolder lh; AppDomain* pAppDomain = SystemDomain::GetAppDomainFromId((ADID)dwAppDomainId, ADV_CURRENTAD); if (pAppDomainBytesSurvived) { *pAppDomainBytesSurvived = pAppDomain->GetSurvivedBytes(); } if (pTotalBytesSurvived) { *pTotalBytesSurvived = SystemDomain::GetTotalSurvivedBytes(); } END_ENTRYPOINT_NOTHROW; return (hr); } virtual HRESULT STDMETHODCALLTYPE GetCurrentCpuTime(DWORD dwAppDomainId, ULONGLONG* pMilliseconds) { CONTRACTL { NOTHROW; GC_TRIGGERS; ENTRY_POINT; } CONTRACTL_END; HRESULT hr = S_OK; BEGIN_ENTRYPOINT_NOTHROW; { SystemDomain::LockHolder lh; AppDomain* pAppDomain = SystemDomain::GetAppDomainFromId((ADID)dwAppDomainId, ADV_CURRENTAD); if (pMilliseconds) { *pMilliseconds = pAppDomain->QueryProcessorUsage() / 10000; } } END_ENTRYPOINT_NOTHROW; return hr; } virtual ULONG STDMETHODCALLTYPE AddRef(void) { LIMITED_METHOD_CONTRACT; return 1; } virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, OUT PVOID *ppUnk) { LIMITED_METHOD_CONTRACT; *ppUnk = NULL; if (riid == IID_IUnknown) *ppUnk = (IUnknown*)this; else if (riid == IID_ICLRAppDomainResourceMonitor) *ppUnk = (ICLRAppDomainResourceMonitor*)this; else return E_NOINTERFACE; return S_OK; } virtual ULONG STDMETHODCALLTYPE Release(void) { LIMITED_METHOD_CONTRACT; return 1; } }; static CCLRAppDomainResourceMonitor s_Arm; #endif //FEATURE_APPDOMAIN_RESOURCE_MONITORING BOOL g_CLRPolicyRequested = FALSE; class CCorCLRControl: public ICLRControl { public: virtual HRESULT STDMETHODCALLTYPE GetCLRManager(REFIID riid, void **ppObject) { CONTRACTL { NOTHROW; GC_NOTRIGGER; } CONTRACTL_END; // Sanity check. if (ppObject == NULL) return E_INVALIDARG; #if defined(FEATURE_WINDOWSPHONE) if (riid == IID_ICLRErrorReportingManager2) { *ppObject = &g_CLRErrorReportingManager; return S_OK; } else #endif //defined(FEATURE_WINDOWSPHONE) if (g_fEEStarted && !m_fFullAccess) { // If runtime has been started, do not allow user to obtain CLR managers. return HOST_E_INVALIDOPERATION; } // CoreCLR supports ICLRPolicyManager since it allows the host // to specify the policy for AccessViolation. else if (riid == IID_ICLRPolicyManager) { *ppObject = &s_PolicyManager; FastInterlockExchange((LONG*)&g_CLRPolicyRequested, TRUE); return S_OK; } #if defined(FEATURE_WINDOWSPHONE) else if ((riid == IID_ICLRGCManager) || (riid == IID_ICLRGCManager2)) { *ppObject = &s_GCManager; return S_OK; } #endif //FEATURE_WINDOWSPHONE #ifdef FEATURE_APPDOMAIN_RESOURCE_MONITORING else if (riid == IID_ICLRAppDomainResourceMonitor) { EnableARM(); *ppObject = &s_Arm; return S_OK; } #endif //FEATURE_APPDOMAIN_RESOURCE_MONITORING else return (E_NOINTERFACE); } virtual HRESULT STDMETHODCALLTYPE SetAppDomainManagerType( LPCWSTR pwzAppDomainManagerAssembly, LPCWSTR pwzAppDomainManagerType) { // CoreCLR does not support this method return E_NOTIMPL; } virtual ULONG STDMETHODCALLTYPE AddRef(void) { LIMITED_METHOD_CONTRACT; return 1; } virtual ULONG STDMETHODCALLTYPE Release(void) { LIMITED_METHOD_CONTRACT; return 1; } BEGIN_INTERFACE HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject) { LIMITED_METHOD_CONTRACT; if (riid != IID_ICLRControl && riid != IID_IUnknown) return (E_NOINTERFACE); // Ensure that the out going pointer is not null if (ppvObject == NULL) return E_POINTER; *ppvObject = this; return S_OK; } // This is to avoid having ctor. We have static objects, and it is // difficult to support ctor on certain platform. void SetAccess(BOOL fFullAccess) { LIMITED_METHOD_CONTRACT; m_fFullAccess = fFullAccess; } private: BOOL m_fFullAccess; }; // Before CLR starts, we give out s_CorCLRControl which has full access to all managers. // After CLR starts, we give out s_CorCLRControlLimited which allows limited access to managers. static CCorCLRControl s_CorCLRControl; /////////////////////////////////////////////////////////////////////////////// // ICLRRuntimeHost::GetCLRControl HRESULT CorHost2::GetCLRControl(ICLRControl** pCLRControl) { LIMITED_METHOD_CONTRACT; // Ensure that we have a valid pointer if (pCLRControl == NULL) { return E_POINTER; } HRESULT hr = S_OK; STATIC_CONTRACT_ENTRY_POINT; BEGIN_ENTRYPOINT_NOTHROW; if (!g_fEEStarted && m_Version >= 2) { s_CorCLRControl.SetAccess(TRUE); *pCLRControl = &s_CorCLRControl; } else { // If : // 1) request comes for interface other than ICLRControl*, OR // 2) runtime has already started, OR // 3) version is not 2 // // we will return failure and set the out pointer to NULL *pCLRControl = NULL; if (g_fEEStarted) { // Return HOST_E_INVALIDOPERATION as per MSDN if runtime has already started hr = HOST_E_INVALIDOPERATION; } else { hr = E_NOTIMPL; } } END_ENTRYPOINT_NOTHROW; return hr; } // static EInitializeNewDomainFlags CorHost2::GetAppDomainManagerInitializeNewDomainFlags() { LIMITED_METHOD_CONTRACT; return eInitializeNewDomainFlags_None; } #endif // !DAC #ifdef DACCESS_COMPILE #endif //DACCESS_COMPILE #ifndef DACCESS_COMPILE #if defined(FEATURE_WINDOWSPHONE) HRESULT CCLRErrorReportingManager::QueryInterface(REFIID riid, void** ppUnk) { if (!ppUnk) return E_POINTER; CONTRACTL { NOTHROW; GC_NOTRIGGER; } CONTRACTL_END; HRESULT hr = S_OK; if (ppUnk == NULL) { return E_POINTER; } *ppUnk = 0; // Deliberately do NOT hand out ICorConfiguration. They must explicitly call // GetConfiguration to obtain that interface. if (riid == IID_IUnknown) { *ppUnk = (IUnknown *) this; } else if (riid == IID_ICLRErrorReportingManager) { *ppUnk = (ICLRErrorReportingManager *) this; } #ifdef FEATURE_WINDOWSPHONE else if (riid == IID_ICLRErrorReportingManager2) { *ppUnk = (ICLRErrorReportingManager2 *) this; } #endif // FEATURE_WINDOWSPHONE else { hr = E_NOINTERFACE; } return hr; } // HRESULT CCLRErrorReportingManager::QueryInterface() ULONG CCLRErrorReportingManager::AddRef() { LIMITED_METHOD_CONTRACT; return 1; } // HRESULT CCLRErrorReportingManager::AddRef() ULONG CCLRErrorReportingManager::Release() { LIMITED_METHOD_CONTRACT; return 1; } // HRESULT CCLRErrorReportingManager::Release() // Get Watson bucket parameters for "current" exception (on calling thread). HRESULT CCLRErrorReportingManager::GetBucketParametersForCurrentException( BucketParameters *pParams) { CONTRACTL { WRAPPER(THROWS); WRAPPER(GC_TRIGGERS); ENTRY_POINT; } CONTRACTL_END; HRESULT hr = S_OK; BEGIN_ENTRYPOINT_NOTHROW; // To avoid confusion, clear the buckets. memset(pParams, 0, sizeof(BucketParameters)); #ifndef FEATURE_PAL // Defer to Watson helper. hr = ::GetBucketParametersForCurrentException(pParams); #else // Watson doesn't exist on non-windows platforms hr = E_NOTIMPL; #endif // !FEATURE_PAL END_ENTRYPOINT_NOTHROW; return hr; } // HRESULT CCLRErrorReportingManager::GetBucketParametersForCurrentException() // // The BeginCustomDump function configures the custom dump support // // Parameters - // dwFlavor - The flavor of the dump // dwNumItems - The number of items in the CustomDumpItem array. // Should always be zero today, since no custom items are defined // items - Array of CustomDumpItem structs specifying items to be added to the dump. // Should always be NULL today, since no custom items are defined. // dwReserved - reserved for future use. Must be zero today // HRESULT CCLRErrorReportingManager::BeginCustomDump( ECustomDumpFlavor dwFlavor, DWORD dwNumItems, CustomDumpItem items[], DWORD dwReserved) { STATIC_CONTRACT_ENTRY_POINT; HRESULT hr = S_OK; BEGIN_ENTRYPOINT_NOTHROW; if (dwNumItems != 0 || items != NULL || dwReserved != 0) { IfFailGo(E_INVALIDARG); } if (g_ECustomDumpFlavor != DUMP_FLAVOR_Default) { // BeginCustomDump is called without matching EndCustomDump IfFailGo(E_INVALIDARG); } g_ECustomDumpFlavor = dwFlavor; ErrExit: END_ENTRYPOINT_NOTHROW; return hr; } // // EndCustomDump clears the custom dump configuration // HRESULT CCLRErrorReportingManager::EndCustomDump() { STATIC_CONTRACT_ENTRY_POINT; // NOT IMPLEMENTED YET BEGIN_ENTRYPOINT_NOTHROW; g_ECustomDumpFlavor = DUMP_FLAVOR_Default; END_ENTRYPOINT_NOTHROW; return S_OK; } #ifdef FEATURE_WINDOWSPHONE HRESULT CopyStringWorker(_Out_ WCHAR** pTarget, WCHAR const* pSource) { LIMITED_METHOD_CONTRACT; if (pTarget == NULL || pSource == NULL) return E_INVALIDARG; if (*pTarget) delete[] (*pTarget); // allocate space for the data plus one wchar for NULL size_t sourceLen = wcslen(pSource); *pTarget = new (nothrow) WCHAR[sourceLen + 1]; if (!(*pTarget)) return E_OUTOFMEMORY; errno_t result = wcsncpy_s(*pTarget, sourceLen + 1, pSource, sourceLen); _ASSERTE(result == 0); if (result != 0) { delete[] (*pTarget); *pTarget = NULL; return E_FAIL; } return S_OK; } CCLRErrorReportingManager::BucketParamsCache::BucketParamsCache(DWORD maxNumParams) : m_pParams(NULL), m_cMaxParams(maxNumParams) { LIMITED_METHOD_CONTRACT; } CCLRErrorReportingManager::BucketParamsCache::~BucketParamsCache() { LIMITED_METHOD_CONTRACT; if (m_pParams) { for (DWORD i = 0; i < m_cMaxParams; ++i) if (m_pParams[i]) delete[] m_pParams[i]; } } WCHAR const* CCLRErrorReportingManager::BucketParamsCache::GetAt(BucketParameterIndex index) { LIMITED_METHOD_CONTRACT; if (index >= InvalidBucketParamIndex) { _ASSERTE(!"bad bucket parameter index"); return NULL; } if (!m_pParams) return NULL; return m_pParams[index]; } HRESULT CCLRErrorReportingManager::BucketParamsCache::SetAt(BucketParameterIndex index, WCHAR const* val) { LIMITED_METHOD_CONTRACT; if (index < 0 || index >= InvalidBucketParamIndex) { _ASSERTE(!"bad bucket parameter index"); return E_INVALIDARG; } if (!val) return E_INVALIDARG; if (!m_pParams) { m_pParams = new (nothrow) WCHAR*[m_cMaxParams]; if (!m_pParams) return E_OUTOFMEMORY; for (DWORD i = 0; i < m_cMaxParams; ++i) m_pParams[i] = NULL; } return CopyStringWorker(&m_pParams[index], val); } HRESULT CCLRErrorReportingManager::CopyToDataCache(_Out_ WCHAR** pTarget, WCHAR const* pSource) { LIMITED_METHOD_CONTRACT; return CopyStringWorker(pTarget, pSource); } HRESULT CCLRErrorReportingManager::SetApplicationData(ApplicationDataKey key, WCHAR const* pValue) { STATIC_CONTRACT_ENTRY_POINT; BEGIN_ENTRYPOINT_NOTHROW; if(g_fEEStarted) return HOST_E_INVALIDOPERATION; if (pValue == NULL || wcslen(pValue) > MAX_LONGPATH) return E_INVALIDARG; HRESULT hr = S_OK; switch (key) { case ApplicationID: hr = CopyToDataCache(&m_pApplicationId, pValue); break; case InstanceID: hr = CopyToDataCache(&m_pInstanceId, pValue); break; default: hr = E_INVALIDARG; } END_ENTRYPOINT_NOTHROW; return hr; } HRESULT CCLRErrorReportingManager::SetBucketParametersForUnhandledException(BucketParameters const* pBucketParams, DWORD* pCountParams) { STATIC_CONTRACT_ENTRY_POINT; BEGIN_ENTRYPOINT_NOTHROW; if(g_fEEStarted) return HOST_E_INVALIDOPERATION; if (pBucketParams == NULL || pCountParams == NULL || pBucketParams->fInited != TRUE) return E_INVALIDARG; *pCountParams = 0; if (!m_pBucketParamsCache) { m_pBucketParamsCache = new (nothrow) BucketParamsCache(InvalidBucketParamIndex); if (!m_pBucketParamsCache) return E_OUTOFMEMORY; } HRESULT hr = S_OK; bool hasOverride = false; for (DWORD i = 0; i < InvalidBucketParamIndex; ++i) { if (pBucketParams->pszParams[i][0] != W('\0')) { hasOverride = true; hr = m_pBucketParamsCache->SetAt(static_cast(i), pBucketParams->pszParams[i]); if (SUCCEEDED(hr)) *pCountParams += 1; else break; } } if (!hasOverride) return E_INVALIDARG; END_ENTRYPOINT_NOTHROW; return hr; } WCHAR const* CCLRErrorReportingManager::GetApplicationData(ApplicationDataKey key) { LIMITED_METHOD_CONTRACT; WCHAR* pValue = NULL; switch (key) { case ApplicationID: pValue = m_pApplicationId; break; case InstanceID: pValue = m_pInstanceId; break; default: _ASSERTE(!"invalid key specified"); } return pValue; } WCHAR const* CCLRErrorReportingManager::GetBucketParamOverride(BucketParameterIndex bucketParamId) { LIMITED_METHOD_CONTRACT; if (!m_pBucketParamsCache) return NULL; return m_pBucketParamsCache->GetAt(bucketParamId); } #endif // FEATURE_WINDOWSPHONE CCLRErrorReportingManager::CCLRErrorReportingManager() { LIMITED_METHOD_CONTRACT; #ifdef FEATURE_WINDOWSPHONE m_pApplicationId = NULL; m_pInstanceId = NULL; m_pBucketParamsCache = NULL; #endif } CCLRErrorReportingManager::~CCLRErrorReportingManager() { LIMITED_METHOD_CONTRACT; #ifdef FEATURE_WINDOWSPHONE if (m_pApplicationId) delete[] m_pApplicationId; if (m_pInstanceId) delete[] m_pInstanceId; if (m_pBucketParamsCache) delete m_pBucketParamsCache; #endif } #endif // defined(FEATURE_WINDOWSPHONE) void GetProcessMemoryLoad(LPMEMORYSTATUSEX pMSEX) { CONTRACTL { NOTHROW; GC_NOTRIGGER; } CONTRACTL_END; pMSEX->dwLength = sizeof(MEMORYSTATUSEX); BOOL fRet = GlobalMemoryStatusEx(pMSEX); _ASSERTE (fRet); } // This is the instance that exposes interfaces out to all the other DLLs of the CLR // so they can use our services for TLS, synchronization, memory allocation, etc. static BYTE g_CEEInstance[sizeof(CExecutionEngine)]; static Volatile g_pCEE = NULL; PTLS_CALLBACK_FUNCTION CExecutionEngine::Callbacks[MAX_PREDEFINED_TLS_SLOT]; extern "C" IExecutionEngine * __stdcall IEE() { LIMITED_METHOD_CONTRACT; // Unfortunately,we can't probe here. The probing system requires the // use of TLS, and in order to initialize TLS we need to call IEE. //BEGIN_ENTRYPOINT_VOIDRET; // The following code does NOT contain a race condition. The following code is BY DESIGN. // The issue is that we can have two separate threads inside this if statement, both of which are // initializing the g_CEEInstance variable (and subsequently updating g_pCEE). This works fine, // and will not cause an inconsistent state due to the fact that CExecutionEngine has no // local variables. If multiple threads make it inside this if statement, it will copy the same // bytes over g_CEEInstance and there will not be a time when there is an inconsistent state. if ( !g_pCEE ) { // Create a local copy on the stack and then copy it over to the static instance. // This avoids race conditions caused by multiple initializations of vtable in the constructor CExecutionEngine local; memcpy(&g_CEEInstance, (void*)&local, sizeof(CExecutionEngine)); g_pCEE = (IExecutionEngine*)(CExecutionEngine*)&g_CEEInstance; } //END_ENTRYPOINT_VOIDRET; return g_pCEE; } HRESULT STDMETHODCALLTYPE CExecutionEngine::QueryInterface(REFIID id, void **pInterface) { LIMITED_METHOD_CONTRACT; if (!pInterface) return E_POINTER; *pInterface = NULL; //CANNOTTHROWCOMPLUSEXCEPTION(); if (id == IID_IExecutionEngine) *pInterface = (IExecutionEngine *)this; else if (id == IID_IEEMemoryManager) *pInterface = (IEEMemoryManager *)this; else if (id == IID_IUnknown) *pInterface = (IUnknown *)(IExecutionEngine *)this; else return E_NOINTERFACE; AddRef(); return S_OK; } // HRESULT STDMETHODCALLTYPE CExecutionEngine::QueryInterface() ULONG STDMETHODCALLTYPE CExecutionEngine::AddRef() { LIMITED_METHOD_CONTRACT; return 1; } ULONG STDMETHODCALLTYPE CExecutionEngine::Release() { LIMITED_METHOD_CONTRACT; return 1; } struct ClrTlsInfo { void* data[MAX_PREDEFINED_TLS_SLOT]; }; #define DataToClrTlsInfo(a) ((ClrTlsInfo*)a) void** CExecutionEngine::GetTlsData() { LIMITED_METHOD_CONTRACT; return gCurrentThreadInfo.m_EETlsData; } BOOL CExecutionEngine::SetTlsData (void** ppTlsInfo) { LIMITED_METHOD_CONTRACT; gCurrentThreadInfo.m_EETlsData = ppTlsInfo; return TRUE; } //--------------------------------------------------------------------------------------- // // Returns the current logical thread's data block (ClrTlsInfo::data). // // Arguments: // slot - Index of the slot that is about to be requested // force - If the data block does not exist yet, create it as a side-effect // // Return Value: // NULL, if the data block did not exist yet for the current thread and force was FALSE. // A pointer to the data block, otherwise. // // Notes: // If the underlying OS does not support fiber mode, the data block is stored in TLS. // If the underlying OS does support fiber mode, it is primarily stored in FLS, // and cached in TLS so that we can use our generated optimized TLS accessors. // // TLS support for the other DLLs of the CLR operates quite differently in hosted // and unhosted scenarios. void **CExecutionEngine::CheckThreadState(DWORD slot, BOOL force) { STATIC_CONTRACT_GC_NOTRIGGER; STATIC_CONTRACT_THROWS; STATIC_CONTRACT_MODE_ANY; STATIC_CONTRACT_CANNOT_TAKE_LOCK; // @TODO: Decide on an exception strategy for all the DLLs of the CLR, and then // enable all the exceptions out of this method. // Treat as a runtime assertion, since the invariant spans many DLLs. _ASSERTE(slot < MAX_PREDEFINED_TLS_SLOT); // if (slot >= MAX_PREDEFINED_TLS_SLOT) // COMPlusThrow(kArgumentOutOfRangeException); void** pTlsData = CExecutionEngine::GetTlsData(); BOOL fInTls = (pTlsData != NULL); ClrTlsInfo *pTlsInfo = DataToClrTlsInfo(pTlsData); if (pTlsInfo == 0 && force) { #undef HeapAlloc #undef GetProcessHeap // !!! Contract uses our TLS support. Contract may be used before our host support is set up. // !!! To better support contract, we call into OS for memory allocation. pTlsInfo = (ClrTlsInfo*) ::HeapAlloc(GetProcessHeap(),0,sizeof(ClrTlsInfo)); #define GetProcessHeap() Dont_Use_GetProcessHeap() #define HeapAlloc(hHeap, dwFlags, dwBytes) Dont_Use_HeapAlloc(hHeap, dwFlags, dwBytes) if (pTlsInfo == NULL) { goto LError; } memset (pTlsInfo, 0, sizeof(ClrTlsInfo)); } if (!fInTls && pTlsInfo) { if (!CExecutionEngine::SetTlsData(pTlsInfo->data)) { goto LError; } } return pTlsInfo?pTlsInfo->data:NULL; LError: if (pTlsInfo) { #undef HeapFree #undef GetProcessHeap ::HeapFree(GetProcessHeap(), 0, pTlsInfo); #define GetProcessHeap() Dont_Use_GetProcessHeap() #define HeapFree(hHeap, dwFlags, lpMem) Dont_Use_HeapFree(hHeap, dwFlags, lpMem) } // If this is for the stack probe, and we failed to allocate memory for it, we won't // put in a guard page. if (slot == TlsIdx_ClrDebugState) return NULL; ThrowOutOfMemory(); } void **CExecutionEngine::CheckThreadStateNoCreate(DWORD slot #ifdef _DEBUG , BOOL fForDestruction #endif // _DEBUG ) { STATIC_CONTRACT_GC_NOTRIGGER; STATIC_CONTRACT_NOTHROW; STATIC_CONTRACT_MODE_ANY; // !!! This function is called during Thread::SwitchIn and SwitchOut // !!! It is extremely important that while executing this function, we will not // !!! cause fiber switch. This means we can not allocate memory, lock, etc... // Treat as a runtime assertion, since the invariant spans many DLLs. _ASSERTE(slot < MAX_PREDEFINED_TLS_SLOT); void **pTlsData = CExecutionEngine::GetTlsData(); ClrTlsInfo *pTlsInfo = DataToClrTlsInfo(pTlsData); return pTlsInfo?pTlsInfo->data:NULL; } // Note: Sampling profilers also use this function to initialize TLS for a unmanaged // sampling thread so that initialization can be done in advance to avoid deadlocks. // See ProfToEEInterfaceImpl::InitializeCurrentThread for more details. void CExecutionEngine::SetupTLSForThread(Thread *pThread) { STATIC_CONTRACT_THROWS; STATIC_CONTRACT_GC_NOTRIGGER; STATIC_CONTRACT_MODE_ANY; #ifdef STRESS_LOG if (StressLog::StressLogOn(~0u, 0)) { StressLog::CreateThreadStressLog(); } #endif void **pTlsData; pTlsData = CheckThreadState(0); PREFIX_ASSUME(pTlsData != NULL); #ifdef ENABLE_CONTRACTS // Profilers need the side effect of GetClrDebugState() to perform initialization // in advance to avoid deadlocks. Refer to ProfToEEInterfaceImpl::InitializeCurrentThread ClrDebugState *pDebugState = ::GetClrDebugState(); if (pThread) pThread->m_pClrDebugState = pDebugState; #endif } static void ThreadDetachingHelper(PTLS_CALLBACK_FUNCTION callback, void* pData) { // Do not use contract. We are freeing TLS blocks. STATIC_CONTRACT_NOTHROW; STATIC_CONTRACT_GC_NOTRIGGER; STATIC_CONTRACT_MODE_ANY; callback(pData); } // Called here from a thread detach or from destruction of a Thread object. In // the detach case, we get our info from TLS. In the destruct case, it comes from // the object we are destructing. void CExecutionEngine::ThreadDetaching(void ** pTlsData) { // Can not cause memory allocation during thread detach, so no real contracts. STATIC_CONTRACT_NOTHROW; STATIC_CONTRACT_GC_NOTRIGGER; STATIC_CONTRACT_MODE_ANY; // This function may be called twice: // 1. When a physical thread dies, our DLL_THREAD_DETACH calls this function with pTlsData = NULL // 2. When a fiber is destroyed, or OS calls FlsCallback after DLL_THREAD_DETACH process. // We will null the FLS and TLS entry if it matches the deleted one. if (pTlsData) { DeleteTLS (pTlsData); } } void CExecutionEngine::DeleteTLS(void ** pTlsData) { // Can not cause memory allocation during thread detach, so no real contracts. STATIC_CONTRACT_NOTHROW; STATIC_CONTRACT_GC_NOTRIGGER; STATIC_CONTRACT_MODE_ANY; if (CExecutionEngine::GetTlsData() == NULL) { // We have not allocated TlsData yet. return; } PREFIX_ASSUME(pTlsData != NULL); ClrTlsInfo *pTlsInfo = DataToClrTlsInfo(pTlsData); BOOL fNeed; do { fNeed = FALSE; for (int i=0; idata[i] != 0) { void* pData = pTlsInfo->data[i]; pTlsInfo->data[i] = 0; ThreadDetachingHelper(Callbacks[i], pData); fNeed = TRUE; } } } while (fNeed); if (pTlsInfo->data[TlsIdx_StressLog] != 0) { #ifdef STRESS_LOG StressLog::ThreadDetach((ThreadStressLog *)pTlsInfo->data[TlsIdx_StressLog]); #else _ASSERTE (!"should not have StressLog"); #endif } if (Callbacks[TlsIdx_ClrDebugState] != 0 && pTlsInfo->data[TlsIdx_ClrDebugState] != 0) { void* pData = pTlsInfo->data[TlsIdx_ClrDebugState]; pTlsInfo->data[TlsIdx_ClrDebugState] = 0; ThreadDetachingHelper(Callbacks[TlsIdx_ClrDebugState], pData); } // NULL TLS and FLS entry so that we don't double free. // We may get two callback here on thread death // 1. From EEDllMain // 2. From OS callback on FLS destruction if (CExecutionEngine::GetTlsData() == pTlsData) { CExecutionEngine::SetTlsData(0); } #undef HeapFree #undef GetProcessHeap ::HeapFree (GetProcessHeap(),0,pTlsInfo); #define HeapFree(hHeap, dwFlags, lpMem) Dont_Use_HeapFree(hHeap, dwFlags, lpMem) #define GetProcessHeap() Dont_Use_GetProcessHeap() } #ifdef ENABLE_CONTRACTS_IMPL // Fls callback to deallocate ClrDebugState when our FLS block goes away. void FreeClrDebugState(LPVOID pTlsData); #endif VOID STDMETHODCALLTYPE CExecutionEngine::TLS_AssociateCallback(DWORD slot, PTLS_CALLBACK_FUNCTION callback) { WRAPPER_NO_CONTRACT; CheckThreadState(slot); // They can toggle between a callback and no callback. But anything else looks like // confusion on their part. // // (TlsIdx_ClrDebugState associates its callback from utilcode.lib - which can be replicated. But // all the callbacks are equally good.) _ASSERTE(slot == TlsIdx_ClrDebugState || Callbacks[slot] == 0 || Callbacks[slot] == callback || callback == 0); if (slot == TlsIdx_ClrDebugState) { #ifdef ENABLE_CONTRACTS_IMPL // ClrDebugState is shared among many dlls. Some dll, like perfcounter.dll, may be unloaded. // We force the callback function to be in mscorwks.dll. Callbacks[slot] = FreeClrDebugState; #else _ASSERTE (!"should not get here"); #endif } else Callbacks[slot] = callback; } LPVOID* STDMETHODCALLTYPE CExecutionEngine::TLS_GetDataBlock() { STATIC_CONTRACT_GC_NOTRIGGER; STATIC_CONTRACT_THROWS; STATIC_CONTRACT_MODE_ANY; STATIC_CONTRACT_CANNOT_TAKE_LOCK; return CExecutionEngine::GetTlsData(); } LPVOID STDMETHODCALLTYPE CExecutionEngine::TLS_GetValue(DWORD slot) { WRAPPER_NO_CONTRACT; return EETlsGetValue(slot); } BOOL STDMETHODCALLTYPE CExecutionEngine::TLS_CheckValue(DWORD slot, LPVOID * pValue) { WRAPPER_NO_CONTRACT; return EETlsCheckValue(slot, pValue); } VOID STDMETHODCALLTYPE CExecutionEngine::TLS_SetValue(DWORD slot, LPVOID pData) { WRAPPER_NO_CONTRACT; EETlsSetValue(slot,pData); } VOID STDMETHODCALLTYPE CExecutionEngine::TLS_ThreadDetaching() { WRAPPER_NO_CONTRACT; CExecutionEngine::ThreadDetaching(NULL); } CRITSEC_COOKIE STDMETHODCALLTYPE CExecutionEngine::CreateLock(LPCSTR szTag, LPCSTR level, CrstFlags flags) { CONTRACTL { NOTHROW; GC_NOTRIGGER; MODE_ANY; ENTRY_POINT; } CONTRACTL_END; CRITSEC_COOKIE cookie = NULL; BEGIN_ENTRYPOINT_VOIDRET; cookie = ::EECreateCriticalSection(*(CrstType*)&level, flags); END_ENTRYPOINT_VOIDRET; return cookie; } void STDMETHODCALLTYPE CExecutionEngine::DestroyLock(CRITSEC_COOKIE cookie) { WRAPPER_NO_CONTRACT; ::EEDeleteCriticalSection(cookie); } void STDMETHODCALLTYPE CExecutionEngine::AcquireLock(CRITSEC_COOKIE cookie) { WRAPPER_NO_CONTRACT; ::EEEnterCriticalSection(cookie); } void STDMETHODCALLTYPE CExecutionEngine::ReleaseLock(CRITSEC_COOKIE cookie) { WRAPPER_NO_CONTRACT; ::EELeaveCriticalSection(cookie); } // Locking routines supplied by the EE to the other DLLs of the CLR. In a _DEBUG // build of the EE, we poison the Crst as a poor man's attempt to do some argument // validation. #define POISON_BITS 3 static inline EVENT_COOKIE CLREventToCookie(CLREvent * pEvent) { LIMITED_METHOD_CONTRACT; _ASSERTE((((uintptr_t) pEvent) & POISON_BITS) == 0); #ifdef _DEBUG pEvent = (CLREvent *) (((uintptr_t) pEvent) | POISON_BITS); #endif return (EVENT_COOKIE) pEvent; } static inline CLREvent *CookieToCLREvent(EVENT_COOKIE cookie) { LIMITED_METHOD_CONTRACT; _ASSERTE((((uintptr_t) cookie) & POISON_BITS) == POISON_BITS); #ifdef _DEBUG if (cookie) { cookie = (EVENT_COOKIE) (((uintptr_t) cookie) & ~POISON_BITS); } #endif return (CLREvent *) cookie; } EVENT_COOKIE STDMETHODCALLTYPE CExecutionEngine::CreateAutoEvent(BOOL bInitialState) { CONTRACTL { THROWS; MODE_ANY; GC_NOTRIGGER; ENTRY_POINT; } CONTRACTL_END; EVENT_COOKIE event = NULL; BEGIN_ENTRYPOINT_THROWS; NewHolder pEvent(new CLREvent()); pEvent->CreateAutoEvent(bInitialState); event = CLREventToCookie(pEvent); pEvent.SuppressRelease(); END_ENTRYPOINT_THROWS; return event; } EVENT_COOKIE STDMETHODCALLTYPE CExecutionEngine::CreateManualEvent(BOOL bInitialState) { CONTRACTL { THROWS; MODE_ANY; GC_NOTRIGGER; ENTRY_POINT; } CONTRACTL_END; EVENT_COOKIE event = NULL; BEGIN_ENTRYPOINT_THROWS; NewHolder pEvent(new CLREvent()); pEvent->CreateManualEvent(bInitialState); event = CLREventToCookie(pEvent); pEvent.SuppressRelease(); END_ENTRYPOINT_THROWS; return event; } void STDMETHODCALLTYPE CExecutionEngine::CloseEvent(EVENT_COOKIE event) { WRAPPER_NO_CONTRACT; if (event) { CLREvent *pEvent = CookieToCLREvent(event); pEvent->CloseEvent(); delete pEvent; } } BOOL STDMETHODCALLTYPE CExecutionEngine::ClrSetEvent(EVENT_COOKIE event) { CONTRACTL { NOTHROW; GC_NOTRIGGER; MODE_ANY; } CONTRACTL_END; if (event) { CLREvent *pEvent = CookieToCLREvent(event); return pEvent->Set(); } return FALSE; } BOOL STDMETHODCALLTYPE CExecutionEngine::ClrResetEvent(EVENT_COOKIE event) { CONTRACTL { NOTHROW; GC_NOTRIGGER; MODE_ANY; } CONTRACTL_END; if (event) { CLREvent *pEvent = CookieToCLREvent(event); return pEvent->Reset(); } return FALSE; } DWORD STDMETHODCALLTYPE CExecutionEngine::WaitForEvent(EVENT_COOKIE event, DWORD dwMilliseconds, BOOL bAlertable) { WRAPPER_NO_CONTRACT; if (event) { CLREvent *pEvent = CookieToCLREvent(event); return pEvent->Wait(dwMilliseconds,bAlertable); } if (GetThread() && bAlertable) ThrowHR(E_INVALIDARG); return WAIT_FAILED; } DWORD STDMETHODCALLTYPE CExecutionEngine::WaitForSingleObject(HANDLE handle, DWORD dwMilliseconds) { STATIC_CONTRACT_WRAPPER; return ::WaitForSingleObject(handle,dwMilliseconds); } static inline SEMAPHORE_COOKIE CLRSemaphoreToCookie(CLRSemaphore * pSemaphore) { LIMITED_METHOD_CONTRACT; _ASSERTE((((uintptr_t) pSemaphore) & POISON_BITS) == 0); #ifdef _DEBUG pSemaphore = (CLRSemaphore *) (((uintptr_t) pSemaphore) | POISON_BITS); #endif return (SEMAPHORE_COOKIE) pSemaphore; } static inline CLRSemaphore *CookieToCLRSemaphore(SEMAPHORE_COOKIE cookie) { LIMITED_METHOD_CONTRACT; _ASSERTE((((uintptr_t) cookie) & POISON_BITS) == POISON_BITS); #ifdef _DEBUG if (cookie) { cookie = (SEMAPHORE_COOKIE) (((uintptr_t) cookie) & ~POISON_BITS); } #endif return (CLRSemaphore *) cookie; } SEMAPHORE_COOKIE STDMETHODCALLTYPE CExecutionEngine::ClrCreateSemaphore(DWORD dwInitial, DWORD dwMax) { CONTRACTL { THROWS; MODE_ANY; GC_NOTRIGGER; } CONTRACTL_END; NewHolder pSemaphore(new CLRSemaphore()); pSemaphore->Create(dwInitial, dwMax); SEMAPHORE_COOKIE ret = CLRSemaphoreToCookie(pSemaphore);; pSemaphore.SuppressRelease(); return ret; } void STDMETHODCALLTYPE CExecutionEngine::ClrCloseSemaphore(SEMAPHORE_COOKIE semaphore) { CONTRACTL { NOTHROW; GC_NOTRIGGER; MODE_ANY; } CONTRACTL_END; CLRSemaphore *pSemaphore = CookieToCLRSemaphore(semaphore); pSemaphore->Close(); delete pSemaphore; } BOOL STDMETHODCALLTYPE CExecutionEngine::ClrReleaseSemaphore(SEMAPHORE_COOKIE semaphore, LONG lReleaseCount, LONG *lpPreviousCount) { CONTRACTL { NOTHROW; GC_NOTRIGGER; MODE_ANY; } CONTRACTL_END; CLRSemaphore *pSemaphore = CookieToCLRSemaphore(semaphore); return pSemaphore->Release(lReleaseCount,lpPreviousCount); } DWORD STDMETHODCALLTYPE CExecutionEngine::ClrWaitForSemaphore(SEMAPHORE_COOKIE semaphore, DWORD dwMilliseconds, BOOL bAlertable) { WRAPPER_NO_CONTRACT; CLRSemaphore *pSemaphore = CookieToCLRSemaphore(semaphore); return pSemaphore->Wait(dwMilliseconds,bAlertable); } static inline MUTEX_COOKIE CLRMutexToCookie(CLRMutex * pMutex) { LIMITED_METHOD_CONTRACT; _ASSERTE((((uintptr_t) pMutex) & POISON_BITS) == 0); #ifdef _DEBUG pMutex = (CLRMutex *) (((uintptr_t) pMutex) | POISON_BITS); #endif return (MUTEX_COOKIE) pMutex; } static inline CLRMutex *CookieToCLRMutex(MUTEX_COOKIE cookie) { LIMITED_METHOD_CONTRACT; _ASSERTE((((uintptr_t) cookie) & POISON_BITS) == POISON_BITS); #ifdef _DEBUG if (cookie) { cookie = (MUTEX_COOKIE) (((uintptr_t) cookie) & ~POISON_BITS); } #endif return (CLRMutex *) cookie; } MUTEX_COOKIE STDMETHODCALLTYPE CExecutionEngine::ClrCreateMutex(LPSECURITY_ATTRIBUTES lpMutexAttributes, BOOL bInitialOwner, LPCTSTR lpName) { CONTRACTL { NOTHROW; MODE_ANY; GC_NOTRIGGER; } CONTRACTL_END; MUTEX_COOKIE mutex = 0; CLRMutex *pMutex = new (nothrow) CLRMutex(); if (pMutex) { EX_TRY { pMutex->Create(lpMutexAttributes, bInitialOwner, lpName); mutex = CLRMutexToCookie(pMutex); } EX_CATCH { delete pMutex; } EX_END_CATCH(SwallowAllExceptions); } return mutex; } void STDMETHODCALLTYPE CExecutionEngine::ClrCloseMutex(MUTEX_COOKIE mutex) { CONTRACTL { NOTHROW; GC_NOTRIGGER; MODE_ANY; } CONTRACTL_END; CLRMutex *pMutex = CookieToCLRMutex(mutex); pMutex->Close(); delete pMutex; } BOOL STDMETHODCALLTYPE CExecutionEngine::ClrReleaseMutex(MUTEX_COOKIE mutex) { CONTRACTL { NOTHROW; GC_NOTRIGGER; MODE_ANY; } CONTRACTL_END; CLRMutex *pMutex = CookieToCLRMutex(mutex); return pMutex->Release(); } DWORD STDMETHODCALLTYPE CExecutionEngine::ClrWaitForMutex(MUTEX_COOKIE mutex, DWORD dwMilliseconds, BOOL bAlertable) { CONTRACTL { NOTHROW; GC_NOTRIGGER; MODE_ANY; } CONTRACTL_END; CLRMutex *pMutex = CookieToCLRMutex(mutex); return pMutex->Wait(dwMilliseconds,bAlertable); } #undef ClrSleepEx DWORD STDMETHODCALLTYPE CExecutionEngine::ClrSleepEx(DWORD dwMilliseconds, BOOL bAlertable) { WRAPPER_NO_CONTRACT; return EESleepEx(dwMilliseconds,bAlertable); } #define ClrSleepEx EESleepEx #undef ClrAllocationDisallowed BOOL STDMETHODCALLTYPE CExecutionEngine::ClrAllocationDisallowed() { WRAPPER_NO_CONTRACT; return EEAllocationDisallowed(); } #define ClrAllocationDisallowed EEAllocationDisallowed #undef ClrVirtualAlloc LPVOID STDMETHODCALLTYPE CExecutionEngine::ClrVirtualAlloc(LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect) { WRAPPER_NO_CONTRACT; return EEVirtualAlloc(lpAddress, dwSize, flAllocationType, flProtect); } #define ClrVirtualAlloc EEVirtualAlloc #undef ClrVirtualFree BOOL STDMETHODCALLTYPE CExecutionEngine::ClrVirtualFree(LPVOID lpAddress, SIZE_T dwSize, DWORD dwFreeType) { WRAPPER_NO_CONTRACT; return EEVirtualFree(lpAddress, dwSize, dwFreeType); } #define ClrVirtualFree EEVirtualFree #undef ClrVirtualQuery SIZE_T STDMETHODCALLTYPE CExecutionEngine::ClrVirtualQuery(LPCVOID lpAddress, PMEMORY_BASIC_INFORMATION lpBuffer, SIZE_T dwLength) { WRAPPER_NO_CONTRACT; return EEVirtualQuery(lpAddress, lpBuffer, dwLength); } #define ClrVirtualQuery EEVirtualQuery #if defined(_DEBUG) && !defined(FEATURE_PAL) static VolatilePtr s_pStartOfUEFSection = NULL; static VolatilePtr s_pEndOfUEFSectionBoundary = NULL; static Volatile s_dwProtection = 0; #endif // _DEBUG && !FEATURE_PAL #undef ClrVirtualProtect BOOL STDMETHODCALLTYPE CExecutionEngine::ClrVirtualProtect(LPVOID lpAddress, SIZE_T dwSize, DWORD flNewProtect, PDWORD lpflOldProtect) { WRAPPER_NO_CONTRACT; // Get the UEF installation details - we will use these to validate // that the calls to ClrVirtualProtect are not going to affect the UEF. // // The OS UEF invocation mechanism was updated. When a UEF is setup,the OS captures // the following details about it: // 1) Protection of the pages in which the UEF lives // 2) The size of the region in which the UEF lives // 3) The region's Allocation Base // // The OS verifies details surrounding the UEF before invocation. For security reasons // the page protection cannot change between SetUnhandledExceptionFilter and invocation. // // Prior to this change, the UEF lived in a common section of code_Seg, along with // JIT_PatchedCode. Thus, their pages have the same protection, they live // in the same region (and thus, its size is the same). // // In EEStartupHelper, when we setup the UEF and then invoke InitJitHelpers1 and InitJitHelpers2, // they perform some optimizations that result in the memory page protection being changed. When // the UEF is to be invoked, the OS does the check on the UEF's cached details against the current // memory pages. This check used to fail when on 64bit retail builds when JIT_PatchedCode was // aligned after the UEF with a different memory page protection (post the optimizations by InitJitHelpers). // Thus, the UEF was never invoked. // // To circumvent this, we put the UEF in its own section in the code segment so that any modifications // to memory pages will not affect the UEF details that the OS cached. This is done in Excep.cpp // using the "#pragma code_seg" directives. // // Below, we double check that: // // 1) the address being protected does not lie in the region of of the UEF. // 2) the section after UEF is not having the same memory protection as UEF section. // // We assert if either of the two conditions above are true. #if defined(_DEBUG) && !defined(FEATURE_PAL) // We do this check in debug/checked builds only // Do we have the UEF details? if (s_pEndOfUEFSectionBoundary.Load() == NULL) { // Get reference to MSCORWKS image in memory... PEDecoder pe(g_pMSCorEE); // Find the UEF section from the image IMAGE_SECTION_HEADER* pUEFSection = pe.FindSection(CLR_UEF_SECTION_NAME); _ASSERTE(pUEFSection != NULL); if (pUEFSection) { // We got our section - get the start of the section BYTE* pStartOfUEFSection = static_cast(pe.GetBase())+pUEFSection->VirtualAddress; s_pStartOfUEFSection = pStartOfUEFSection; // Now we need the protection attributes for the memory region in which the // UEF section is... MEMORY_BASIC_INFORMATION uefInfo; if (ClrVirtualQuery(pStartOfUEFSection, &uefInfo, sizeof(uefInfo)) != 0) { // Calculate how many pages does the UEF section take to get to the start of the // next section. We dont calculate this as // // pStartOfUEFSection + uefInfo.RegionSize // // because the section following UEF will also be included in the region size // if it has the same protection as the UEF section. DWORD dwUEFSectionPageCount = ((pUEFSection->Misc.VirtualSize + GetOsPageSize() - 1)/GetOsPageSize()); BYTE* pAddressOfFollowingSection = pStartOfUEFSection + (GetOsPageSize() * dwUEFSectionPageCount); // Ensure that the section following us is having different memory protection MEMORY_BASIC_INFORMATION nextSectionInfo; _ASSERTE(ClrVirtualQuery(pAddressOfFollowingSection, &nextSectionInfo, sizeof(nextSectionInfo)) != 0); _ASSERTE(nextSectionInfo.Protect != uefInfo.Protect); // save the memory protection details s_dwProtection = uefInfo.Protect; // Get the end of the UEF section BYTE* pEndOfUEFSectionBoundary = pAddressOfFollowingSection - 1; // Set the end of UEF section boundary FastInterlockExchangePointer(s_pEndOfUEFSectionBoundary.GetPointer(), pEndOfUEFSectionBoundary); } else { _ASSERTE(!"Unable to get UEF Details!"); } } } if (s_pEndOfUEFSectionBoundary.Load() != NULL) { // Is the protection being changed? if (flNewProtect != s_dwProtection) { // Is the target address NOT affecting the UEF ? Possible cases: // 1) Starts and ends before the UEF start // 2) Starts after the UEF start void* pEndOfRangeAddr = static_cast(lpAddress)+dwSize-1; _ASSERTE_MSG(((pEndOfRangeAddr < s_pStartOfUEFSection.Load()) || (lpAddress > s_pEndOfUEFSectionBoundary.Load())), "Do not virtual protect the section in which UEF lives!"); } } #endif // _DEBUG && !FEATURE_PAL return EEVirtualProtect(lpAddress, dwSize, flNewProtect, lpflOldProtect); } #define ClrVirtualProtect EEVirtualProtect #undef ClrGetProcessHeap HANDLE STDMETHODCALLTYPE CExecutionEngine::ClrGetProcessHeap() { WRAPPER_NO_CONTRACT; return EEGetProcessHeap(); } #define ClrGetProcessHeap EEGetProcessHeap #undef ClrGetProcessExecutableHeap HANDLE STDMETHODCALLTYPE CExecutionEngine::ClrGetProcessExecutableHeap() { WRAPPER_NO_CONTRACT; return EEGetProcessExecutableHeap(); } #define ClrGetProcessExecutableHeap EEGetProcessExecutableHeap #undef ClrHeapCreate HANDLE STDMETHODCALLTYPE CExecutionEngine::ClrHeapCreate(DWORD flOptions, SIZE_T dwInitialSize, SIZE_T dwMaximumSize) { WRAPPER_NO_CONTRACT; return EEHeapCreate(flOptions, dwInitialSize, dwMaximumSize); } #define ClrHeapCreate EEHeapCreate #undef ClrHeapDestroy BOOL STDMETHODCALLTYPE CExecutionEngine::ClrHeapDestroy(HANDLE hHeap) { WRAPPER_NO_CONTRACT; return EEHeapDestroy(hHeap); } #define ClrHeapDestroy EEHeapDestroy #undef ClrHeapAlloc LPVOID STDMETHODCALLTYPE CExecutionEngine::ClrHeapAlloc(HANDLE hHeap, DWORD dwFlags, SIZE_T dwBytes) { WRAPPER_NO_CONTRACT; return EEHeapAlloc(hHeap, dwFlags, dwBytes); } #define ClrHeapAlloc EEHeapAlloc #undef ClrHeapFree BOOL STDMETHODCALLTYPE CExecutionEngine::ClrHeapFree(HANDLE hHeap, DWORD dwFlags, LPVOID lpMem) { WRAPPER_NO_CONTRACT; return EEHeapFree(hHeap, dwFlags, lpMem); } #define ClrHeapFree EEHeapFree #undef ClrHeapValidate BOOL STDMETHODCALLTYPE CExecutionEngine::ClrHeapValidate(HANDLE hHeap, DWORD dwFlags, LPCVOID lpMem) { WRAPPER_NO_CONTRACT; return EEHeapValidate(hHeap, dwFlags, lpMem); } #define ClrHeapValidate EEHeapValidate //------------------------------------------------------------------------------ // Helper function to get an exception object from outside the exception. In // the CLR, it may be from the Thread object. Non-CLR users have no thread object, // and it will do nothing. void CExecutionEngine::GetLastThrownObjectExceptionFromThread(void **ppvException) { WRAPPER_NO_CONTRACT; // Cast to our real type. Exception **ppException = reinterpret_cast(ppvException); // Try to get a better message. GetLastThrownObjectExceptionFromThread_Internal(ppException); } // HRESULT CExecutionEngine::GetLastThrownObjectExceptionFromThread() LocaleID RuntimeGetFileSystemLocale() { return PEImage::GetFileSystemLocale(); }; HRESULT CorHost2::DllGetActivationFactory(DWORD appDomainID, LPCWSTR wszTypeName, IActivationFactory ** factory) { #ifdef FEATURE_COMINTEROP_WINRT_MANAGED_ACTIVATION // WinRT activation currently supported in default domain only if (appDomainID != DefaultADID) return HOST_E_INVALIDOPERATION; HRESULT hr = S_OK; Thread *pThread = GetThread(); if (pThread == NULL) { pThread = SetupThreadNoThrow(&hr); if (pThread == NULL) { return hr; } } if(SystemDomain::GetCurrentDomain()->GetId().m_dwId != DefaultADID) { return HOST_E_INVALIDOPERATION; } return DllGetActivationFactoryImpl(NULL, wszTypeName, NULL, factory); #else return E_NOTIMPL; #endif } #ifdef FEATURE_COMINTEROP_WINRT_MANAGED_ACTIVATION HRESULT STDMETHODCALLTYPE DllGetActivationFactoryImpl(LPCWSTR wszAssemblyName, LPCWSTR wszTypeName, LPCWSTR wszCodeBase, IActivationFactory ** factory) { CONTRACTL { DISABLED(NOTHROW); GC_TRIGGERS; MODE_ANY; ENTRY_POINT; } CONTRACTL_END; HRESULT hr = S_OK; BEGIN_ENTRYPOINT_NOTHROW; AppDomain* pDomain = SystemDomain::System()->DefaultDomain(); _ASSERTE(pDomain); BEGIN_EXTERNAL_ENTRYPOINT(&hr); { GCX_COOP(); bool bIsPrimitive; TypeHandle typeHandle = WinRTTypeNameConverter::GetManagedTypeFromWinRTTypeName(wszTypeName, &bIsPrimitive); if (!bIsPrimitive && !typeHandle.IsNull() && !typeHandle.IsTypeDesc() && typeHandle.AsMethodTable()->IsExportedToWinRT()) { struct _gc { OBJECTREF type; } gc; memset(&gc, 0, sizeof(gc)); IActivationFactory* activationFactory; GCPROTECT_BEGIN(gc); gc.type = typeHandle.GetManagedClassObject(); MethodDescCallSite mdcs(METHOD__WINDOWSRUNTIMEMARSHAL__GET_ACTIVATION_FACTORY_FOR_TYPE); ARG_SLOT args[1] = { ObjToArgSlot(gc.type) }; activationFactory = (IActivationFactory*)mdcs.Call_RetLPVOID(args); *factory = activationFactory; GCPROTECT_END(); } else { hr = COR_E_TYPELOAD; } } END_EXTERNAL_ENTRYPOINT; END_ENTRYPOINT_NOTHROW; return hr; } #endif // !FEATURE_COMINTEROP_MANAGED_ACTIVATION #endif // !DACCESS_COMPILE