Permalink
Fetching contributors…
Cannot retrieve contributors at this time
2759 lines (2256 sloc) 83.5 KB
// 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.
/*============================================================
**
** Header: Assembly.cpp
**
**
** Purpose: Implements assembly (loader domain) architecture
**
**
===========================================================*/
#include "common.h"
#include <stdlib.h>
#include "assembly.hpp"
#include "appdomain.hpp"
#include "perfcounters.h"
#include "assemblyname.hpp"
#include "eeprofinterfaces.h"
#include "reflectclasswriter.h"
#include "comdynamic.h"
#include <wincrypt.h>
#include "urlmon.h"
#include "sha1.h"
#include "eeconfig.h"
#include "strongname.h"
#include "ceefilegenwriter.h"
#include "assemblynative.hpp"
#include "threadsuspend.h"
#ifdef FEATURE_PREJIT
#include "corcompile.h"
#endif
#include "appdomainnative.hpp"
#include "customattribute.h"
#include "winnls.h"
#include "caparser.h"
#include "../md/compiler/custattr.h"
#include "mdaassistants.h"
#include "peimagelayout.inl"
// Define these macro's to do strict validation for jit lock and class init entry leaks.
// This defines determine if the asserts that verify for these leaks are defined or not.
// These asserts can sometimes go off even if no entries have been leaked so this defines
// should be used with caution.
//
// If we are inside a .cctor when the application shut's down then the class init lock's
// head will be set and this will cause the assert to go off.,
//
// If we are jitting a method when the application shut's down then the jit lock's head
// will be set causing the assert to go off.
//#define STRICT_JITLOCK_ENTRY_LEAK_DETECTION
//#define STRICT_CLSINITLOCK_ENTRY_LEAK_DETECTION
#ifndef DACCESS_COMPILE
// This value is to make it easier to diagnose Assembly Loader "grant set" crashes.
// See Dev11 bug 358184 for more details.
// This value is not thread safe and is not intended to be. It is just a best
// effort to collect more data on the problem. Is is possible, though unlikely,
// that thread A would record a reason for an upcoming crash,
// thread B would then record a different reason, and we would then
// crash on thread A, thus ending up with the recorded reason not matching
// the thread we crash in. Be aware of this when using this value
// to help your debugging.
DWORD g_dwLoaderReasonForNotSharing = 0; // See code:DomainFile::m_dwReasonForRejectingNativeImage for a similar variable.
// These will sometimes result in a crash with error code 0x80131401 SECURITY_E_INCOMPATIBLE_SHARE
// "Loading this assembly would produce a different grant set from other instances."
enum ReasonForNotSharing
{
ReasonForNotSharing_NoInfoRecorded = 0x1,
ReasonForNotSharing_NullDomainassembly = 0x2,
ReasonForNotSharing_DebuggerFlagMismatch = 0x3,
ReasonForNotSharing_NullPeassembly = 0x4,
ReasonForNotSharing_MissingAssemblyClosure1 = 0x5,
ReasonForNotSharing_MissingAssemblyClosure2 = 0x6,
ReasonForNotSharing_MissingDependenciesResolved = 0x7,
ReasonForNotSharing_ClosureComparisonFailed = 0x8,
};
#define NO_FRIEND_ASSEMBLIES_MARKER ((FriendAssemblyDescriptor *)S_FALSE)
//----------------------------------------------------------------------------------------------
// The ctor's job is to initialize the Assembly enough so that the dtor can safely run.
// It cannot do any allocations or operations that might fail. Those operations should be done
// in Assembly::Init()
//----------------------------------------------------------------------------------------------
Assembly::Assembly(BaseDomain *pDomain, PEAssembly* pFile, DebuggerAssemblyControlFlags debuggerFlags, BOOL fIsCollectible) :
m_FreeFlag(0),
m_pDomain(pDomain),
m_pClassLoader(NULL),
m_pEntryPoint(NULL),
m_pManifest(NULL),
m_pManifestFile(clr::SafeAddRef(pFile)),
m_pOnDiskManifest(NULL),
m_pFriendAssemblyDescriptor(NULL),
m_pbStrongNameKeyPair(NULL),
m_pwStrongNameKeyContainer(NULL),
m_isDynamic(false),
#ifdef FEATURE_COLLECTIBLE_TYPES
m_isCollectible(fIsCollectible),
#endif
m_needsToHideManifestForEmit(FALSE),
m_dwDynamicAssemblyAccess(ASSEMBLY_ACCESS_RUN),
m_nextAvailableModuleIndex(1),
m_pLoaderAllocator(NULL),
m_isDisabledPrivateReflection(0),
#ifdef FEATURE_COMINTEROP
m_pITypeLib(NULL),
m_winMDStatus(WinMDStatus_Unknown),
m_pManifestWinMDImport(NULL),
#endif // FEATURE_COMINTEROP
m_fIsDomainNeutral(pDomain == SharedDomain::GetDomain()),
#ifdef FEATURE_LOADER_OPTIMIZATION
m_bMissingDependenciesCheckDone(FALSE),
#endif // FEATURE_LOADER_OPTIMIZATION
m_debuggerFlags(debuggerFlags),
m_fTerminated(FALSE),
m_HostAssemblyId(0)
#ifdef FEATURE_COMINTEROP
, m_InteropAttributeStatus(INTEROP_ATTRIBUTE_UNSET)
#endif
#ifdef FEATURE_PREJIT
, m_isInstrumentedStatus(IS_INSTRUMENTED_UNSET)
#endif
{
STANDARD_VM_CONTRACT;
}
// This name needs to stay in sync with AssemblyBuilder.MANIFEST_MODULE_NAME
// which is used in AssemblyBuilder.InitManifestModule
#define REFEMIT_MANIFEST_MODULE_NAME W("RefEmit_InMemoryManifestModule")
//----------------------------------------------------------------------------------------------
// Does most Assembly initialization tasks. It can assume the ctor has already run
// and the assembly is safely destructable. Whether this function throws or succeeds,
// it must leave the Assembly in a safely destructable state.
//----------------------------------------------------------------------------------------------
void Assembly::Init(AllocMemTracker *pamTracker, LoaderAllocator *pLoaderAllocator)
{
STANDARD_VM_CONTRACT;
if (IsSystem())
{
_ASSERTE(pLoaderAllocator == NULL); // pLoaderAllocator may only be non-null for collectible types
m_pLoaderAllocator = SystemDomain::GetGlobalLoaderAllocator();
}
else
{
if (!IsDomainNeutral())
{
if (!IsCollectible())
{
// pLoaderAllocator will only be non-null for reflection emit assemblies
_ASSERTE((pLoaderAllocator == NULL) || (pLoaderAllocator == GetDomain()->AsAppDomain()->GetLoaderAllocator()));
m_pLoaderAllocator = GetDomain()->AsAppDomain()->GetLoaderAllocator();
}
else
{
_ASSERTE(pLoaderAllocator != NULL); // ppLoaderAllocator must be non-null for collectible assemblies
m_pLoaderAllocator = pLoaderAllocator;
}
}
else
{
_ASSERTE(pLoaderAllocator == NULL); // pLoaderAllocator may only be non-null for collectible types
// use global loader heaps
m_pLoaderAllocator = SystemDomain::GetGlobalLoaderAllocator();
}
}
_ASSERTE(m_pLoaderAllocator != NULL);
m_pClassLoader = new ClassLoader(this);
m_pClassLoader->Init(pamTracker);
COUNTER_ONLY(GetPerfCounters().m_Loading.cAssemblies++);
#ifndef CROSSGEN_COMPILE
if (GetManifestFile()->IsDynamic())
// manifest modules of dynamic assemblies are always transient
m_pManifest = ReflectionModule::Create(this, GetManifestFile(), pamTracker, REFEMIT_MANIFEST_MODULE_NAME, TRUE);
else
#endif
m_pManifest = Module::Create(this, mdFileNil, GetManifestFile(), pamTracker);
PrepareModuleForAssembly(m_pManifest, pamTracker);
CacheManifestFiles();
if (!m_pManifest->IsReadyToRun())
CacheManifestExportedTypes(pamTracker);
// Check for the assemblies that contain SIMD Vector types.
// If we encounter a non-trusted assembly with these names, we will simply not recognize any of its
// methods as intrinsics.
LPCUTF8 assemblyName = GetSimpleName();
const int length = sizeof("System.Numerics") - 1;
if ((strncmp(assemblyName, "System.Numerics", length) == 0) &&
((assemblyName[length] == '\0') || (strcmp(assemblyName+length, ".Vectors") == 0)))
{
m_fIsSIMDVectorAssembly = true;
}
else
{
m_fIsSIMDVectorAssembly = false;
}
// We'll load the friend assembly information lazily. For the ngen case we should avoid
// loading it entirely.
//CacheFriendAssemblyInfo();
{
CANNOTTHROWCOMPLUSEXCEPTION();
FAULT_FORBID();
//Cannot fail after this point.
PublishModuleIntoAssembly(m_pManifest);
return; // Explicit return to let you know you are NOT welcome to add code after the CANNOTTHROW/FAULT_FORBID expires
}
}
BOOL Assembly::IsDisabledPrivateReflection()
{
CONTRACTL
{
THROWS;
}
CONTRACTL_END;
enum { UNINITIALIZED, ENABLED, DISABLED};
if (m_isDisabledPrivateReflection == UNINITIALIZED)
{
IMDInternalImport *pImport = GetManifestImport();
HRESULT hr = pImport->GetCustomAttributeByName(GetManifestToken(), DISABLED_PRIVATE_REFLECTION_TYPE, NULL, 0);
IfFailThrow(hr);
if (hr == S_OK)
{
m_isDisabledPrivateReflection = DISABLED;
}
else
{
m_isDisabledPrivateReflection = ENABLED;
}
}
return m_isDisabledPrivateReflection == DISABLED;
}
#ifndef CROSSGEN_COMPILE
Assembly::~Assembly()
{
CONTRACTL
{
NOTHROW;
GC_TRIGGERS;
DISABLED(FORBID_FAULT); //Must clean up some profiler stuff
}
CONTRACTL_END
Terminate();
if (m_pFriendAssemblyDescriptor != NULL && m_pFriendAssemblyDescriptor != NO_FRIEND_ASSEMBLIES_MARKER)
delete m_pFriendAssemblyDescriptor;
if (m_pbStrongNameKeyPair && (m_FreeFlag & FREE_KEY_PAIR))
delete[] m_pbStrongNameKeyPair;
if (m_pwStrongNameKeyContainer && (m_FreeFlag & FREE_KEY_CONTAINER))
delete[] m_pwStrongNameKeyContainer;
if (IsDynamic()) {
if (m_pOnDiskManifest)
// clear the on disk manifest if it is not cleared yet.
m_pOnDiskManifest = NULL;
}
if (m_pManifestFile)
{
m_pManifestFile->Release();
}
#ifdef FEATURE_COMINTEROP
if (m_pManifestWinMDImport)
{
m_pManifestWinMDImport->Release();
}
#endif // FEATURE_COMINTEROP
}
#ifdef FEATURE_PREJIT
void Assembly::DeleteNativeCodeRanges()
{
CONTRACTL
{
NOTHROW;
GC_NOTRIGGER;
MODE_PREEMPTIVE;
FORBID_FAULT;
}
CONTRACTL_END
ModuleIterator i = IterateModules();
while (i.Next())
i.GetModule()->DeleteNativeCodeRanges();
}
#endif
#ifdef PROFILING_SUPPORTED
void ProfilerCallAssemblyUnloadStarted(Assembly* assemblyUnloaded)
{
WRAPPER_NO_CONTRACT;
{
BEGIN_PIN_PROFILER(CORProfilerPresent());
GCX_PREEMP();
g_profControlBlock.pProfInterface->AssemblyUnloadStarted((AssemblyID)assemblyUnloaded);
END_PIN_PROFILER();
}
}
void ProfilerCallAssemblyUnloadFinished(Assembly* assemblyUnloaded)
{
WRAPPER_NO_CONTRACT;
{
BEGIN_PIN_PROFILER(CORProfilerPresent());
GCX_PREEMP();
g_profControlBlock.pProfInterface->AssemblyUnloadFinished((AssemblyID) assemblyUnloaded, S_OK);
END_PIN_PROFILER();
}
}
#endif
void Assembly::StartUnload()
{
STATIC_CONTRACT_NOTHROW;
STATIC_CONTRACT_GC_TRIGGERS;
STATIC_CONTRACT_FORBID_FAULT;
#ifdef PROFILING_SUPPORTED
if (CORProfilerTrackAssemblyLoads())
{
ProfilerCallAssemblyUnloadStarted(this);
}
#endif
// we need to release tlb files eagerly
#ifdef FEATURE_COMINTEROP
if(g_fProcessDetach == FALSE)
{
DefaultCatchFilterParam param; param.pv = COMPLUS_EXCEPTION_EXECUTE_HANDLER;
PAL_TRY(Assembly *, pThis, this)
{
if (pThis->m_pITypeLib && pThis->m_pITypeLib != (ITypeLib*)-1) {
pThis->m_pITypeLib->Release();
pThis->m_pITypeLib = NULL;
}
}
PAL_EXCEPT_FILTER(DefaultCatchFilter)
{
}
PAL_ENDTRY
}
#endif // FEATURE_COMINTEROP
}
void Assembly::Terminate( BOOL signalProfiler )
{
STATIC_CONTRACT_NOTHROW;
STATIC_CONTRACT_GC_TRIGGERS;
STRESS_LOG1(LF_LOADER, LL_INFO100, "Assembly::Terminate (this = 0x%p)\n", reinterpret_cast<void *>(this));
if (this->m_fTerminated)
return;
if (m_pClassLoader != NULL)
{
GCX_PREEMP();
delete m_pClassLoader;
m_pClassLoader = NULL;
}
if (m_pLoaderAllocator != NULL)
{
if (IsCollectible())
{
// This cleanup code starts resembling parts of AppDomain::Terminate too much.
// It would be useful to reduce duplication and also establish clear responsibilites
// for LoaderAllocator::Destroy, Assembly::Terminate, LoaderAllocator::Terminate
// and LoaderAllocator::~LoaderAllocator. We need to establish how these
// cleanup paths interact with app-domain unload and process tear-down, too.
if (!IsAtProcessExit())
{
// Suspend the EE to do some clean up that can only occur
// while no threads are running.
GCX_COOP (); // SuspendEE may require current thread to be in Coop mode
// SuspendEE cares about the reason flag only when invoked for a GC
// Other values are typically ignored. If using SUSPEND_FOR_APPDOMAIN_SHUTDOWN
// is inappropriate, we can introduce a new flag or hijack an unused one.
ThreadSuspend::SuspendEE(ThreadSuspend::SUSPEND_FOR_APPDOMAIN_SHUTDOWN);
}
ExecutionManager::Unload(m_pLoaderAllocator);
m_pLoaderAllocator->UninitVirtualCallStubManager();
MethodTable::ClearMethodDataCache();
_ASSERTE(m_pDomain->IsAppDomain());
AppDomain *pAppDomain = m_pDomain->AsAppDomain();
ClearJitGenericHandleCache(pAppDomain);
if (!IsAtProcessExit())
{
// Resume the EE.
ThreadSuspend::RestartEE(FALSE, TRUE);
}
// Once the manifest file is tenured, the managed LoaderAllocatorScout is responsible for cleanup.
if (m_pManifest != NULL && m_pManifest->IsTenured())
{
pAppDomain->RegisterLoaderAllocatorForDeletion(m_pLoaderAllocator);
}
}
m_pLoaderAllocator = NULL;
}
COUNTER_ONLY(GetPerfCounters().m_Loading.cAssemblies--);
#ifdef PROFILING_SUPPORTED
if (CORProfilerTrackAssemblyLoads())
{
ProfilerCallAssemblyUnloadFinished(this);
}
#endif // PROFILING_SUPPORTED
this->m_fTerminated = TRUE;
}
#endif // CROSSGEN_COMPILE
Assembly * Assembly::Create(
BaseDomain * pDomain,
PEAssembly * pFile,
DebuggerAssemblyControlFlags debuggerFlags,
BOOL fIsCollectible,
AllocMemTracker * pamTracker,
LoaderAllocator * pLoaderAllocator)
{
STANDARD_VM_CONTRACT;
NewHolder<Assembly> pAssembly (new Assembly(pDomain, pFile, debuggerFlags, fIsCollectible));
// If there are problems that arise from this call stack, we'll chew up a lot of stack
// with the various EX_TRY/EX_HOOKs that we will encounter.
INTERIOR_STACK_PROBE_FOR(GetThread(), DEFAULT_ENTRY_PROBE_SIZE);
#ifdef PROFILING_SUPPORTED
{
BEGIN_PIN_PROFILER(CORProfilerTrackAssemblyLoads());
GCX_COOP();
g_profControlBlock.pProfInterface->AssemblyLoadStarted((AssemblyID)(Assembly *) pAssembly);
END_PIN_PROFILER();
}
// Need TRY/HOOK instead of holder so we can get HR of exception thrown for profiler callback
EX_TRY
#endif
{
pAssembly->Init(pamTracker, pLoaderAllocator);
}
#ifdef PROFILING_SUPPORTED
EX_HOOK
{
{
BEGIN_PIN_PROFILER(CORProfilerTrackAssemblyLoads());
GCX_COOP();
g_profControlBlock.pProfInterface->AssemblyLoadFinished((AssemblyID)(Assembly *) pAssembly,
GET_EXCEPTION()->GetHR());
END_PIN_PROFILER();
}
}
EX_END_HOOK;
#endif
pAssembly.SuppressRelease();
END_INTERIOR_STACK_PROBE;
return pAssembly;
} // Assembly::Create
#ifndef CROSSGEN_COMPILE
Assembly *Assembly::CreateDynamic(AppDomain *pDomain, CreateDynamicAssemblyArgs *args)
{
// WARNING: not backout clean
CONTRACT(Assembly *)
{
THROWS;
GC_TRIGGERS;
INJECT_FAULT(COMPlusThrowOM(););
MODE_COOPERATIVE;
PRECONDITION(CheckPointer(args));
}
CONTRACT_END;
// This must be before creation of the AllocMemTracker so that the destructor for the AllocMemTracker happens before the destructor for pLoaderAllocator.
// That is necessary as the allocation of Assembly objects and other related details is done on top of heaps located in
// the loader allocator objects.
NewHolder<LoaderAllocator> pLoaderAllocator;
AllocMemTracker amTracker;
AllocMemTracker *pamTracker = &amTracker;
Assembly *pRetVal = NULL;
AppDomain *pCallersDomain;
MethodDesc *pmdEmitter = SystemDomain::GetCallersMethod(args->stackMark, &pCallersDomain);
// Called either from interop or async delegate invocation. Rejecting because we don't
// know how to set the correct permission on the new dynamic assembly.
if (!pmdEmitter)
COMPlusThrow(kInvalidOperationException);
Assembly *pCallerAssembly = pmdEmitter->GetAssembly();
// First, we set up a pseudo-manifest file for the assembly.
// Set up the assembly name
STRINGREF strRefName = (STRINGREF) args->assemblyName->GetSimpleName();
if (strRefName == NULL)
COMPlusThrow(kArgumentException, W("ArgumentNull_AssemblyNameName"));
StackSString name;
strRefName->GetSString(name);
if (name.GetCount() == 0)
COMPlusThrow(kArgumentException, W("ArgumentNull_AssemblyNameName"));
SString::Iterator i = name.Begin();
if (COMCharacter::nativeIsWhiteSpace(*i)
|| name.Find(i, '\\')
|| name.Find(i, ':')
|| name.Find(i, '/'))
{
COMPlusThrow(kArgumentException, W("Argument_InvalidAssemblyName"));
}
// Set up the assembly manifest metadata
// When we create dynamic assembly, we always use a working copy of IMetaDataAssemblyEmit
// to store temporary runtime assembly information. This is to preserve the invariant that
// an assembly must have a PEFile with proper metadata.
// This working copy of IMetaDataAssemblyEmit will store every AssemblyRef as a simple name
// reference as we must have an instance of Assembly(can be dynamic assembly) before we can
// add such a reference. Also because the referenced assembly if dynamic strong name, it may
// not be ready to be hashed!
SafeComHolder<IMetaDataAssemblyEmit> pAssemblyEmit;
PEFile::DefineEmitScope(
IID_IMetaDataAssemblyEmit,
&pAssemblyEmit);
// remember the hash algorithm
ULONG ulHashAlgId = args->assemblyName->GetAssemblyHashAlgorithm();
if (ulHashAlgId == 0)
ulHashAlgId = CALG_SHA1;
ASSEMBLYMETADATA assemData;
memset(&assemData, 0, sizeof(assemData));
// get the version info (default to 0.0.0.0 if none)
VERSIONREF versionRef = (VERSIONREF) args->assemblyName->GetVersion();
if (versionRef != NULL)
{
assemData.usMajorVersion = (USHORT)versionRef->GetMajor();
assemData.usMinorVersion = (USHORT)versionRef->GetMinor();
assemData.usBuildNumber = (USHORT)versionRef->GetBuild();
assemData.usRevisionNumber = (USHORT)versionRef->GetRevision();
}
struct _gc
{
OBJECTREF cultureinfo;
STRINGREF pString;
OBJECTREF orArrayOrContainer;
OBJECTREF throwable;
OBJECTREF strongNameKeyPair;
} gc;
ZeroMemory(&gc, sizeof(gc));
GCPROTECT_BEGIN(gc);
StackSString culture;
gc.cultureinfo = args->assemblyName->GetCultureInfo();
if (gc.cultureinfo != NULL)
{
MethodDescCallSite getName(METHOD__CULTURE_INFO__GET_NAME, &gc.cultureinfo);
ARG_SLOT args2[] =
{
ObjToArgSlot(gc.cultureinfo)
};
// convert culture info into a managed string form
gc.pString = getName.Call_RetSTRINGREF(args2);
gc.pString->GetSString(culture);
assemData.szLocale = (LPWSTR) (LPCWSTR) culture;
}
SBuffer publicKey;
if (args->assemblyName->GetPublicKey() != NULL)
{
publicKey.Set(args->assemblyName->GetPublicKey()->GetDataPtr(),
args->assemblyName->GetPublicKey()->GetNumComponents());
}
// get flags
DWORD dwFlags = args->assemblyName->GetFlags();
// Now create a dynamic PE file out of the name & metadata
PEAssemblyHolder pFile;
{
GCX_PREEMP();
mdAssembly ma;
IfFailThrow(pAssemblyEmit->DefineAssembly(publicKey, publicKey.GetSize(), ulHashAlgId,
name, &assemData, dwFlags,
&ma));
pFile = PEAssembly::Create(pCallerAssembly->GetManifestFile(), pAssemblyEmit);
// Dynamically created modules (aka RefEmit assemblies) do not have a LoadContext associated with them since they are not bound
// using an actual binder. As a result, we will assume the same binding/loadcontext information for the dynamic assembly as its
// caller/creator to ensure that any assembly loads triggered by the dynamic assembly are resolved using the intended load context.
//
// If the creator assembly has a HostAssembly associated with it, then use it for binding. Otherwise, the creator is dynamic
// and will have a fallback load context binder associated with it.
ICLRPrivBinder *pFallbackLoadContextBinder = nullptr;
// There is always a manifest file - wehther working with static or dynamic assemblies.
PEFile *pCallerAssemblyManifestFile = pCallerAssembly->GetManifestFile();
_ASSERTE(pCallerAssemblyManifestFile != NULL);
if (!pCallerAssemblyManifestFile->IsDynamic())
{
// Static assemblies with do not have fallback load context
_ASSERTE(pCallerAssemblyManifestFile->GetFallbackLoadContextBinder() == nullptr);
if (pCallerAssemblyManifestFile->IsSystem())
{
// CoreLibrary is always bound to TPA binder
pFallbackLoadContextBinder = pDomain->GetTPABinderContext();
}
else
{
// Fetch the binder from the host assembly
PTR_ICLRPrivAssembly pCallerAssemblyHostAssembly = pCallerAssemblyManifestFile->GetHostAssembly();
_ASSERTE(pCallerAssemblyHostAssembly != nullptr);
UINT_PTR assemblyBinderID = 0;
IfFailThrow(pCallerAssemblyHostAssembly->GetBinderID(&assemblyBinderID));
pFallbackLoadContextBinder = reinterpret_cast<ICLRPrivBinder *>(assemblyBinderID);
}
}
else
{
// Creator assembly is dynamic too, so use its fallback load context for the one
// we are creating.
pFallbackLoadContextBinder = pCallerAssemblyManifestFile->GetFallbackLoadContextBinder();
}
// At this point, we should have a fallback load context binder to work with
_ASSERTE(pFallbackLoadContextBinder != nullptr);
// Set it as the fallback load context binder for the dynamic assembly being created
pFile->SetFallbackLoadContextBinder(pFallbackLoadContextBinder);
}
NewHolder<DomainAssembly> pDomainAssembly;
{
GCX_PREEMP();
// Create a new LoaderAllocator if appropriate
if ((args->access & ASSEMBLY_ACCESS_COLLECT) != 0)
{
AssemblyLoaderAllocator *pAssemblyLoaderAllocator = new AssemblyLoaderAllocator();
pLoaderAllocator = pAssemblyLoaderAllocator;
// Some of the initialization functions are not virtual. Call through the derived class
// to prevent calling the base class version.
pAssemblyLoaderAllocator->Init(pDomain);
// Setup the managed proxy now, but do not actually transfer ownership to it.
// Once everything is setup and nothing can fail anymore, the ownership will be
// atomically transfered by call to LoaderAllocator::ActivateManagedTracking().
pAssemblyLoaderAllocator->SetupManagedTracking(&args->loaderAllocator);
}
else
{
pLoaderAllocator = pDomain->GetLoaderAllocator();
pLoaderAllocator.SuppressRelease();
}
// Create a domain assembly
pDomainAssembly = new DomainAssembly(pDomain, pFile, pLoaderAllocator);
}
// Start loading process
{
// Create a concrete assembly
// (!Do not remove scoping brace: order is important here: the Assembly holder must destruct before the AllocMemTracker!)
NewHolder<Assembly> pAssem;
{
GCX_PREEMP();
// Assembly::Create will call SuppressRelease on the NewHolder that holds the LoaderAllocator when it transfers ownership
pAssem = Assembly::Create(pDomain, pFile, pDomainAssembly->GetDebuggerInfoBits(), args->access & ASSEMBLY_ACCESS_COLLECT ? TRUE : FALSE, pamTracker, pLoaderAllocator);
ReflectionModule* pModule = (ReflectionModule*) pAssem->GetManifestModule();
pModule->SetCreatingAssembly( pCallerAssembly );
if ((args->access & ASSEMBLY_ACCESS_COLLECT) != 0)
{
// Initializing the virtual call stub manager is delayed to remove the need for the LoaderAllocator destructor to properly handle
// uninitializing the VSD system. (There is a need to suspend the runtime, and that's tricky)
pLoaderAllocator->InitVirtualCallStubManager(pDomain, TRUE);
}
}
pAssem->m_isDynamic = true;
pAssem->m_dwDynamicAssemblyAccess = args->access;
// Set the additional strong name information
pAssem->SetStrongNameLevel(Assembly::SN_NONE);
if (publicKey.GetSize() > 0)
{
pAssem->SetStrongNameLevel(Assembly::SN_PUBLIC_KEY);
{
// Since we have no way to validate the public key of a dynamic assembly we don't allow
// partial trust code to emit a dynamic assembly with an arbitrary public key.
// Ideally we shouldn't allow anyone to emit a dynamic assembly with only a public key,
// but we allow a couple of exceptions to reduce the compat risk: full trust, caller's own key.
// As usual we treat anonymously hosted dynamic methods as partial trust code.
DomainAssembly* pCallerDomainAssembly = pCallerAssembly->GetDomainAssembly(pCallersDomain);
if (pCallerDomainAssembly == pCallersDomain->GetAnonymouslyHostedDynamicMethodsAssembly())
{
DWORD cbKey = 0;
const void* pKey = pCallerAssembly->GetPublicKey(&cbKey);
if (!publicKey.Equals((const BYTE *)pKey, cbKey))
COMPlusThrow(kInvalidOperationException, W("InvalidOperation_StrongNameKeyPairRequired"));
}
}
}
//we need to suppress release for pAssem to avoid double release
pAssem.SuppressRelease ();
{
GCX_PREEMP();
// Finish loading process
// <TODO> would be REALLY nice to unify this with main loading loop </TODO>
pDomainAssembly->Begin();
pDomainAssembly->SetAssembly(pAssem);
pDomainAssembly->m_level = FILE_LOAD_ALLOCATE;
pDomainAssembly->DeliverSyncEvents();
pDomainAssembly->DeliverAsyncEvents();
pDomainAssembly->FinishLoad();
pDomainAssembly->ClearLoading();
pDomainAssembly->m_level = FILE_ACTIVE;
}
{
CANNOTTHROWCOMPLUSEXCEPTION();
FAULT_FORBID();
//Cannot fail after this point
pDomainAssembly.SuppressRelease(); // This also effectively suppresses the release of the pAssem
pamTracker->SuppressRelease();
// Once we reach this point, the loader allocator lifetime is controlled by the Assembly object.
if ((args->access & ASSEMBLY_ACCESS_COLLECT) != 0)
{
// Atomically transfer ownership to the managed heap
pLoaderAllocator->ActivateManagedTracking();
pLoaderAllocator.SuppressRelease();
}
pAssem->SetIsTenured();
pRetVal = pAssem;
}
}
GCPROTECT_END();
RETURN pRetVal;
} // Assembly::CreateDynamic
#endif // CROSSGEN_COMPILE
void Assembly::SetDomainAssembly(DomainAssembly *pDomainAssembly)
{
CONTRACTL
{
PRECONDITION(CheckPointer(pDomainAssembly));
THROWS;
GC_TRIGGERS;
INJECT_FAULT(COMPlusThrowOM(););
}
CONTRACTL_END;
GetManifestModule()->SetDomainFile(pDomainAssembly);
} // Assembly::SetDomainAssembly
#endif // #ifndef DACCESS_COMPILE
DomainAssembly *Assembly::GetDomainAssembly(AppDomain *pDomain)
{
CONTRACT(DomainAssembly *)
{
PRECONDITION(CheckPointer(pDomain, NULL_NOT_OK));
POSTCONDITION(CheckPointer(RETVAL));
THROWS;
GC_TRIGGERS;
}
CONTRACT_END;
RETURN GetManifestModule()->GetDomainAssembly(pDomain);
}
DomainAssembly *Assembly::FindDomainAssembly(AppDomain *pDomain)
{
CONTRACT(DomainAssembly *)
{
PRECONDITION(CheckPointer(pDomain));
POSTCONDITION(CheckPointer(RETVAL, NULL_OK));
NOTHROW;
GC_NOTRIGGER;
FORBID_FAULT;
SO_TOLERANT;
SUPPORTS_DAC;
}
CONTRACT_END;
PREFIX_ASSUME (GetManifestModule() !=NULL);
RETURN GetManifestModule()->FindDomainAssembly(pDomain);
}
PTR_LoaderHeap Assembly::GetLowFrequencyHeap()
{
WRAPPER_NO_CONTRACT;
return GetLoaderAllocator()->GetLowFrequencyHeap();
}
PTR_LoaderHeap Assembly::GetHighFrequencyHeap()
{
WRAPPER_NO_CONTRACT;
return GetLoaderAllocator()->GetHighFrequencyHeap();
}
PTR_LoaderHeap Assembly::GetStubHeap()
{
WRAPPER_NO_CONTRACT;
return GetLoaderAllocator()->GetStubHeap();
}
PTR_BaseDomain Assembly::GetDomain()
{
LIMITED_METHOD_CONTRACT;
SUPPORTS_DAC;
_ASSERTE(m_pDomain);
return (m_pDomain);
}
#ifndef DACCESS_COMPILE
void Assembly::SetParent(BaseDomain* pParent)
{
LIMITED_METHOD_CONTRACT;
m_pDomain = pParent;
}
#endif // !DACCCESS_COMPILE
mdFile Assembly::GetManifestFileToken(LPCSTR name)
{
return mdFileNil;
}
mdFile Assembly::GetManifestFileToken(IMDInternalImport *pImport, mdFile kFile)
{
WRAPPER_NO_CONTRACT;
SUPPORTS_DAC;
LPCSTR name;
if ((TypeFromToken(kFile) != mdtFile) ||
!pImport->IsValidToken(kFile))
{
BAD_FORMAT_NOTHROW_ASSERT(!"Invalid File token");
return mdTokenNil;
}
if (FAILED(pImport->GetFileProps(kFile, &name, NULL, NULL, NULL)))
{
BAD_FORMAT_NOTHROW_ASSERT(!"Invalid File token");
return mdTokenNil;
}
return GetManifestFileToken(name);
}
Module *Assembly::FindModuleByExportedType(mdExportedType mdType,
Loader::LoadFlag loadFlag,
mdTypeDef mdNested,
mdTypeDef* pCL)
{
CONTRACT(Module *)
{
if (FORBIDGC_LOADER_USE_ENABLED()) NOTHROW; else THROWS;
if (FORBIDGC_LOADER_USE_ENABLED()) GC_NOTRIGGER; else GC_TRIGGERS;
if (FORBIDGC_LOADER_USE_ENABLED()) FORBID_FAULT; else INJECT_FAULT(COMPlusThrowOM(););
MODE_ANY;
POSTCONDITION(CheckPointer(RETVAL, loadFlag==Loader::Load ? NULL_NOT_OK : NULL_OK));
SUPPORTS_DAC;
}
CONTRACT_END
mdToken mdLinkRef;
mdToken mdBinding;
IMDInternalImport *pManifestImport = GetManifestImport();
IfFailThrow(pManifestImport->GetExportedTypeProps(
mdType,
NULL,
NULL,
&mdLinkRef, // Impl
&mdBinding, // Hint
NULL)); // dwflags
// Don't trust the returned tokens.
if (!pManifestImport->IsValidToken(mdLinkRef))
{
if (loadFlag != Loader::Load)
{
RETURN NULL;
}
else
{
ThrowHR(COR_E_BADIMAGEFORMAT, BFA_INVALID_TOKEN);
}
}
switch(TypeFromToken(mdLinkRef)) {
case mdtAssemblyRef:
{
*pCL = mdTypeDefNil; // We don't trust the mdBinding token
Assembly *pAssembly = NULL;
switch(loadFlag)
{
case Loader::Load:
{
#ifndef DACCESS_COMPILE
// LoadAssembly never returns NULL
DomainAssembly * pDomainAssembly =
GetManifestModule()->LoadAssembly(::GetAppDomain(), mdLinkRef);
PREFIX_ASSUME(pDomainAssembly != NULL);
RETURN pDomainAssembly->GetCurrentModule();
#else
_ASSERTE(!"DAC shouldn't attempt to trigger loading");
return NULL;
#endif // !DACCESS_COMPILE
};
case Loader::DontLoad:
pAssembly = GetManifestModule()->GetAssemblyIfLoaded(mdLinkRef);
break;
case Loader::SafeLookup:
pAssembly = GetManifestModule()->LookupAssemblyRef(mdLinkRef);
break;
default:
_ASSERTE(FALSE);
}
if (pAssembly)
RETURN pAssembly->GetManifestModule();
else
RETURN NULL;
}
case mdtFile:
{
// We may not want to trust this TypeDef token, since it
// was saved in a scope other than the one it was defined in
if (mdNested == mdTypeDefNil)
*pCL = mdBinding;
else
*pCL = mdNested;
// Note that we don't want to attempt a LoadModule if a GetModuleIfLoaded will
// succeed, because it has a stronger contract.
Module *pModule = GetManifestModule()->GetModuleIfLoaded(mdLinkRef, TRUE, FALSE);
#ifdef DACCESS_COMPILE
return pModule;
#else
if (pModule != NULL)
RETURN pModule;
if(loadFlag==Loader::SafeLookup)
return NULL;
// We should never get here in the GC case - the above should have succeeded.
CONSISTENCY_CHECK(!FORBIDGC_LOADER_USE_ENABLED());
DomainFile * pDomainModule = GetManifestModule()->LoadModule(::GetAppDomain(), mdLinkRef, FALSE, loadFlag!=Loader::Load);
if (pDomainModule == NULL)
RETURN NULL;
else
{
pModule = pDomainModule->GetCurrentModule();
if (pModule == NULL)
{
_ASSERTE(loadFlag!=Loader::Load);
}
RETURN pModule;
}
#endif // DACCESS_COMPILE
}
case mdtExportedType:
// Only override the nested type token if it hasn't been set yet.
if (mdNested != mdTypeDefNil)
mdBinding = mdNested;
RETURN FindModuleByExportedType(mdLinkRef, loadFlag, mdBinding, pCL);
default:
ThrowHR(COR_E_BADIMAGEFORMAT, BFA_INVALID_TOKEN_TYPE);
}
} // Assembly::FindModuleByExportedType
// The returned Module is non-NULL unless you prevented the load by setting loadFlag=Loader::DontLoad.
/* static */
Module * Assembly::FindModuleByTypeRef(
Module * pModule,
mdTypeRef tkType,
Loader::LoadFlag loadFlag,
BOOL * pfNoResolutionScope)
{
CONTRACT(Module *)
{
if (FORBIDGC_LOADER_USE_ENABLED()) NOTHROW; else THROWS;
if (FORBIDGC_LOADER_USE_ENABLED()) GC_NOTRIGGER; else GC_TRIGGERS;
if (FORBIDGC_LOADER_USE_ENABLED()) FORBID_FAULT; else { INJECT_FAULT(COMPlusThrowOM();); }
MODE_ANY;
PRECONDITION(CheckPointer(pModule));
PRECONDITION(TypeFromToken(tkType) == mdtTypeRef);
PRECONDITION(CheckPointer(pfNoResolutionScope));
POSTCONDITION( CheckPointer(RETVAL, loadFlag==Loader::Load ? NULL_NOT_OK : NULL_OK) );
SUPPORTS_DAC;
}
CONTRACT_END
// WARNING! Correctness of the type forwarder detection algorithm in code:ClassLoader::ResolveTokenToTypeDefThrowing
// relies on this function not performing any form of type forwarding itself.
IMDInternalImport * pImport;
mdTypeRef tkTopLevelEncloserTypeRef;
pImport = pModule->GetMDImport();
if (TypeFromToken(tkType) != mdtTypeRef)
{
ThrowHR(COR_E_BADIMAGEFORMAT, BFA_INVALID_TOKEN_TYPE);
}
{
// Find the top level encloser
GCX_NOTRIGGER();
// If nested, get top level encloser's impl
int iter = 0;
int maxIter = 1000;
do
{
_ASSERTE(TypeFromToken(tkType) == mdtTypeRef);
tkTopLevelEncloserTypeRef = tkType;
if (!pImport->IsValidToken(tkType) || iter >= maxIter)
{
break;
}
IfFailThrow(pImport->GetResolutionScopeOfTypeRef(tkType, &tkType));
// nil-scope TR okay if there's an ExportedType
// Return manifest file
if (IsNilToken(tkType))
{
*pfNoResolutionScope = TRUE;
RETURN(pModule);
}
iter++;
}
while (TypeFromToken(tkType) == mdtTypeRef);
}
*pfNoResolutionScope = FALSE;
#ifndef DACCESS_COMPILE
if (!pImport->IsValidToken(tkType)) // redundant check only when invalid token already found.
{
THROW_BAD_FORMAT(BFA_BAD_TYPEREF_TOKEN, pModule);
}
#endif //!DACCESS_COMPILE
switch (TypeFromToken(tkType))
{
case mdtModule:
{
// Type is in the referencing module.
GCX_NOTRIGGER();
CANNOTTHROWCOMPLUSEXCEPTION();
RETURN( pModule );
}
case mdtModuleRef:
{
if ((loadFlag != Loader::Load) || IsGCThread() || IsStackWalkerThread())
{
// Either we're not supposed to load, or we're doing a GC or stackwalk
// in which case we shouldn't need to load. So just look up the module
// and return what we find.
RETURN(pModule->LookupModule(tkType,FALSE));
}
#ifndef DACCESS_COMPILE
DomainFile * pActualDomainFile = pModule->LoadModule(::GetAppDomain(), tkType, FALSE, loadFlag!=Loader::Load);
if (pActualDomainFile == NULL)
{
RETURN NULL;
}
else
{
RETURN(pActualDomainFile->GetModule());
}
#else //DACCESS_COMPILE
_ASSERTE(loadFlag!=Loader::Load);
DacNotImpl();
RETURN NULL;
#endif //DACCESS_COMPILE
}
break;
case mdtAssemblyRef:
{
// Do this first because it has a strong contract
Assembly * pAssembly = NULL;
#if defined(FEATURE_COMINTEROP) || !defined(DACCESS_COMPILE)
LPCUTF8 szNamespace = NULL;
LPCUTF8 szClassName = NULL;
#endif
#ifdef FEATURE_COMINTEROP
if (pModule->HasBindableIdentity(tkType))
#endif// FEATURE_COMINTEROP
{
if (loadFlag == Loader::SafeLookup)
{
pAssembly = pModule->LookupAssemblyRef(tkType);
}
else
{
pAssembly = pModule->GetAssemblyIfLoaded(tkType);
}
}
#ifdef FEATURE_COMINTEROP
else
{
_ASSERTE(IsAfContentType_WindowsRuntime(pModule->GetAssemblyRefFlags(tkType)));
if (FAILED(pImport->GetNameOfTypeRef(
tkTopLevelEncloserTypeRef,
&szNamespace,
&szClassName)))
{
THROW_BAD_FORMAT(BFA_BAD_TYPEREF_TOKEN, pModule);
}
pAssembly = pModule->GetAssemblyIfLoaded(
tkType,
szNamespace,
szClassName,
NULL); // pMDImportOverride
}
#endif // FEATURE_COMINTEROP
if (pAssembly != NULL)
{
RETURN pAssembly->m_pManifest;
}
#ifdef DACCESS_COMPILE
RETURN NULL;
#else
if (loadFlag != Loader::Load)
{
RETURN NULL;
}
DomainAssembly * pDomainAssembly = pModule->LoadAssembly(
::GetAppDomain(),
tkType,
szNamespace,
szClassName);
if (pDomainAssembly == NULL)
RETURN NULL;
pAssembly = pDomainAssembly->GetCurrentAssembly();
if (pAssembly == NULL)
{
RETURN NULL;
}
else
{
RETURN pAssembly->m_pManifest;
}
#endif //!DACCESS_COMPILE
}
default:
ThrowHR(COR_E_BADIMAGEFORMAT, BFA_INVALID_TOKEN_TYPE);
}
} // Assembly::FindModuleByTypeRef
#ifndef DACCESS_COMPILE
Module *Assembly::FindModuleByName(LPCSTR pszModuleName)
{
CONTRACT(Module *)
{
THROWS;
GC_TRIGGERS;
INJECT_FAULT(COMPlusThrowOM(););
MODE_ANY;
POSTCONDITION(CheckPointer(RETVAL));
}
CONTRACT_END;
CQuickBytes qbLC;
// Need to perform case insensitive hashing.
UTF8_TO_LOWER_CASE(pszModuleName, qbLC);
pszModuleName = (LPUTF8) qbLC.Ptr();
mdFile kFile = GetManifestFileToken(pszModuleName);
if (kFile == mdTokenNil)
ThrowHR(COR_E_UNAUTHORIZEDACCESS);
if (this == SystemDomain::SystemAssembly())
RETURN m_pManifest->GetModuleIfLoaded(kFile, TRUE, TRUE);
else
RETURN m_pManifest->LoadModule(::GetAppDomain(), kFile)->GetModule();
}
void Assembly::CacheManifestExportedTypes(AllocMemTracker *pamTracker)
{
CONTRACT_VOID
{
THROWS;
GC_TRIGGERS;
INJECT_FAULT(COMPlusThrowOM(););
}
CONTRACT_END;
// Prejitted assemblies are expected to have their table prebuilt.
// If not, we do it here at load time (as if we would jit the assembly).
if (m_pManifest->IsPersistedObject(m_pManifest->m_pAvailableClasses))
RETURN;
mdToken mdExportedType;
HENUMInternalHolder phEnum(GetManifestImport());
phEnum.EnumInit(mdtExportedType,
mdTokenNil);
ClassLoader::AvailableClasses_LockHolder lh(m_pClassLoader);
for(int i = 0; GetManifestImport()->EnumNext(&phEnum, &mdExportedType); i++)
m_pClassLoader->AddExportedTypeHaveLock(GetManifestModule(),
mdExportedType,
pamTracker);
RETURN;
}
void Assembly::CacheManifestFiles()
{
}
//<TODO>@TODO: if module is not signed it needs to acquire the
//permissions from the assembly.</TODO>
void Assembly::PrepareModuleForAssembly(Module* module, AllocMemTracker *pamTracker)
{
CONTRACTL
{
THROWS;
GC_TRIGGERS;
INJECT_FAULT(COMPlusThrowOM(););
PRECONDITION(CheckPointer(module));
}
CONTRACTL_END;
if (module->m_pAvailableClasses != NULL && !module->IsPersistedObject(module->m_pAvailableClasses))
{
// ! We intentionally do not take the AvailableClass lock here. It creates problems at
// startup and we haven't yet published the module yet so nobody should be searching it.
m_pClassLoader->PopulateAvailableClassHashTable(module, pamTracker);
}
#ifdef DEBUGGING_SUPPORTED
// Modules take the DebuggerAssemblyControlFlags down from its
// parent Assembly initially.
module->SetDebuggerInfoBits(GetDebuggerInfoBits());
LOG((LF_CORDB, LL_INFO10, "Module %s: bits=0x%x\n",
module->GetFile()->GetSimpleName(),
module->GetDebuggerInfoBits()));
#endif // DEBUGGING_SUPPORTED
m_pManifest->EnsureFileCanBeStored(module->GetModuleRef());
}
// This is the final step of publishing a Module into an Assembly. This step cannot fail.
void Assembly::PublishModuleIntoAssembly(Module *module)
{
CONTRACTL
{
NOTHROW;
GC_TRIGGERS;
FORBID_FAULT;
}
CONTRACTL_END
GetManifestModule()->EnsuredStoreFile(module->GetModuleRef(), module);
FastInterlockIncrement((LONG*)&m_pClassLoader->m_cUnhashedModules);
}
//*****************************************************************************
// Set up the list of names of any friend assemblies
void Assembly::CacheFriendAssemblyInfo()
{
CONTRACTL
{
THROWS;
GC_TRIGGERS;
INJECT_FAULT(COMPlusThrowOM(););
}
CONTRACTL_END
if (m_pFriendAssemblyDescriptor == NULL)
{
FriendAssemblyDescriptor *pFriendAssemblies = FriendAssemblyDescriptor::CreateFriendAssemblyDescriptor(this->GetManifestFile());
if (pFriendAssemblies == NULL)
{
pFriendAssemblies = NO_FRIEND_ASSEMBLIES_MARKER;
}
void *pvPreviousDescriptor = InterlockedCompareExchangeT(&m_pFriendAssemblyDescriptor,
pFriendAssemblies,
NULL);
if (pvPreviousDescriptor != NULL && pFriendAssemblies != NO_FRIEND_ASSEMBLIES_MARKER)
{
if (pFriendAssemblies != NO_FRIEND_ASSEMBLIES_MARKER)
{
delete pFriendAssemblies;
}
}
}
} // void Assembly::CacheFriendAssemblyInfo()
//*****************************************************************************
// Is the given assembly a friend of this assembly?
bool Assembly::GrantsFriendAccessTo(Assembly *pAccessingAssembly, FieldDesc *pFD)
{
WRAPPER_NO_CONTRACT;
CacheFriendAssemblyInfo();
if (m_pFriendAssemblyDescriptor == NO_FRIEND_ASSEMBLIES_MARKER)
{
return false;
}
return m_pFriendAssemblyDescriptor->GrantsFriendAccessTo(pAccessingAssembly, pFD);
}
bool Assembly::GrantsFriendAccessTo(Assembly *pAccessingAssembly, MethodDesc *pMD)
{
WRAPPER_NO_CONTRACT;
CacheFriendAssemblyInfo();
if (m_pFriendAssemblyDescriptor == NO_FRIEND_ASSEMBLIES_MARKER)
{
return false;
}
return m_pFriendAssemblyDescriptor->GrantsFriendAccessTo(pAccessingAssembly, pMD);
}
bool Assembly::GrantsFriendAccessTo(Assembly *pAccessingAssembly, MethodTable *pMT)
{
WRAPPER_NO_CONTRACT;
CacheFriendAssemblyInfo();
if (m_pFriendAssemblyDescriptor == NO_FRIEND_ASSEMBLIES_MARKER)
{
return false;
}
return m_pFriendAssemblyDescriptor->GrantsFriendAccessTo(pAccessingAssembly, pMT);
}
bool Assembly::IgnoresAccessChecksTo(Assembly *pAccessedAssembly)
{
CONTRACTL
{
THROWS;
GC_TRIGGERS;
PRECONDITION(CheckPointer(pAccessedAssembly));
}
CONTRACTL_END;
CacheFriendAssemblyInfo();
if (m_pFriendAssemblyDescriptor == NO_FRIEND_ASSEMBLIES_MARKER)
{
return false;
}
if (pAccessedAssembly->IsDisabledPrivateReflection())
{
return false;
}
return m_pFriendAssemblyDescriptor->IgnoresAccessChecksTo(pAccessedAssembly);
}
#ifndef CROSSGEN_COMPILE
enum CorEntryPointType
{
EntryManagedMain, // void main(String[])
EntryCrtMain // unsigned main(void)
};
void DECLSPEC_NORETURN ThrowMainMethodException(MethodDesc* pMD, UINT resID)
{
CONTRACTL
{
THROWS;
GC_TRIGGERS;
MODE_ANY;
INJECT_FAULT(COMPlusThrowOM());
}
CONTRACTL_END;
DefineFullyQualifiedNameForClassW();
LPCWSTR szClassName = GetFullyQualifiedNameForClassW(pMD->GetMethodTable());
LPCUTF8 szUTFMethodName;
if (FAILED(pMD->GetMDImport()->GetNameOfMethodDef(pMD->GetMemberDef(), &szUTFMethodName)))
{
szUTFMethodName = "Invalid MethodDef record";
}
PREFIX_ASSUME(szUTFMethodName!=NULL);
MAKE_WIDEPTR_FROMUTF8(szMethodName, szUTFMethodName);
COMPlusThrowHR(COR_E_METHODACCESS, resID, szClassName, szMethodName);
}
// Returns true if this is a valid main method?
void ValidateMainMethod(MethodDesc * pFD, CorEntryPointType *pType)
{
CONTRACTL
{
THROWS;
GC_TRIGGERS;
MODE_ANY;
INJECT_FAULT(COMPlusThrowOM());
PRECONDITION(CheckPointer(pType));
}
CONTRACTL_END;
// Must be static, but we don't care about accessibility
if ((pFD->GetAttrs() & mdStatic) == 0)
ThrowMainMethodException(pFD, IDS_EE_MAIN_METHOD_MUST_BE_STATIC);
if (pFD->GetNumGenericClassArgs() != 0 || pFD->GetNumGenericMethodArgs() != 0)
ThrowMainMethodException(pFD, IDS_EE_LOAD_BAD_MAIN_SIG);
// Check for types
SigPointer sig(pFD->GetSigPointer());
ULONG nCallConv;
if (FAILED(sig.GetData(&nCallConv)))
ThrowMainMethodException(pFD, BFA_BAD_SIGNATURE);
if (nCallConv != IMAGE_CEE_CS_CALLCONV_DEFAULT)
ThrowMainMethodException(pFD, IDS_EE_LOAD_BAD_MAIN_SIG);
ULONG nParamCount;
if (FAILED(sig.GetData(&nParamCount)))
ThrowMainMethodException(pFD, BFA_BAD_SIGNATURE);
CorElementType nReturnType;
if (FAILED(sig.GetElemType(&nReturnType)))
ThrowMainMethodException(pFD, BFA_BAD_SIGNATURE);
if ((nReturnType != ELEMENT_TYPE_VOID) && (nReturnType != ELEMENT_TYPE_I4) && (nReturnType != ELEMENT_TYPE_U4))
ThrowMainMethodException(pFD, IDS_EE_MAIN_METHOD_HAS_INVALID_RTN);
if (nParamCount == 0)
*pType = EntryCrtMain;
else {
*pType = EntryManagedMain;
if (nParamCount != 1)
ThrowMainMethodException(pFD, IDS_EE_TO_MANY_ARGUMENTS_IN_MAIN);
CorElementType argType;
CorElementType argType2 = ELEMENT_TYPE_END;
if (FAILED(sig.GetElemType(&argType)))
ThrowMainMethodException(pFD, BFA_BAD_SIGNATURE);
if (argType == ELEMENT_TYPE_SZARRAY)
if (FAILED(sig.GetElemType(&argType2)))
ThrowMainMethodException(pFD, BFA_BAD_SIGNATURE);
if (argType != ELEMENT_TYPE_SZARRAY || argType2 != ELEMENT_TYPE_STRING)
ThrowMainMethodException(pFD, IDS_EE_LOAD_BAD_MAIN_SIG);
}
}
/* static */
HRESULT RunMain(MethodDesc *pFD ,
short numSkipArgs,
INT32 *piRetVal,
PTRARRAYREF *stringArgs /*=NULL*/)
{
STATIC_CONTRACT_THROWS;
_ASSERTE(piRetVal);
DWORD cCommandArgs = 0; // count of args on command line
LPWSTR *wzArgs = NULL; // command line args
HRESULT hr = S_OK;
*piRetVal = -1;
// The exit code for the process is communicated in one of two ways. If the
// entrypoint returns an 'int' we take that. Otherwise we take a latched
// process exit code. This can be modified by the app via setting
// Environment's ExitCode property.
//
// When we're executing the default exe main in the default domain, set the latched exit code to
// zero as a default. If it gets set to something else by user code then that value will be returned.
//
// StringArgs appears to be non-null only when the main method is explicitly invoked via the hosting api
// or through creating a subsequent domain and running an exe within it. In those cases we don't
// want to reset the (global) latched exit code.
if (stringArgs == NULL)
SetLatchedExitCode(0);
if (!pFD) {
_ASSERTE(!"Must have a function to call!");
return E_FAIL;
}
CorEntryPointType EntryType = EntryManagedMain;
ValidateMainMethod(pFD, &EntryType);
if ((EntryType == EntryManagedMain) &&
(stringArgs == NULL)) {
return E_INVALIDARG;
}
ETWFireEvent(Main_V1);
struct Param
{
MethodDesc *pFD;
short numSkipArgs;
INT32 *piRetVal;
PTRARRAYREF *stringArgs;
CorEntryPointType EntryType;
DWORD cCommandArgs;
LPWSTR *wzArgs;
} param;
param.pFD = pFD;
param.numSkipArgs = numSkipArgs;
param.piRetVal = piRetVal;
param.stringArgs = stringArgs;
param.EntryType = EntryType;
param.cCommandArgs = cCommandArgs;
param.wzArgs = wzArgs;
EX_TRY_NOCATCH(Param *, pParam, &param)
{
MethodDescCallSite threadStart(pParam->pFD);
PTRARRAYREF StrArgArray = NULL;
GCPROTECT_BEGIN(StrArgArray);
// Build the parameter array and invoke the method.
if (pParam->EntryType == EntryManagedMain) {
if (pParam->stringArgs == NULL) {
// Allocate a COM Array object with enough slots for cCommandArgs - 1
StrArgArray = (PTRARRAYREF) AllocateObjectArray((pParam->cCommandArgs - pParam->numSkipArgs), g_pStringClass);
// Create Stringrefs for each of the args
for (DWORD arg = pParam->numSkipArgs; arg < pParam->cCommandArgs; arg++) {
STRINGREF sref = StringObject::NewString(pParam->wzArgs[arg]);
StrArgArray->SetAt(arg - pParam->numSkipArgs, (OBJECTREF) sref);
}
}
else
StrArgArray = *pParam->stringArgs;
}
ARG_SLOT stackVar = ObjToArgSlot(StrArgArray);
if (pParam->pFD->IsVoid())
{
// Set the return value to 0 instead of returning random junk
*pParam->piRetVal = 0;
threadStart.Call(&stackVar);
}
else
{
*pParam->piRetVal = (INT32)threadStart.Call_RetArgSlot(&stackVar);
SetLatchedExitCode(*pParam->piRetVal);
}
GCPROTECT_END();
//<TODO>
// When we get mainCRTStartup from the C++ then this should be able to go away.</TODO>
fflush(stdout);
fflush(stderr);
}
EX_END_NOCATCH
ETWFireEvent(MainEnd_V1);
return hr;
}
static void RunMainPre()
{
LIMITED_METHOD_CONTRACT;
_ASSERTE(GetThread() != 0);
g_fWeControlLifetime = TRUE;
}
static void RunMainPost()
{
CONTRACTL
{
THROWS;
GC_TRIGGERS;
MODE_ANY;
INJECT_FAULT(COMPlusThrowOM(););
PRECONDITION(CheckPointer(GetThread()));
}
CONTRACTL_END
GCX_PREEMP();
ThreadStore::s_pThreadStore->WaitForOtherThreads();
DWORD dwSecondsToSleep = g_pConfig->GetSleepOnExit();
// if dwSeconds is non-zero then we will sleep for that many seconds
// before we exit this allows the vaDumpCmd to detect that our process
// has gone idle and this allows us to get a vadump of our process at
// this point in it's execution
//
if (dwSecondsToSleep != 0)
{
ClrSleepEx(dwSecondsToSleep * 1000, FALSE);
}
}
INT32 Assembly::ExecuteMainMethod(PTRARRAYREF *stringArgs, BOOL waitForOtherThreads)
{
CONTRACTL
{
INSTANCE_CHECK;
THROWS;
GC_TRIGGERS;
MODE_ANY;
ENTRY_POINT;
INJECT_FAULT(COMPlusThrowOM());
}
CONTRACTL_END;
// reset the error code for std C
errno=0;
HRESULT hr = S_OK;
INT32 iRetVal = 0;
BEGIN_ENTRYPOINT_THROWS;
Thread *pThread = GetThread();
MethodDesc *pMeth;
{
// This thread looks like it wandered in -- but actually we rely on it to keep the process alive.
pThread->SetBackground(FALSE);
GCX_COOP();
pMeth = GetEntryPoint();
if (pMeth) {
{
#ifdef FEATURE_COMINTEROP
GCX_PREEMP();
Thread::ApartmentState state = Thread::AS_Unknown;
state = SystemDomain::GetEntryPointThreadAptState(pMeth->GetMDImport(), pMeth->GetMemberDef());
SystemDomain::SetThreadAptState(state);
#endif // FEATURE_COMINTEROP
}
RunMainPre();
// Set the root assembly as the assembly that is containing the main method
// The root assembly is used in the GetEntryAssembly method that on CoreCLR is used
// to get the TargetFrameworkMoniker for the app
AppDomain * pDomain = pThread->GetDomain();
pDomain->SetRootAssembly(pMeth->GetAssembly());
hr = RunMain(pMeth, 1, &iRetVal, stringArgs);
}
}
//RunMainPost is supposed to be called on the main thread of an EXE,
//after that thread has finished doing useful work. It contains logic
//to decide when the process should get torn down. So, don't call it from
// AppDomain.ExecuteAssembly()
if (pMeth) {
if (waitForOtherThreads)
RunMainPost();
}
else {
StackSString displayName;
GetDisplayName(displayName);
COMPlusThrowHR(COR_E_MISSINGMETHOD, IDS_EE_FAILED_TO_FIND_MAIN, displayName);
}
IfFailThrow(hr);
END_ENTRYPOINT_THROWS;
return iRetVal;
}
#endif // CROSSGEN_COMPILE
MethodDesc* Assembly::GetEntryPoint()
{
CONTRACT(MethodDesc*)
{
THROWS;
INJECT_FAULT(COMPlusThrowOM(););
MODE_ANY;
// Can return NULL if no entry point.
POSTCONDITION(CheckPointer(RETVAL, NULL_OK));
}
CONTRACT_END;
if (m_pEntryPoint)
RETURN m_pEntryPoint;
mdToken mdEntry = m_pManifestFile->GetEntryPointToken();
if (IsNilToken(mdEntry))
RETURN NULL;
Module *pModule = NULL;
switch(TypeFromToken(mdEntry)) {
case mdtFile:
pModule = m_pManifest->LoadModule(::GetAppDomain(), mdEntry, FALSE)->GetModule();
mdEntry = pModule->GetEntryPointToken();
if ( (TypeFromToken(mdEntry) != mdtMethodDef) ||
(!pModule->GetMDImport()->IsValidToken(mdEntry)) )
pModule = NULL;
break;
case mdtMethodDef:
if (m_pManifestFile->GetPersistentMDImport()->IsValidToken(mdEntry))
pModule = m_pManifest;
break;
}
// May be unmanaged entrypoint
if (!pModule)
RETURN NULL;
// We need to get its properties and the class token for this MethodDef token.
mdToken mdParent;
if (FAILED(pModule->GetMDImport()->GetParentToken(mdEntry, &mdParent))) {
StackSString displayName;
GetDisplayName(displayName);
COMPlusThrowHR(COR_E_BADIMAGEFORMAT, IDS_EE_ILLEGAL_TOKEN_FOR_MAIN, displayName);
}
// For the entrypoint, also validate if the paramList is valid or not. We do this check
// by asking for the return-value (sequence 0) parameter to MDInternalRO::FindParamOfMethod.
// Incase the parameter list is invalid, CLDB_E_FILE_CORRUPT will be returned
// byMDInternalRO::FindParamOfMethod and we will bail out.
//
// If it does not exist (return value CLDB_E_RECORD_NOTFOUND) or if it is found (S_OK),
// we do not bother as the values would have come upon ensuring a valid parameter record
// list.
mdParamDef pdParam;
HRESULT hrValidParamList = pModule->GetMDImport()->FindParamOfMethod(mdEntry, 0, &pdParam);
if (hrValidParamList == CLDB_E_FILE_CORRUPT)
{
// Throw an exception for bad_image_format (because of corrupt metadata)
StackSString displayName;
GetDisplayName(displayName);
COMPlusThrowHR(COR_E_BADIMAGEFORMAT, IDS_EE_ILLEGAL_TOKEN_FOR_MAIN, displayName);
}
if (mdParent != COR_GLOBAL_PARENT_TOKEN) {
GCX_COOP();
// This code needs a class init frame, because without it, the
// debugger will assume any code that results from searching for a
// type handle (ie, loading an assembly) is the first line of a program.
FrameWithCookie<DebuggerClassInitMarkFrame> __dcimf;
MethodTable * pInitialMT = ClassLoader::LoadTypeDefOrRefThrowing(pModule, mdParent,
ClassLoader::ThrowIfNotFound,
ClassLoader::FailIfUninstDefOrRef).GetMethodTable();
m_pEntryPoint = MemberLoader::FindMethod(pInitialMT, mdEntry);
__dcimf.Pop();
}
else
{
m_pEntryPoint = pModule->FindMethod(mdEntry);
}
RETURN m_pEntryPoint;
}
#ifndef CROSSGEN_COMPILE
OBJECTREF Assembly::GetExposedObject()
{
CONTRACT(OBJECTREF)
{
GC_TRIGGERS;
THROWS;
INJECT_FAULT(COMPlusThrowOM(););
MODE_COOPERATIVE;
}
CONTRACT_END;
RETURN GetDomainAssembly()->GetExposedAssemblyObject();
}
#endif // CROSSGEN_COMPILE
/* static */
BOOL Assembly::FileNotFound(HRESULT hr)
{
LIMITED_METHOD_CONTRACT;
return IsHRESULTForExceptionKind(hr, kFileNotFoundException) ||
#ifdef FEATURE_COMINTEROP
(hr == RO_E_METADATA_NAME_NOT_FOUND) ||
#endif //FEATURE_COMINTEROP
(hr == CLR_E_BIND_TYPE_NOT_FOUND);
}
BOOL Assembly::GetResource(LPCSTR szName, DWORD *cbResource,
PBYTE *pbInMemoryResource, Assembly** pAssemblyRef,
LPCSTR *szFileName, DWORD *dwLocation,
StackCrawlMark *pStackMark, BOOL fSkipSecurityCheck,
BOOL fSkipRaiseResolveEvent)
{
CONTRACTL
{
THROWS;
GC_TRIGGERS;
INJECT_FAULT(COMPlusThrowOM(););
}
CONTRACTL_END;
DomainAssembly *pAssembly = NULL;
BOOL result = GetDomainAssembly()->GetResource(szName, cbResource,
pbInMemoryResource, &pAssembly,
szFileName, dwLocation, pStackMark, fSkipSecurityCheck,
fSkipRaiseResolveEvent);
if (result && pAssemblyRef != NULL && pAssembly!=NULL)
*pAssemblyRef = pAssembly->GetAssembly();
return result;
}
#ifdef FEATURE_PREJIT
BOOL Assembly::IsInstrumented()
{
STATIC_CONTRACT_THROWS;
STATIC_CONTRACT_GC_TRIGGERS;
STATIC_CONTRACT_FAULT;
// This will set the value of m_isInstrumentedStatus by calling IsInstrumentedHelper()
// that method performs string pattern matching using the Config value of ZapBBInstr
// We cache the value returned from that method in m_isInstrumentedStatus
//
if (m_isInstrumentedStatus == IS_INSTRUMENTED_UNSET)
{
EX_TRY
{
FAULT_NOT_FATAL();
if (IsInstrumentedHelper())
{
m_isInstrumentedStatus = IS_INSTRUMENTED_TRUE;
}
else
{
m_isInstrumentedStatus = IS_INSTRUMENTED_FALSE;
}
}
EX_CATCH
{
m_isInstrumentedStatus = IS_INSTRUMENTED_FALSE;
}
EX_END_CATCH(RethrowTerminalExceptions);
}
// At this point m_isInstrumentedStatus can't have the value of IS_INSTRUMENTED_UNSET
_ASSERTE(m_isInstrumentedStatus != IS_INSTRUMENTED_UNSET);
return (m_isInstrumentedStatus == IS_INSTRUMENTED_TRUE);
}
BOOL Assembly::IsInstrumentedHelper()
{
STATIC_CONTRACT_THROWS;
STATIC_CONTRACT_GC_TRIGGERS;
STATIC_CONTRACT_FAULT;
// Dynamic Assemblies cannot be instrumented
if (IsDynamic())
return false;
// We must have a native image in order to perform IBC instrumentation
if (!GetManifestFile()->HasNativeOrReadyToRunImage())
return false;
// @Consider using the full name instead of the short form
// (see GetFusionAssemblyName()->IsEqual).
LPCUTF8 szZapBBInstr = g_pConfig->GetZapBBInstr();
LPCUTF8 szAssemblyName = GetSimpleName();
if (!szZapBBInstr || !szAssemblyName ||
(*szZapBBInstr == '\0') || (*szAssemblyName == '\0'))
return false;
// Convert to unicode so that we can do a case insensitive comparison
SString instrumentedAssemblyNamesList(SString::Utf8, szZapBBInstr);
SString assemblyName(SString::Utf8, szAssemblyName);
const WCHAR *wszInstrumentedAssemblyNamesList = instrumentedAssemblyNamesList.GetUnicode();
const WCHAR *wszAssemblyName = assemblyName.GetUnicode();
// wszInstrumentedAssemblyNamesList is a space separated list of assembly names.
// We need to determine if wszAssemblyName is in this list.
// If there is a "*" in the list, then all assemblies match.
const WCHAR * pCur = wszInstrumentedAssemblyNamesList;
do
{
_ASSERTE(pCur[0] != W('\0'));
const WCHAR * pNextSpace = wcschr(pCur, W(' '));
_ASSERTE(pNextSpace == NULL || pNextSpace[0] == W(' '));
if (pCur != pNextSpace)
{
// pCur is not pointing to a space
_ASSERTE(pCur[0] != W(' '));
if (pCur[0] == W('*') && (pCur[1] == W(' ') || pCur[1] == W('\0')))
return true;
if (pNextSpace == NULL)
{
// We have reached the last name in the list. There are no more spaces.
return (SString::_wcsicmp(wszAssemblyName, pCur) == 0);
}
else
{
if (SString::_wcsnicmp(wszAssemblyName, pCur, static_cast<COUNT_T>(pNextSpace - pCur)) == 0)
return true;
}
}
pCur = pNextSpace + 1;
}
while (pCur[0] != W('\0'));
return false;
}
#endif // FEATURE_PREJIT
//***********************************************************
// Add an assembly to the assemblyref list. pAssemEmitter specifies where
// the AssemblyRef is emitted to.
//***********************************************************
mdAssemblyRef Assembly::AddAssemblyRef(Assembly *refedAssembly, IMetaDataAssemblyEmit *pAssemEmitter, BOOL fUsePublicKeyToken)
{
CONTRACT(mdAssemblyRef)
{
THROWS;
GC_TRIGGERS;
INJECT_FAULT(COMPlusThrowOM(););
PRECONDITION(CheckPointer(refedAssembly));
PRECONDITION(CheckPointer(pAssemEmitter, NULL_NOT_OK));
POSTCONDITION(!IsNilToken(RETVAL));
POSTCONDITION(TypeFromToken(RETVAL) == mdtAssemblyRef);
}
CONTRACT_END;
SafeComHolder<IMetaDataAssemblyEmit> emitHolder;
AssemblySpec spec;
spec.InitializeSpec(refedAssembly->GetManifestFile());
if (refedAssembly->IsCollectible())
{
if (this->IsCollectible())
this->GetLoaderAllocator()->EnsureReference(refedAssembly->GetLoaderAllocator());
else
COMPlusThrow(kNotSupportedException, W("NotSupported_CollectibleBoundNonCollectible"));
}
mdAssemblyRef ar;
IfFailThrow(spec.EmitToken(pAssemEmitter, &ar, fUsePublicKeyToken));
RETURN ar;
} // Assembly::AddAssemblyRef
//***********************************************************
// Add a typedef to the runtime TypeDef table of this assembly
//***********************************************************
void Assembly::AddType(
Module *pModule,
mdTypeDef cl)
{
CONTRACTL
{
THROWS;
GC_TRIGGERS;
INJECT_FAULT(COMPlusThrowOM(););
}
CONTRACTL_END
AllocMemTracker amTracker;
if (pModule->GetAssembly() != this)
{
// you cannot add a typedef outside of the assembly to the typedef table
_ASSERTE(!"Bad usage!");
}
m_pClassLoader->AddAvailableClassDontHaveLock(pModule,
cl,
&amTracker);
amTracker.SuppressRelease();
}
//***********************************************************
// Add an ExportedType to the runtime TypeDef table of this assembly
//***********************************************************
void Assembly::AddExportedType(mdExportedType cl)
{
CONTRACTL
{
THROWS;
GC_TRIGGERS;
INJECT_FAULT(COMPlusThrowOM(););
}
CONTRACTL_END
AllocMemTracker amTracker;
m_pClassLoader->AddExportedTypeDontHaveLock(GetManifestModule(),
cl,
&amTracker);
amTracker.SuppressRelease();
}
HRESULT STDMETHODCALLTYPE
GetAssembliesByName(LPCWSTR szAppBase,
LPCWSTR szPrivateBin,
LPCWSTR szAssemblyName,
IUnknown *ppIUnk[],
ULONG cMax,
ULONG *pcAssemblies)
{
CONTRACTL
{
NOTHROW;
MODE_PREEMPTIVE;
GC_TRIGGERS;
INJECT_FAULT(return E_OUTOFMEMORY;);
}
CONTRACTL_END
HRESULT hr = S_OK;
if (g_fEEInit) {
// Cannot call this during EE startup
return MSEE_E_ASSEMBLYLOADINPROGRESS;
}
if (!(szAssemblyName && ppIUnk && pcAssemblies))
return E_POINTER;
hr = COR_E_NOTSUPPORTED;
return hr;
}// Used by the IMetadata API's to access an assemblies metadata.
#ifdef FEATURE_LOADER_OPTIMIZATION
void Assembly::SetMissingDependenciesCheckDone()
{
LIMITED_METHOD_CONTRACT;
m_bMissingDependenciesCheckDone=TRUE;
};
BOOL Assembly::MissingDependenciesCheckDone()
{
LIMITED_METHOD_CONTRACT;
return m_bMissingDependenciesCheckDone;
};
BOOL Assembly::CanBeShared(DomainAssembly *pDomainAssembly)
{
PTR_PEAssembly pFile=pDomainAssembly->GetFile();
if(pFile == NULL)
return FALSE;
if(pFile->IsDynamic())
return FALSE;
if(IsSystem() && pFile->IsSystem())
return TRUE;
if ((pDomainAssembly->GetDebuggerInfoBits()&~(DACF_PDBS_COPIED|DACF_IGNORE_PDBS|DACF_OBSOLETE_TRACK_JIT_INFO))
!= (m_debuggerFlags&~(DACF_PDBS_COPIED|DACF_IGNORE_PDBS|DACF_OBSOLETE_TRACK_JIT_INFO)))
{
LOG((LF_CODESHARING,
LL_INFO100,
"We can't share it, desired debugging flags %x are different than %x\n",
pDomainAssembly->GetDebuggerInfoBits(), (m_debuggerFlags&~(DACF_PDBS_COPIED|DACF_IGNORE_PDBS|DACF_OBSOLETE_TRACK_JIT_INFO))));
STRESS_LOG2(LF_CODESHARING, LL_INFO100,"Flags diff= %08x [%08x/%08x]",pDomainAssembly->GetDebuggerInfoBits(),
m_debuggerFlags);
return FALSE;
}
return TRUE;
}
#endif // FEATURE_LOADER_OPTIMIZATION
void DECLSPEC_NORETURN Assembly::ThrowTypeLoadException(LPCUTF8 pszFullName, UINT resIDWhy)
{
WRAPPER_NO_CONTRACT;
ThrowTypeLoadException(NULL, pszFullName, NULL,
resIDWhy);
}
void DECLSPEC_NORETURN Assembly::ThrowTypeLoadException(LPCUTF8 pszNameSpace, LPCUTF8 pszTypeName,
UINT resIDWhy)
{
WRAPPER_NO_CONTRACT;
ThrowTypeLoadException(pszNameSpace, pszTypeName, NULL,
resIDWhy);
}
void DECLSPEC_NORETURN Assembly::ThrowTypeLoadException(NameHandle *pName, UINT resIDWhy)
{
STATIC_CONTRACT_THROWS;
if (pName->GetName()) {
ThrowTypeLoadException(pName->GetNameSpace(),
pName->GetName(),
NULL,
resIDWhy);
}
else
ThrowTypeLoadException(pName->GetTypeModule()->GetMDImport(),
pName->GetTypeToken(),
resIDWhy);
}
void DECLSPEC_NORETURN Assembly::ThrowTypeLoadException(IMDInternalImport *pInternalImport,
mdToken token,
UINT resIDWhy)
{
WRAPPER_NO_CONTRACT;
ThrowTypeLoadException(pInternalImport, token, NULL, resIDWhy);
}
void DECLSPEC_NORETURN Assembly::ThrowTypeLoadException(IMDInternalImport *pInternalImport,
mdToken token,
LPCUTF8 pszFieldOrMethodName,
UINT resIDWhy)
{
STATIC_CONTRACT_THROWS;
char pszBuff[32];
LPCUTF8 pszClassName = (LPCUTF8)pszBuff;
LPCUTF8 pszNameSpace = "Invalid_Token";
if(pInternalImport->IsValidToken(token))
{
switch (TypeFromToken(token)) {
case mdtTypeRef:
if (FAILED(pInternalImport->GetNameOfTypeRef(token, &pszNameSpace, &pszClassName)))
{
pszNameSpace = pszClassName = "Invalid TypeRef record";
}
break;
case mdtTypeDef:
if (FAILED(pInternalImport->GetNameOfTypeDef(token, &pszClassName, &pszNameSpace)))
{
pszNameSpace = pszClassName = "Invalid TypeDef record";
}
break;
case mdtTypeSpec:
// If you see this assert, you need to make sure the message for
// this resID is appropriate for TypeSpecs
_ASSERTE((resIDWhy == IDS_CLASSLOAD_GENERAL) ||
(resIDWhy == IDS_CLASSLOAD_BADFORMAT) ||
(resIDWhy == IDS_CLASSLOAD_TYPESPEC));
resIDWhy = IDS_CLASSLOAD_TYPESPEC;
}
}
else
sprintf_s(pszBuff, sizeof(pszBuff), "0x%8.8X", token);
ThrowTypeLoadException(pszNameSpace, pszClassName,
pszFieldOrMethodName, resIDWhy);
}
void DECLSPEC_NORETURN Assembly::ThrowTypeLoadException(LPCUTF8 pszNameSpace,
LPCUTF8 pszTypeName,
LPCUTF8 pszMethodName,
UINT resIDWhy)
{
STATIC_CONTRACT_THROWS;
StackSString displayName;
GetDisplayName(displayName);
::ThrowTypeLoadException(pszNameSpace, pszTypeName, displayName,
pszMethodName, resIDWhy);
}
void DECLSPEC_NORETURN Assembly::ThrowBadImageException(LPCUTF8 pszNameSpace,
LPCUTF8 pszTypeName,
UINT resIDWhy)
{
STATIC_CONTRACT_THROWS;
StackSString displayName;
GetDisplayName(displayName);
StackSString fullName;
SString sNameSpace(SString::Utf8, pszNameSpace);
SString sTypeName(SString::Utf8, pszTypeName);
fullName.MakeFullNamespacePath(sNameSpace, sTypeName);
COMPlusThrowHR(COR_E_BADIMAGEFORMAT, resIDWhy, fullName, displayName);
}
#ifdef FEATURE_COMINTEROP
//
// Manage an ITypeLib pointer for this Assembly.
//
ITypeLib* Assembly::GetTypeLib()
{
CONTRACTL
{
NOTHROW;
GC_TRIGGERS;
FORBID_FAULT;
}
CONTRACTL_END
// Get the value we are going to return.
ITypeLib *pResult = m_pITypeLib;
// If there is a value, AddRef() it.
if (pResult && pResult != (ITypeLib*)-1)
pResult->AddRef();
return pResult;
} // ITypeLib* Assembly::GetTypeLib()
void Assembly::SetTypeLib(ITypeLib *pNew)
{
CONTRACTL
{
NOTHROW;
GC_TRIGGERS;
FORBID_FAULT;
}
CONTRACTL_END
ITypeLib *pOld;
pOld = InterlockedExchangeT(&m_pITypeLib, pNew);
// TypeLibs are refcounted pointers.
if (pNew != pOld)
{
if (pNew && pNew != (ITypeLib*)-1)
pNew->AddRef();
if (pOld && pOld != (ITypeLib*)-1)
pOld->Release();
}
} // void Assembly::SetTypeLib()
Assembly::WinMDStatus Assembly::GetWinMDStatus()
{
LIMITED_METHOD_CONTRACT;
if (m_winMDStatus == WinMDStatus_Unknown)
{
IWinMDImport *pWinMDImport = GetManifestWinMDImport();
if (pWinMDImport != NULL)
{
BOOL bIsWinMDExp;
VERIFY(SUCCEEDED(pWinMDImport->IsScenarioWinMDExp(&bIsWinMDExp)));
if (bIsWinMDExp)
{
// this is a managed backed WinMD
m_winMDStatus = WinMDStatus_IsManagedWinMD;
}
else
{
// this is a pure WinMD
m_winMDStatus = WinMDStatus_IsPureWinMD;
}
}
else
{
// this is not a WinMD at all
m_winMDStatus = WinMDStatus_IsNotWinMD;
}
}
return m_winMDStatus;
}
bool Assembly::IsWinMD()
{
LIMITED_METHOD_CONTRACT;
return GetWinMDStatus() != WinMDStatus_IsNotWinMD;
}
bool Assembly::IsManagedWinMD()
{
LIMITED_METHOD_CONTRACT;
return GetWinMDStatus() == WinMDStatus_IsManagedWinMD;
}
IWinMDImport *Assembly::GetManifestWinMDImport()
{
LIMITED_METHOD_CONTRACT;
if (m_pManifestWinMDImport == NULL)
{
ReleaseHolder<IWinMDImport> pWinMDImport;
if (SUCCEEDED(m_pManifest->GetMDImport()->QueryInterface(IID_IWinMDImport, (void **)&pWinMDImport)))
{
if (InterlockedCompareExchangeT<IWinMDImport *>(&m_pManifestWinMDImport, pWinMDImport, NULL) == NULL)
{
pWinMDImport.SuppressRelease();
}
}
}
return m_pManifestWinMDImport;
}
#endif // FEATURE_COMINTEROP
#endif // #ifndef DACCESS_COMPILE
#ifndef DACCESS_COMPILE
void Assembly::EnsureActive()
{
CONTRACTL
{
THROWS;
GC_TRIGGERS;
INJECT_FAULT(COMPlusThrowOM(););
}
CONTRACTL_END;
GetDomainAssembly()->EnsureActive();
}
#endif //!DACCESS_COMPILE
CHECK Assembly::CheckActivated()
{
#ifndef DACCESS_COMPILE
WRAPPER_NO_CONTRACT;
CHECK(GetDomainAssembly()->CheckActivated());
#endif
CHECK_OK;
}
#ifdef DACCESS_COMPILE
void
Assembly::EnumMemoryRegions(CLRDataEnumMemoryFlags flags)
{
WRAPPER_NO_CONTRACT;
SUPPORTS_DAC;
// We don't need Assembly info in triage dumps.
if (flags == CLRDATA_ENUM_MEM_TRIAGE)
{
return;
}
DAC_ENUM_DTHIS();
EMEM_OUT(("MEM: %p Assembly\n", dac_cast<TADDR>(this)));
if (m_pDomain.IsValid())
{
m_pDomain->EnumMemoryRegions(flags, true);
}
if (m_pClassLoader.IsValid())
{
m_pClassLoader->EnumMemoryRegions(flags);
}
if (m_pManifest.IsValid())
{
m_pManifest->EnumMemoryRegions(flags, true);
}
if (m_pManifestFile.IsValid())
{
m_pManifestFile->EnumMemoryRegions(flags);
}
}
#endif
#ifndef DACCESS_COMPILE
FriendAssemblyDescriptor::FriendAssemblyDescriptor()
{
}
FriendAssemblyDescriptor::~FriendAssemblyDescriptor()
{
CONTRACTL
{
DESTRUCTOR_CHECK;
}
CONTRACTL_END;
ArrayList::Iterator itFullAccessAssemblies = m_alFullAccessFriendAssemblies.Iterate();
while (itFullAccessAssemblies.Next())
{
FriendAssemblyName_t *pFriendAssemblyName = static_cast<FriendAssemblyName_t *>(itFullAccessAssemblies.GetElement());
delete pFriendAssemblyName;
}
}
//---------------------------------------------------------------------------------------
//
// Builds a FriendAssemblyDescriptor for a given assembly
//
// Arguments:
// pAssembly - assembly to get friend assembly information for
//
// Return Value:
// A friend assembly descriptor if the assembly declares any friend assemblies, otherwise NULL
//
// static
FriendAssemblyDescriptor *FriendAssemblyDescriptor::CreateFriendAssemblyDescriptor(PEAssembly *pAssembly)
{
CONTRACTL
{
THROWS;
GC_TRIGGERS;
PRECONDITION(CheckPointer(pAssembly));
}
CONTRACTL_END
NewHolder<FriendAssemblyDescriptor> pFriendAssemblies = new FriendAssemblyDescriptor;
// We're going to do this twice, once for InternalsVisibleTo and once for IgnoresAccessChecks
ReleaseHolder<IMDInternalImport> pImport(pAssembly->GetMDImportWithRef());
for(int count = 0 ; count < 2 ; ++count)
{
_ASSERTE(pImport != NULL);
MDEnumHolder hEnum(pImport);
HRESULT hr = S_OK;
if (count == 0)
{
hr = pImport->EnumCustomAttributeByNameInit(TokenFromRid(1, mdtAssembly), FRIEND_ASSEMBLY_TYPE, &hEnum);
}
else
{
hr = pImport->EnumCustomAttributeByNameInit(TokenFromRid(1, mdtAssembly), SUBJECT_ASSEMBLY_TYPE, &hEnum);
}
IfFailThrow(hr);
// Nothing to do if there are no attributes
if (hr == S_FALSE)
{
continue;
}
// Enumerate over the declared friends
mdCustomAttribute tkAttribute;
while (pImport->EnumNext(&hEnum, &tkAttribute))
{
// Get raw custom attribute.
const BYTE *pbAttr = NULL; // Custom attribute data as a BYTE*.
ULONG cbAttr = 0; // Size of custom attribute data.
if (FAILED(pImport->GetCustomAttributeAsBlob(tkAttribute, reinterpret_cast<const void **>(&pbAttr), &cbAttr)))
{
THROW_BAD_FORMAT(BFA_INVALID_TOKEN, pAssembly);
}
CustomAttributeParser cap(pbAttr, cbAttr);
if (FAILED(cap.ValidateProlog()))
{
THROW_BAD_FORMAT(BFA_BAD_CA_HEADER, pAssembly);
}
// Get the name of the friend assembly.
LPCUTF8 szString;
ULONG cbString;
if (FAILED(cap.GetNonNullString(&szString, &cbString)))
{
THROW_BAD_FORMAT(BFA_BAD_CA_HEADER, pAssembly);
}
// Convert the string to Unicode.
StackSString displayName(SString::Utf8, szString, cbString);
// Create an AssemblyNameObject from the string.
FriendAssemblyNameHolder pFriendAssemblyName;
StackScratchBuffer buffer;
pFriendAssemblyName = new FriendAssemblyName_t;
hr = pFriendAssemblyName->Init(displayName.GetUTF8(buffer));
if (SUCCEEDED(hr))
{
hr = pFriendAssemblyName->CheckFriendAssemblyName();
}
if (FAILED(hr))
{
THROW_HR_ERROR_WITH_INFO(hr, pAssembly);
}
if (count == 1)
{
pFriendAssemblies->AddSubjectAssembly(pFriendAssemblyName);
pFriendAssemblyName.SuppressRelease();
// Below checks are unnecessary for IgnoresAccessChecks
continue;
}
// CoreCLR does not have a valid scenario for strong-named assemblies requiring their dependencies
// to be strong-named as well.
pFriendAssemblies->AddFriendAssembly(pFriendAssemblyName);
pFriendAssemblyName.SuppressRelease();
}
}
pFriendAssemblies.SuppressRelease();
return pFriendAssemblies.Extract();
}
//---------------------------------------------------------------------------------------
//
// Adds an assembly to the list of friend assemblies for this descriptor
//
// Arguments:
// pFriendAssembly - friend assembly to add to the list
// fAllInternalsVisible - true if all internals are visible to the friend, false if only specifically
// marked internals are visible
//
// Notes:
// This method takes ownership of the friend assembly name. It is not thread safe and does not check to
// see if an assembly has already been added to the friend assembly list.
//
void FriendAssemblyDescriptor::AddFriendAssembly(FriendAssemblyName_t *pFriendAssembly)
{
CONTRACTL
{
THROWS;
GC_TRIGGERS;
PRECONDITION(CheckPointer(pFriendAssembly));
}
CONTRACTL_END
m_alFullAccessFriendAssemblies.Append(pFriendAssembly);
}
void FriendAssemblyDescriptor::AddSubjectAssembly(FriendAssemblyName_t *pFriendAssembly)
{
CONTRACTL
{
THROWS;
GC_TRIGGERS;
PRECONDITION(CheckPointer(pFriendAssembly));
}
CONTRACTL_END
m_subjectAssemblies.Append(pFriendAssembly);
}
// static
bool FriendAssemblyDescriptor::IsAssemblyOnList(PEAssembly *pAssembly, const ArrayList &alAssemblyNames)
{
CONTRACTL
{
THROWS;
GC_TRIGGERS;
PRECONDITION(CheckPointer(pAssembly));
}
CONTRACTL_END;
AssemblySpec asmDef;
asmDef.InitializeSpec(pAssembly);
ArrayList::ConstIterator itAssemblyNames = alAssemblyNames.Iterate();
while (itAssemblyNames.Next())
{
const FriendAssemblyName_t *pFriendAssemblyName = static_cast<const FriendAssemblyName_t *>(itAssemblyNames.GetElement());
HRESULT hr = AssemblySpec::RefMatchesDef(pFriendAssemblyName, &asmDef) ? S_OK : S_FALSE;
if (hr == S_OK)
{
return true;
}
}
return false;
}
#endif // !DACCESS_COMPILE