Permalink
Cannot retrieve contributors at this time
2333 lines (1928 sloc)
63.8 KB
Name already in use
A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
runtime/src/coreclr/src/vm/corhost.cpp
Go to fileThis commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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 TARGET_UNIX | |
#include "dwreport.h" | |
#endif // !TARGET_UNIX | |
#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 TARGET_UNIX | |
EXTERN_C UINT32 _tls_index; | |
#else // TARGET_UNIX | |
UINT32 _tls_index = 0; | |
#endif // TARGET_UNIX | |
#ifndef DACCESS_COMPILE | |
extern void STDMETHODCALLTYPE EEShutDown(BOOL fIsDllUnloading); | |
extern void PrintToStdOutA(const char *pszString); | |
extern void PrintToStdOutW(const WCHAR *pwzString); | |
extern BOOL g_fEEHostedStartup; | |
//*************************************************************************** | |
ULONG CorRuntimeHostBase::m_Version = 0; | |
#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... | |
if (!IsRuntimeActive()) | |
{ | |
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 = DefaultADID; | |
} | |
} | |
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; | |
// Record the command line. | |
SaveManagedCommandLine(pwzAssemblyPath, argc, argv); | |
// 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; | |
} | |
} | |
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); | |
} | |
if(CLRConfig::GetConfigValue(CLRConfig::INTERNAL_Corhost_Swallow_Uncaught_Exceptions)) | |
{ | |
EX_TRY | |
DWORD retval = pAssembly->ExecuteMainMethod(&arguments, TRUE /* waitForOtherThreads */); | |
if (pReturnValue) | |
{ | |
*pReturnValue = retval; | |
} | |
EX_CATCH_HRESULT (hr) | |
} | |
else | |
{ | |
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()); | |
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; | |
// This is currently supported in default domain only | |
if (dwAppDomainId != DefaultADID) | |
return HOST_E_INVALIDOPERATION; | |
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()); | |
// 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_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->CreateBinderContext(); | |
{ | |
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]; | |
} | |
else | |
if (wcscmp(pPropertyNames[i], W("DEFAULT_STACK_SIZE")) == 0) | |
{ | |
extern void ParseDefaultStackSize(LPCWSTR value); | |
ParseDefaultStackSize(pPropertyValues[i]); | |
} | |
else | |
if (wcscmp(pPropertyNames[i], W("USE_ENTRYPOINT_FILTER")) == 0) | |
{ | |
extern void ParseUseEntryPointFilter(LPCWSTR value); | |
ParseUseEntryPointFilter(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=DefaultADID; | |
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; | |
// This is currently supported in default domain only | |
if (appDomainID != DefaultADID) | |
return HOST_E_INVALIDOPERATION; | |
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); | |
{ | |
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_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 | |
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; | |
} | |
//***************************************************************************** | |
// 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<IUnknown *>(static_cast<ICLRRuntimeHost *>(this)); | |
} | |
else if (riid == IID_ICLRRuntimeHost) | |
{ | |
*ppUnk = static_cast<ICLRRuntimeHost *>(this); | |
} | |
else if (riid == IID_ICLRRuntimeHost2) | |
{ | |
ULONG version = 2; | |
if (m_Version == 0) | |
FastInterlockCompareExchange((LONG*)&m_Version, version, 0); | |
*ppUnk = static_cast<ICLRRuntimeHost2 *>(this); | |
} | |
else if (riid == IID_ICLRRuntimeHost4) | |
{ | |
ULONG version = 4; | |
if (m_Version == 0) | |
FastInterlockCompareExchange((LONG*)&m_Version, version, 0); | |
*ppUnk = static_cast<ICLRRuntimeHost4 *>(this); | |
} | |
#ifndef TARGET_UNIX | |
else if (riid == IID_IPrivateManagedExceptionReporting) | |
{ | |
*ppUnk = static_cast<IPrivateManagedExceptionReporting *>(this); | |
} | |
#endif // !TARGET_UNIX | |
else | |
return (E_NOINTERFACE); | |
AddRef(); | |
return (S_OK); | |
} | |
#ifndef TARGET_UNIX | |
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 // !TARGET_UNIX | |
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); | |
} | |
LONG CorHost2::m_RefCount = 0; | |
/////////////////////////////////////////////////////////////////////////////// | |
// ICLRRuntimeHost::SetHostControl | |
/////////////////////////////////////////////////////////////////////////////// | |
HRESULT CorHost2::SetHostControl(IHostControl* pHostControl) | |
{ | |
return E_NOTIMPL; | |
} | |
/////////////////////////////////////////////////////////////////////////////// | |
// ICLRRuntimeHost::GetCLRControl | |
HRESULT CorHost2::GetCLRControl(ICLRControl** pCLRControl) | |
{ | |
return E_NOTIMPL; | |
} | |
// 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<IExecutionEngine*> 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> @TODO: Decide on an exception strategy for all the DLLs of the CLR, and then | |
// enable all the exceptions out of this method.</TODO> | |
// 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; i<MAX_PREDEFINED_TLS_SLOT; i++) | |
{ | |
if (i == TlsIdx_ClrDebugState || | |
i == TlsIdx_StressLog) | |
{ | |
// StressLog and DebugState may be needed during callback. | |
continue; | |
} | |
// If we have some data and a callback, issue it. | |
if (Callbacks[i] != 0 && pTlsInfo->data[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<CLREvent> 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<CLREvent> 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<CLRSemaphore> 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(TARGET_UNIX) | |
static VolatilePtr<BYTE> s_pStartOfUEFSection = NULL; | |
static VolatilePtr<BYTE> s_pEndOfUEFSectionBoundary = NULL; | |
static Volatile<DWORD> s_dwProtection = 0; | |
#endif // _DEBUG && !TARGET_UNIX | |
#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(TARGET_UNIX) | |
// 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<BYTE*>(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<BYTE*>(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 && !TARGET_UNIX | |
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<Exception**>(ppvException); | |
// Try to get a better message. | |
GetLastThrownObjectExceptionFromThread_Internal(ppException); | |
} // HRESULT CExecutionEngine::GetLastThrownObjectExceptionFromThread() | |
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; | |
} | |
} | |
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::LoadManagedTypeForWinRTTypeName(wszTypeName, /* pLoadBinder */ nullptr, &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 |