// 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.
// --------------------------------------------------------------------------------
// PEFile.cpp
//
// --------------------------------------------------------------------------------
#include "common.h"
#include "pefile.h"
#include "strongname.h"
#include "corperm.h"
#include "eecontract.h"
#include "apithreadstress.h"
#include "eeconfig.h"
#include "product_version.h"
#include "eventtrace.h"
#include "corperm.h"
#include "dbginterface.h"
#include "peimagelayout.inl"
#include "dlwrap.h"
#include "invokeutil.h"
#ifdef FEATURE_PREJIT
#include "compile.h"
#endif
#include "strongnameinternal.h"
#include "../binder/inc/applicationcontext.hpp"
#include "clrprivbinderutil.h"
#include "../binder/inc/coreclrbindercommon.h"
#ifdef FEATURE_PREJIT
#include "compile.h"
#ifdef DEBUGGING_SUPPORTED
SVAL_IMPL_INIT(DWORD, PEFile, s_NGENDebugFlags, 0);
#endif
#endif
#include "sha1.h"
#ifndef DACCESS_COMPILE
// ================================================================================
// PEFile class - this is an abstract base class for PEModule and PEAssembly
// @todo: rename TargetFile
// ================================================================================
PEFile::PEFile(PEImage *identity, BOOL fCheckAuthenticodeSignature/*=TRUE*/) :
#if _DEBUG
m_pDebugName(NULL),
#endif
m_identity(NULL),
m_openedILimage(NULL),
#ifdef FEATURE_PREJIT
m_nativeImage(NULL),
m_fCanUseNativeImage(TRUE),
#endif
m_MDImportIsRW_Debugger_Use_Only(FALSE),
m_bHasPersistentMDImport(FALSE),
m_pMDImport(NULL),
m_pImporter(NULL),
m_pEmitter(NULL),
m_pMetadataLock(::new SimpleRWLock(PREEMPTIVE, LOCK_TYPE_DEFAULT)),
m_refCount(1),
m_hash(NULL),
m_flags(0),
m_fStrongNameVerified(FALSE)
,m_pHostAssembly(nullptr)
,m_pFallbackLoadContextBinder(nullptr)
{
CONTRACTL
{
CONSTRUCTOR_CHECK;
THROWS;
GC_TRIGGERS;
MODE_ANY;
}
CONTRACTL_END;
if (identity)
{
identity->AddRef();
m_identity = identity;
if(identity->IsOpened())
{
//already opened, prepopulate
identity->AddRef();
m_openedILimage = identity;
}
}
}
PEFile::~PEFile()
{
CONTRACTL
{
DESTRUCTOR_CHECK;
NOTHROW;
GC_TRIGGERS;
MODE_ANY;
}
CONTRACTL_END;
ReleaseMetadataInterfaces(TRUE);
if (m_hash != NULL)
delete m_hash;
#ifdef FEATURE_PREJIT
if (m_nativeImage != NULL)
{
MarkNativeImageInvalidIfOwned();
m_nativeImage->Release();
}
#endif //FEATURE_PREJIT
if (m_openedILimage != NULL)
m_openedILimage->Release();
if (m_identity != NULL)
m_identity->Release();
if (m_pMetadataLock)
delete m_pMetadataLock;
if (m_pHostAssembly != NULL)
{
m_pHostAssembly->Release();
}
}
#ifndef DACCESS_COMPILE
void PEFile::ReleaseIL()
{
WRAPPER_NO_CONTRACT;
if (m_openedILimage!=NULL )
{
ReleaseMetadataInterfaces(TRUE, TRUE);
if (m_identity != NULL)
{
m_identity->Release();
m_identity=NULL;
}
m_openedILimage->Release();
m_openedILimage = NULL;
}
}
#endif
/* static */
PEFile *PEFile::Open(PEImage *image)
{
CONTRACT(PEFile *)
{
PRECONDITION(image != NULL);
PRECONDITION(image->CheckFormat());
POSTCONDITION(RETVAL != NULL);
POSTCONDITION(!RETVAL->IsModule());
POSTCONDITION(!RETVAL->IsAssembly());
THROWS;
GC_TRIGGERS;
MODE_ANY;
INJECT_FAULT(COMPlusThrowOM(););
}
CONTRACT_END;
PEFile *pFile = new PEFile(image, FALSE);
if (image->HasNTHeaders() && image->HasCorHeader())
pFile->OpenMDImport_Unsafe(); //no one else can see the object yet
#if _DEBUG
pFile->m_debugName = image->GetPath();
pFile->m_debugName.Normalize();
pFile->m_pDebugName = pFile->m_debugName;
#endif
RETURN pFile;
}
// ------------------------------------------------------------
// Loader support routines
// ------------------------------------------------------------
template void CoTaskFree(T *p)
{
if (p != NULL)
{
p->T::~T();
CoTaskMemFree(p);
}
}
NEW_WRAPPER_TEMPLATE1(CoTaskNewHolder, CoTaskFree<_TYPE>);
//-----------------------------------------------------------------------------------------------------
// Catch attempts to load x64 assemblies on x86, etc.
//-----------------------------------------------------------------------------------------------------
static void ValidatePEFileMachineType(PEFile *peFile)
{
STANDARD_VM_CONTRACT;
if (peFile->IsIntrospectionOnly())
return; // ReflectionOnly assemblies permitted to violate CPU restrictions
if (peFile->IsDynamic())
return; // PEFiles for ReflectionEmit assemblies don't cache the machine type.
if (peFile->IsResource())
return; // PEFiles for resource assemblies don't cache the machine type.
if (peFile->HasNativeImage())
return; // If it passed the native binder, no need to do the check again esp. at the risk of inviting an IL page-in.
DWORD peKind;
DWORD actualMachineType;
peFile->GetPEKindAndMachine(&peKind, &actualMachineType);
if (actualMachineType == IMAGE_FILE_MACHINE_I386 && ((peKind & (peILonly | pe32BitRequired)) == peILonly))
return; // Image is marked CPU-agnostic.
if (actualMachineType != IMAGE_FILE_MACHINE_NATIVE && actualMachineType != IMAGE_FILE_MACHINE_NATIVE_NI)
{
#ifdef _TARGET_AMD64_
// v4.0 64-bit compatibility workaround. The 64-bit v4.0 CLR's Reflection.Load(byte[]) api does not detect cpu-matches. We should consider fixing that in
// the next SxS release. In the meantime, this bypass will retain compat for 64-bit v4.0 CLR for target platforms that existed at the time.
//
// Though this bypass kicks in for all Load() flavors, the other Load() flavors did detect cpu-matches through various other code paths that still exist.
// Or to put it another way, this #ifdef makes the (4.5 only) ValidatePEFileMachineType() a NOP for x64, hence preserving 4.0 compatibility.
if (actualMachineType == IMAGE_FILE_MACHINE_I386 || actualMachineType == IMAGE_FILE_MACHINE_IA64)
return;
#endif // _WIN64_
// Image has required machine that doesn't match the CLR.
StackSString name;
if (peFile->IsAssembly())
((PEAssembly*)peFile)->GetDisplayName(name);
else
name = StackSString(SString::Utf8, peFile->GetSimpleName());
COMPlusThrow(kBadImageFormatException, IDS_CLASSLOAD_WRONGCPU, name.GetUnicode());
}
return; // If we got here, all is good.
}
void PEFile::LoadLibrary(BOOL allowNativeSkip/*=TRUE*/) // if allowNativeSkip==FALSE force IL image load
{
CONTRACT_VOID
{
INSTANCE_CHECK;
POSTCONDITION(CheckLoaded());
STANDARD_VM_CHECK;
}
CONTRACT_END;
// Catch attempts to load x64 assemblies on x86, etc.
ValidatePEFileMachineType(this);
// See if we've already loaded it.
if (CheckLoaded(allowNativeSkip))
{
RETURN;
}
// Note that we may be racing other threads here, in the case of domain neutral files
// Resource images are always flat.
if (IsResource())
{
GetILimage()->LoadNoMetaData(IsIntrospectionOnly());
RETURN;
}
#if !defined(_TARGET_64BIT_)
if (!HasNativeImage() && (!GetILimage()->Has32BitNTHeaders()) && !IsIntrospectionOnly())
{
// Tried to load 64-bit assembly on 32-bit platform.
EEFileLoadException::Throw(this, COR_E_BADIMAGEFORMAT, NULL);
}
#endif
// We need contents now
if (!HasNativeImage())
{
EnsureImageOpened();
}
if (IsIntrospectionOnly())
{
GetILimage()->LoadForIntrospection();
RETURN;
}
//---- Below this point, only do the things necessary for execution ----
_ASSERTE(!IsIntrospectionOnly());
#ifdef FEATURE_PREJIT
// For on-disk Dlls, we can call LoadLibrary
if (IsDll() && !((HasNativeImage()?m_nativeImage:GetILimage())->GetPath().IsEmpty()))
{
// Note that we may get a DllMain notification inside here.
if (allowNativeSkip && HasNativeImage())
{
m_nativeImage->Load();
if(!m_nativeImage->IsNativeILILOnly())
GetILimage()->Load(); // For IJW we have to load IL also...
}
else
GetILimage()->Load();
}
else
#endif // FEATURE_PREJIT
{
// Since we couldn't call LoadLibrary, we must be an IL only image
// or the image may still contain unfixed up stuff
// Note that we make an exception for CompilationDomains, since PEImage
// will map non-ILOnly images in a compilation domain.
if (!GetILimage()->IsILOnly() && !GetAppDomain()->IsCompilationDomain())
{
if (!GetILimage()->HasV1Metadata())
ThrowHR(COR_E_FIXUPSINEXE); // @todo: better error
}
// If we are already mapped, we can just use the current image.
#ifdef FEATURE_PREJIT
if (allowNativeSkip && HasNativeImage())
{
m_nativeImage->LoadFromMapped();
if( !m_nativeImage->IsNativeILILOnly())
GetILimage()->LoadFromMapped(); // For IJW we have to load IL also...
}
else
#endif
{
if (GetILimage()->IsFile())
{
#ifdef PLATFORM_UNIX
if (GetILimage()->IsILOnly())
{
GetILimage()->Load();
}
else
#endif // PLATFORM_UNIX
{
GetILimage()->LoadFromMapped();
}
}
else
{
GetILimage()->LoadNoFile();
}
}
}
RETURN;
}
void PEFile::SetLoadedHMODULE(HMODULE hMod)
{
CONTRACT_VOID
{
INSTANCE_CHECK;
PRECONDITION(CheckPointer(hMod));
POSTCONDITION(CheckLoaded());
THROWS;
GC_TRIGGERS;
MODE_ANY;
INJECT_FAULT(COMPlusThrowOM(););
}
CONTRACT_END;
// See if the image is an internal PEImage.
GetILimage()->SetLoadedHMODULE(hMod);
RETURN;
}
/* static */
void PEFile::DefineEmitScope(
GUID iid,
void **ppEmit)
{
CONTRACT_VOID
{
PRECONDITION(CheckPointer(ppEmit));
POSTCONDITION(CheckPointer(*ppEmit));
THROWS;
GC_TRIGGERS;
MODE_ANY;
INJECT_FAULT(COMPlusThrowOM(););
}
CONTRACT_END;
SafeComHolder pDispenser;
// Get the Dispenser interface.
MetaDataGetDispenser(
CLSID_CorMetaDataDispenser,
IID_IMetaDataDispenserEx,
(void **)&pDispenser);
if (pDispenser == NULL)
{
ThrowOutOfMemory();
}
// Set the option on the dispenser turn on duplicate check for TypeDef and moduleRef
VARIANT varOption;
V_VT(&varOption) = VT_UI4;
V_I4(&varOption) = MDDupDefault | MDDupTypeDef | MDDupModuleRef | MDDupExportedType | MDDupAssemblyRef | MDDupPermission | MDDupFile;
IfFailThrow(pDispenser->SetOption(MetaDataCheckDuplicatesFor, &varOption));
// Set minimal MetaData size
V_VT(&varOption) = VT_UI4;
V_I4(&varOption) = MDInitialSizeMinimal;
IfFailThrow(pDispenser->SetOption(MetaDataInitialSize, &varOption));
// turn on the thread safety!
V_I4(&varOption) = MDThreadSafetyOn;
IfFailThrow(pDispenser->SetOption(MetaDataThreadSafetyOptions, &varOption));
IfFailThrow(pDispenser->DefineScope(CLSID_CorMetaDataRuntime, 0, iid, (IUnknown **)ppEmit));
RETURN;
} // PEFile::DefineEmitScope
// ------------------------------------------------------------
// Identity
// ------------------------------------------------------------
BOOL PEFile::Equals(PEFile *pFile)
{
CONTRACTL
{
INSTANCE_CHECK;
PRECONDITION(CheckPointer(pFile));
GC_NOTRIGGER;
NOTHROW;
CANNOT_TAKE_LOCK;
MODE_ANY;
}
CONTRACTL_END;
// Same object is equal
if (pFile == this)
return TRUE;
// Execution and introspection files are NOT equal
if ( (!IsIntrospectionOnly()) != !(pFile->IsIntrospectionOnly()) )
{
return FALSE;
}
// Different host assemblies cannot be equal unless they are associated with the same host binder
// It's ok if only one has a host binder because multiple threads can race to load the same assembly
// and that may cause temporary candidate PEAssembly objects that never get bound to a host assembly
// because another thread beats it; the losing thread will pick up the PEAssembly in the cache.
if (pFile->HasHostAssembly() && this->HasHostAssembly())
{
UINT_PTR fileBinderId = 0;
if (FAILED(pFile->GetHostAssembly()->GetBinderID(&fileBinderId)))
return FALSE;
UINT_PTR thisBinderId = 0;
if (FAILED(this->GetHostAssembly()->GetBinderID(&thisBinderId)))
return FALSE;
if (fileBinderId != thisBinderId)
return FALSE;
}
// Same identity is equal
if (m_identity != NULL && pFile->m_identity != NULL
&& m_identity->Equals(pFile->m_identity))
return TRUE;
// Same image is equal
if (m_openedILimage != NULL && pFile->m_openedILimage != NULL
&& m_openedILimage->Equals(pFile->m_openedILimage))
return TRUE;
return FALSE;
}
BOOL PEFile::Equals(PEImage *pImage)
{
CONTRACTL
{
INSTANCE_CHECK;
PRECONDITION(CheckPointer(pImage));
GC_NOTRIGGER;
NOTHROW;
MODE_ANY;
}
CONTRACTL_END;
// Same object is equal
if (pImage == m_identity || pImage == m_openedILimage)
return TRUE;
#ifdef FEATURE_PREJIT
if(pImage == m_nativeImage)
return TRUE;
#endif
// Same identity is equal
if (m_identity != NULL
&& m_identity->Equals(pImage))
return TRUE;
// Same image is equal
if (m_openedILimage != NULL
&& m_openedILimage->Equals(pImage))
return TRUE;
return FALSE;
}
// ------------------------------------------------------------
// Descriptive strings
// ------------------------------------------------------------
void PEFile::GetCodeBaseOrName(SString &result)
{
CONTRACTL
{
INSTANCE_CHECK;
THROWS;
GC_TRIGGERS;
MODE_ANY;
INJECT_FAULT(COMPlusThrowOM(););
}
CONTRACTL_END;
if (m_identity != NULL && !m_identity->GetPath().IsEmpty())
{
result.Set(m_identity->GetPath());
}
else if (IsAssembly())
{
((PEAssembly*)this)->GetCodeBase(result);
}
else
result.SetUTF8(GetSimpleName());
}
// ------------------------------------------------------------
// Checks
// ------------------------------------------------------------
CHECK PEFile::CheckLoaded(BOOL bAllowNativeSkip/*=TRUE*/)
{
CONTRACT_CHECK
{
INSTANCE_CHECK;
NOTHROW;
GC_NOTRIGGER;
SO_TOLERANT;
MODE_ANY;
}
CONTRACT_CHECK_END;
CHECK(IsLoaded(bAllowNativeSkip)
// We are allowed to skip LoadLibrary in most cases for ngen'ed IL only images
|| (bAllowNativeSkip && HasNativeImage() && IsILOnly()));
CHECK_OK;
}
// ------------------------------------------------------------
// Metadata access
// ------------------------------------------------------------
PTR_CVOID PEFile::GetMetadata(COUNT_T *pSize)
{
CONTRACT(PTR_CVOID)
{
INSTANCE_CHECK;
POSTCONDITION(CheckPointer(pSize, NULL_OK));
POSTCONDITION(CheckPointer(RETVAL, NULL_OK));
THROWS;
GC_TRIGGERS;
MODE_ANY;
SUPPORTS_DAC;
}
CONTRACT_END;
#ifdef FEATURE_PREJIT
if (HasNativeImageMetadata())
{
RETURN m_nativeImage->GetMetadata(pSize);
}
#endif
if (IsDynamic()
|| !GetILimage()->HasNTHeaders()
|| !GetILimage()->HasCorHeader())
{
if (pSize != NULL)
*pSize = 0;
RETURN NULL;
}
else
{
RETURN GetILimage()->GetMetadata(pSize);
}
}
#endif // #ifndef DACCESS_COMPILE
PTR_CVOID PEFile::GetLoadedMetadata(COUNT_T *pSize)
{
CONTRACT(PTR_CVOID)
{
INSTANCE_CHECK;
POSTCONDITION(CheckPointer(pSize, NULL_OK));
POSTCONDITION(CheckPointer(RETVAL, NULL_OK));
NOTHROW;
GC_NOTRIGGER;
MODE_ANY;
SUPPORTS_DAC;
}
CONTRACT_END;
#ifdef FEATURE_PREJIT
if (HasNativeImageMetadata())
{
RETURN GetLoadedNative()->GetMetadata(pSize);
}
#endif
if (!HasLoadedIL()
|| !GetLoadedIL()->HasNTHeaders()
|| !GetLoadedIL()->HasCorHeader())
{
if (pSize != NULL)
*pSize = 0;
RETURN NULL;
}
else
{
RETURN GetLoadedIL()->GetMetadata(pSize);
}
}
TADDR PEFile::GetIL(RVA il)
{
CONTRACT(TADDR)
{
INSTANCE_CHECK;
PRECONDITION(il != 0);
PRECONDITION(!IsDynamic());
PRECONDITION(!IsResource());
#ifndef DACCESS_COMPILE
PRECONDITION(CheckLoaded());
#endif
POSTCONDITION(RETVAL != NULL);
THROWS;
GC_NOTRIGGER;
MODE_ANY;
SUPPORTS_DAC;
}
CONTRACT_END;
PEImageLayout *image = NULL;
#ifdef FEATURE_PREJIT
// Note it is important to get the IL from the native image if
// available, since we are using the metadata from the native image
// which has different IL rva's.
if (HasNativeImageMetadata())
{
image = GetLoadedNative();
#ifndef DACCESS_COMPILE
// NGen images are trusted to be well-formed.
_ASSERTE(image->CheckILMethod(il));
#endif
}
else
#endif // FEATURE_PREJIT
{
image = GetLoadedIL();
#ifndef DACCESS_COMPILE
// Verify that the IL blob is valid before giving it out
if (!image->CheckILMethod(il))
COMPlusThrowHR(COR_E_BADIMAGEFORMAT, BFA_BAD_IL_RANGE);
#endif
}
RETURN image->GetRvaData(il);
}
#ifndef DACCESS_COMPILE
void PEFile::OpenImporter()
{
CONTRACTL
{
INSTANCE_CHECK;
THROWS;
GC_NOTRIGGER;
MODE_ANY;
INJECT_FAULT(COMPlusThrowOM(););
}
CONTRACTL_END;
// Make sure internal MD is in RW format.
ConvertMDInternalToReadWrite();
IMetaDataImport2 *pIMDImport = NULL;
IfFailThrow(GetMetaDataPublicInterfaceFromInternal((void*)GetPersistentMDImport(),
IID_IMetaDataImport2,
(void **)&pIMDImport));
// Atomically swap it into the field (release it if we lose the race)
if (FastInterlockCompareExchangePointer(&m_pImporter, pIMDImport, NULL) != NULL)
pIMDImport->Release();
}
void PEFile::ConvertMDInternalToReadWrite()
{
CONTRACTL
{
INSTANCE_CHECK;
THROWS;
GC_NOTRIGGER;
MODE_ANY;
INJECT_FAULT(EX_THROW(EEMessageException, (E_OUTOFMEMORY)););
}
CONTRACTL_END;
IMDInternalImport *pOld; // Old (current RO) value of internal import.
IMDInternalImport *pNew = NULL; // New (RW) value of internal import.
// Take a local copy of *ppImport. This may be a pointer to an RO
// or to an RW MDInternalXX.
pOld = m_pMDImport;
IMetaDataImport *pIMDImport = m_pImporter;
if (pIMDImport != NULL)
{
HRESULT hr = GetMetaDataInternalInterfaceFromPublic(pIMDImport, IID_IMDInternalImport, (void **)&pNew);
if (FAILED(hr))
{
EX_THROW(EEMessageException, (hr));
}
if (pNew == pOld)
{
pNew->Release();
return;
}
}
else
{
// If an RO, convert to an RW, return S_OK. If already RW, no conversion
// needed, return S_FALSE.
HRESULT hr = ConvertMDInternalImport(pOld, &pNew);
if (FAILED(hr))
{
EX_THROW(EEMessageException, (hr));
}
// If no conversion took place, don't change pointers.
if (hr == S_FALSE)
return;
}
// Swap the pointers in a thread safe manner. If the contents of *ppImport
// equals pOld then no other thread got here first, and the old contents are
// replaced with pNew. The old contents are returned.
_ASSERTE(m_bHasPersistentMDImport);
if (FastInterlockCompareExchangePointer(&m_pMDImport, pNew, pOld) == pOld)
{
//if the debugger queries, it will now see that we have RW metadata
m_MDImportIsRW_Debugger_Use_Only = TRUE;
// Swapped -- get the metadata to hang onto the old Internal import.
HRESULT hr=m_pMDImport->SetUserContextData(pOld);
_ASSERTE(SUCCEEDED(hr)||!"Leaking old MDImport");
IfFailThrow(hr);
}
else
{ // Some other thread finished first. Just free the results of this conversion.
pNew->Release();
}
}
void PEFile::ConvertMetadataToRWForEnC()
{
CONTRACTL
{
THROWS;
GC_NOTRIGGER;
SO_INTOLERANT;
MODE_ANY;
}
CONTRACTL_END;
// This should only ever be called on EnC capable files.
// One can check this using Module::IsEditAndContinueCapable().
// This should only be called if we're debugging, stopped, and on the helper thread.
_ASSERTE(CORDebuggerAttached());
_ASSERTE((g_pDebugInterface != NULL) && g_pDebugInterface->ThisIsHelperThread());
_ASSERTE((g_pDebugInterface != NULL) && g_pDebugInterface->IsStopped());
// Convert the metadata to RW for Edit and Continue, properly replacing the metadata import interface pointer and
// properly preserving the old importer. This will be called before the EnC system tries to apply a delta to the module's
// metadata. ConvertMDInternalToReadWrite() does that quite nicely for us.
ConvertMDInternalToReadWrite();
}
void PEFile::OpenMDImport_Unsafe()
{
CONTRACTL
{
INSTANCE_CHECK;
THROWS;
GC_TRIGGERS;
MODE_ANY;
INJECT_FAULT(COMPlusThrowOM(););
}
CONTRACTL_END;
if (m_pMDImport != NULL)
return;
#ifdef FEATURE_PREJIT
if (m_nativeImage != NULL
&& m_nativeImage->GetMDImport() != NULL
)
{
// Use native image for metadata
m_flags |= PEFILE_HAS_NATIVE_IMAGE_METADATA;
m_pMDImport=m_nativeImage->GetMDImport();
}
else
#endif
{
#ifdef FEATURE_PREJIT
m_flags &= ~PEFILE_HAS_NATIVE_IMAGE_METADATA;
#endif
if (!IsDynamic()
&& GetILimage()->HasNTHeaders()
&& GetILimage()->HasCorHeader())
{
m_pMDImport=GetILimage()->GetMDImport();
}
else
ThrowHR(COR_E_BADIMAGEFORMAT);
m_bHasPersistentMDImport=TRUE;
}
_ASSERTE(m_pMDImport);
m_pMDImport->AddRef();
}
void PEFile::OpenEmitter()
{
CONTRACTL
{
INSTANCE_CHECK;
THROWS;
GC_NOTRIGGER;
MODE_ANY;
INJECT_FAULT(COMPlusThrowOM(););
}
CONTRACTL_END;
// Make sure internal MD is in RW format.
ConvertMDInternalToReadWrite();
IMetaDataEmit *pIMDEmit = NULL;
IfFailThrow(GetMetaDataPublicInterfaceFromInternal((void*)GetPersistentMDImport(),
IID_IMetaDataEmit,
(void **)&pIMDEmit));
// Atomically swap it into the field (release it if we lose the race)
if (FastInterlockCompareExchangePointer(&m_pEmitter, pIMDEmit, NULL) != NULL)
pIMDEmit->Release();
}
void PEFile::ReleaseMetadataInterfaces(BOOL bDestructor, BOOL bKeepNativeData/*=FALSE*/)
{
CONTRACTL
{
INSTANCE_CHECK;
NOTHROW;
GC_NOTRIGGER;
MODE_ANY;
PRECONDITION(bDestructor||m_pMetadataLock->IsWriterLock());
}
CONTRACTL_END;
_ASSERTE(bDestructor || !m_bHasPersistentMDImport);
if (m_pImporter != NULL)
{
m_pImporter->Release();
m_pImporter = NULL;
}
if (m_pEmitter != NULL)
{
m_pEmitter->Release();
m_pEmitter = NULL;
}
if (m_pMDImport != NULL && (!bKeepNativeData || !HasNativeImage()))
{
m_pMDImport->Release();
m_pMDImport=NULL;
}
}
// ------------------------------------------------------------
// PE file access
// ------------------------------------------------------------
// Note that most of these APIs are currently passed through
// to the main image. However, in the near future they will
// be rerouted to the native image in the prejitted case so
// we can avoid using the original IL image.
#endif //!DACCESS_COMPILE
#ifdef FEATURE_PREJIT
#ifndef DACCESS_COMPILE
// ------------------------------------------------------------
// Native image access
// ------------------------------------------------------------
void PEFile::SetNativeImage(PEImage *image)
{
CONTRACT_VOID
{
INSTANCE_CHECK;
PRECONDITION(!HasNativeImage());
STANDARD_VM_CHECK;
}
CONTRACT_END;
_ASSERTE(image != NULL);
PREFIX_ASSUME(image != NULL);
if (image->GetLoadedLayout()->GetBase() != image->GetLoadedLayout()->GetPreferredBase())
{
ExternalLog(LL_WARNING,
W("Native image loaded at base address") LFMT_ADDR
W("rather than preferred address:") LFMT_ADDR ,
DBG_ADDR(image->GetLoadedLayout()->GetBase()),
DBG_ADDR(image->GetLoadedLayout()->GetPreferredBase()));
}
#ifdef FEATURE_TREAT_NI_AS_MSIL_DURING_DIAGNOSTICS
// In Apollo, first ask if we're supposed to be ignoring the prejitted code &
// structures in NGENd images. If so, bail now and do not set m_nativeImage. We've
// already set m_identity & m_openedILimage (possibly even pointing to the
// NGEN/Triton image), and will use those PEImages to find and JIT IL (even if they
// point to an NGENd/Tritonized image).
if (ShouldTreatNIAsMSIL())
RETURN;
#endif
m_nativeImage = image;
m_nativeImage->AddRef();
m_nativeImage->Load();
m_nativeImage->AllocateLazyCOWPages();
#if defined(_TARGET_AMD64_) && !defined(CROSSGEN_COMPILE)
static ConfigDWORD configNGenReserveForJumpStubs;
int percentReserveForJumpStubs = configNGenReserveForJumpStubs.val(CLRConfig::INTERNAL_NGenReserveForJumpStubs);
if (percentReserveForJumpStubs != 0)
{
PEImageLayout * pLayout = image->GetLoadedLayout();
ExecutionManager::GetEEJitManager()->EnsureJumpStubReserve((BYTE *)pLayout->GetBase(), pLayout->GetVirtualSize(),
percentReserveForJumpStubs * (pLayout->GetVirtualSize() / 100));
}
#endif
ExternalLog(LL_INFO100, W("Attempting to use native image %s."), image->GetPath().GetUnicode());
RETURN;
}
void PEFile::ClearNativeImage()
{
CONTRACT_VOID
{
INSTANCE_CHECK;
PRECONDITION(HasNativeImage());
POSTCONDITION(!HasNativeImage());
THROWS;
GC_TRIGGERS;
MODE_ANY;
INJECT_FAULT(COMPlusThrowOM(););
}
CONTRACT_END;
ExternalLog(LL_WARNING, "Discarding native image.");
MarkNativeImageInvalidIfOwned();
{
GCX_PREEMP();
SafeComHolderPreemp pOldImport=GetMDImportWithRef();
SimpleWriteLockHolder lock(m_pMetadataLock);
EX_TRY
{
ReleaseMetadataInterfaces(FALSE);
m_flags &= ~PEFILE_HAS_NATIVE_IMAGE_METADATA;
if (m_nativeImage)
m_nativeImage->Release();
m_nativeImage = NULL;
// Make sure our normal image is open
EnsureImageOpened();
// Reopen metadata from normal image
OpenMDImport();
}
EX_HOOK
{
RestoreMDImport(pOldImport);
}
EX_END_HOOK;
}
RETURN;
}
extern DWORD g_dwLogLevel;
//===========================================================================================================
// Encapsulates CLR and Fusion logging for runtime verification of native images.
//===========================================================================================================
static void RuntimeVerifyVLog(DWORD level, LoggableAssembly *pLogAsm, const WCHAR *fmt, va_list args)
{
STANDARD_VM_CONTRACT;
BOOL fOutputToDebugger = (level == LL_ERROR && IsDebuggerPresent());
BOOL fOutputToLogging = LoggingOn(LF_ZAP, level);
StackSString message;
message.VPrintf(fmt, args);
if (fOutputToLogging)
{
SString displayString = pLogAsm->DisplayString();
LOG((LF_ZAP, level, "%s: \"%S\"\n", "ZAP", displayString.GetUnicode()));
LOG((LF_ZAP, level, "%S", message.GetUnicode()));
LOG((LF_ZAP, level, "\n"));
}
if (fOutputToDebugger)
{
SString displayString = pLogAsm->DisplayString();
WszOutputDebugString(W("CLR:("));
WszOutputDebugString(displayString.GetUnicode());
WszOutputDebugString(W(") "));
WszOutputDebugString(message);
WszOutputDebugString(W("\n"));
}
}
//===========================================================================================================
// Encapsulates CLR and Fusion logging for runtime verification of native images.
//===========================================================================================================
static void RuntimeVerifyLog(DWORD level, LoggableAssembly *pLogAsm, const WCHAR *fmt, ...)
{
STANDARD_VM_CONTRACT;
// Avoid calling RuntimeVerifyVLog unless logging is on
if ( ((level == LL_ERROR) && IsDebuggerPresent())
|| LoggingOn(LF_ZAP, level)
)
{
va_list args;
va_start(args, fmt);
RuntimeVerifyVLog(level, pLogAsm, fmt, args);
va_end(args);
}
}
//==============================================================================
static const LPCWSTR CorCompileRuntimeDllNames[NUM_RUNTIME_DLLS] =
{
MAKEDLLNAME_W(W("coreclr")),
MAKEDLLNAME_W(W("clrjit"))
};
LPCWSTR CorCompileGetRuntimeDllName(CorCompileRuntimeDlls id)
{
CONTRACTL
{
THROWS;
GC_TRIGGERS;
MODE_ANY;
SO_INTOLERANT;
INJECT_FAULT(COMPlusThrowOM(););
}
CONTRACTL_END;
return CorCompileRuntimeDllNames[id];
}
#ifndef CROSSGEN_COMPILE
//==============================================================================
// Will always return a valid HMODULE for CLR_INFO, but will return NULL for NGEN_COMPILER_INFO
// if the DLL has not yet been loaded (it does not try to cause a load).
// Gets set by IJitManager::LoadJit (yes, this breaks the abstraction boundary).
HMODULE s_ngenCompilerDll = NULL;
extern HMODULE CorCompileGetRuntimeDll(CorCompileRuntimeDlls id)
{
CONTRACTL
{
THROWS;
GC_NOTRIGGER;
MODE_ANY;
SO_INTOLERANT;
INJECT_FAULT(COMPlusThrowOM(););
}
CONTRACTL_END;
// Currently special cased for every entry.
static_assert_no_msg(NUM_RUNTIME_DLLS == 2);
static_assert_no_msg(CORECLR_INFO == 0);
HMODULE hMod = NULL;
// Try to load the correct DLL
switch (id)
{
case CORECLR_INFO:
hMod = GetCLRModule();
break;
default:
COMPlusThrowNonLocalized(kExecutionEngineException,
W("Invalid runtime DLL ID"));
break;
}
return hMod;
}
#endif // CROSSGEN_COMPILE
//===========================================================================================================
// Helper for RuntimeVerifyNativeImageVersion(). Compares the loaded clr.dll and clrjit.dll's against
// the ones the native image was compiled against.
//===========================================================================================================
static BOOL RuntimeVerifyNativeImageTimestamps(const CORCOMPILE_VERSION_INFO *info, LoggableAssembly *pLogAsm)
{
STANDARD_VM_CONTRACT;
return TRUE;
}
//===========================================================================================================
// Validates that an NI matches the running CLR, OS, CPU, etc. This is the entrypoint used by the CLR loader.
//
//===========================================================================================================
BOOL PEAssembly::CheckNativeImageVersion(PEImage *peimage)
{
STANDARD_VM_CONTRACT;
//
// Get the zap version header. Note that modules will not have version
// headers - they add no additional versioning constraints from their
// assemblies.
//
PEImageLayoutHolder image=peimage->GetLayout(PEImageLayout::LAYOUT_ANY,PEImage::LAYOUT_CREATEIFNEEDED);
if (!image->HasNativeHeader())
return FALSE;
if (!image->CheckNativeHeaderVersion())
{
// Wrong native image version is fatal error on CoreCLR
ThrowHR(COR_E_NI_AND_RUNTIME_VERSION_MISMATCH);
}
CORCOMPILE_VERSION_INFO *info = image->GetNativeVersionInfo();
if (info == NULL)
return FALSE;
LoggablePEAssembly logAsm(this);
if (!RuntimeVerifyNativeImageVersion(info, &logAsm))
{
// Wrong native image version is fatal error on CoreCLR
ThrowHR(COR_E_NI_AND_RUNTIME_VERSION_MISMATCH);
}
CorCompileConfigFlags configFlags = PEFile::GetNativeImageConfigFlagsWithOverrides();
// Otherwise, match regardless of the instrumentation flags
configFlags = (CorCompileConfigFlags) (configFlags & ~(CORCOMPILE_CONFIG_INSTRUMENTATION_NONE | CORCOMPILE_CONFIG_INSTRUMENTATION));
if ((info->wConfigFlags & configFlags) != configFlags)
{
return FALSE;
}
return TRUE;
}
//===========================================================================================================
// Validates that an NI matches the running CLR, OS, CPU, etc.
//
// For historial reasons, some versions of the runtime perform this check at native bind time (preferrred),
// while others check at CLR load time.
//
// This is the common funnel for both versions and is agnostic to whether the "assembly" is represented
// by a CLR object or Fusion object.
//===========================================================================================================
BOOL RuntimeVerifyNativeImageVersion(const CORCOMPILE_VERSION_INFO *info, LoggableAssembly *pLogAsm)
{
STANDARD_VM_CONTRACT;
if (!RuntimeVerifyNativeImageTimestamps(info, pLogAsm))
return FALSE;
//
// Check that the EE version numbers are the same.
//
if (info->wVersionMajor != VER_MAJORVERSION
|| info->wVersionMinor != VER_MINORVERSION
|| info->wVersionBuildNumber != VER_PRODUCTBUILD
|| info->wVersionPrivateBuildNumber != VER_PRODUCTBUILD_QFE)
{
RuntimeVerifyLog(LL_ERROR, pLogAsm, W("CLR version recorded in native image doesn't match the current CLR."));
return FALSE;
}
//
// Check checked/free status
//
if (info->wBuild !=
#if _DEBUG
CORCOMPILE_BUILD_CHECKED
#else
CORCOMPILE_BUILD_FREE
#endif
)
{
RuntimeVerifyLog(LL_ERROR, pLogAsm, W("Checked/free mismatch with native image."));
return FALSE;
}
//
// Check processor
//
if (info->wMachine != IMAGE_FILE_MACHINE_NATIVE_NI)
{
RuntimeVerifyLog(LL_ERROR, pLogAsm, W("Processor type recorded in native image doesn't match this machine's processor."));
return FALSE;
}
#ifndef CROSSGEN_COMPILE
//
// Check the processor specific ID
//
CORINFO_CPU cpuInfo;
GetSpecificCpuInfo(&cpuInfo);
if (!IsCompatibleCpuInfo(&cpuInfo, &info->cpuInfo))
{
RuntimeVerifyLog(LL_ERROR, pLogAsm, W("Required CPU features recorded in native image don't match this machine's processor."));
return FALSE;
}
#endif // CROSSGEN_COMPILE
//
// The zap is up to date.
//
RuntimeVerifyLog(LL_INFO100, pLogAsm, W("Native image has correct version information."));
return TRUE;
}
#endif // !DACCESS_COMPILE
/* static */
CorCompileConfigFlags PEFile::GetNativeImageConfigFlags(BOOL fForceDebug/*=FALSE*/,
BOOL fForceProfiling/*=FALSE*/,
BOOL fForceInstrument/*=FALSE*/)
{
LIMITED_METHOD_DAC_CONTRACT;
CorCompileConfigFlags result = (CorCompileConfigFlags)0;
// Debugging
#ifdef DEBUGGING_SUPPORTED
// if these have been set, the take precedence over anything else
if (s_NGENDebugFlags)
{
if ((s_NGENDebugFlags & CORCOMPILE_CONFIG_DEBUG_NONE) != 0)
{
result = (CorCompileConfigFlags) (result|CORCOMPILE_CONFIG_DEBUG_NONE);
}
else
{
if ((s_NGENDebugFlags & CORCOMPILE_CONFIG_DEBUG) != 0)
{
result = (CorCompileConfigFlags) (result|CORCOMPILE_CONFIG_DEBUG);
}
}
}
else
#endif // DEBUGGING_SUPPORTED
{
if (fForceDebug)
{
result = (CorCompileConfigFlags) (result|CORCOMPILE_CONFIG_DEBUG);
}
else
{
result = (CorCompileConfigFlags) (result|CORCOMPILE_CONFIG_DEBUG_DEFAULT);
}
}
// Profiling
#ifdef PROFILING_SUPPORTED
if (fForceProfiling || CORProfilerUseProfileImages())
{
result = (CorCompileConfigFlags) (result|CORCOMPILE_CONFIG_PROFILING);
result = (CorCompileConfigFlags) (result & ~(CORCOMPILE_CONFIG_DEBUG_NONE|
CORCOMPILE_CONFIG_DEBUG|
CORCOMPILE_CONFIG_DEBUG_DEFAULT));
}
else
#endif //PROFILING_SUPPORTED
result = (CorCompileConfigFlags) (result|CORCOMPILE_CONFIG_PROFILING_NONE);
// Instrumentation
#ifndef DACCESS_COMPILE
BOOL instrumented = (!IsCompilationProcess() && g_pConfig->GetZapBBInstr());
#else
BOOL instrumented = FALSE;
#endif
if (instrumented || fForceInstrument)
{
result = (CorCompileConfigFlags) (result|CORCOMPILE_CONFIG_INSTRUMENTATION);
}
else
{
result = (CorCompileConfigFlags) (result|CORCOMPILE_CONFIG_INSTRUMENTATION_NONE);
}
// NOTE: Right now we are not taking instrumentation into account when binding.
return result;
}
CorCompileConfigFlags PEFile::GetNativeImageConfigFlagsWithOverrides()
{
LIMITED_METHOD_DAC_CONTRACT;
BOOL fForceDebug, fForceProfiling, fForceInstrument;
SystemDomain::GetCompilationOverrides(&fForceDebug,
&fForceProfiling,
&fForceInstrument);
return PEFile::GetNativeImageConfigFlags(fForceDebug,
fForceProfiling,
fForceInstrument);
}
#ifndef DACCESS_COMPILE
//===========================================================================================================
// Validates that a hard-dep matches the a parent NI's compile-time hard-dep.
//
// For historial reasons, some versions of the runtime perform this check at native bind time (preferrred),
// while others check at CLR load time.
//
// This is the common funnel for both versions and is agnostic to whether the "assembly" is represented
// by a CLR object or Fusion object.
//
//===========================================================================================================
BOOL RuntimeVerifyNativeImageDependency(const CORCOMPILE_NGEN_SIGNATURE &ngenSigExpected,
const CORCOMPILE_VERSION_INFO *pActual,
LoggableAssembly *pLogAsm)
{
STANDARD_VM_CONTRACT;
if (ngenSigExpected != pActual->signature)
{
// Signature did not match
SString displayString = pLogAsm->DisplayString();
RuntimeVerifyLog(LL_ERROR,
pLogAsm,
W("Rejecting native image because native image dependency %s ")
W("had a different identity than expected"),
displayString.GetUnicode());
return FALSE;
}
return TRUE;
}
// Wrapper function for use by parts of the runtime that actually have a CORCOMPILE_DEPENDENCY to work with.
BOOL RuntimeVerifyNativeImageDependency(const CORCOMPILE_DEPENDENCY *pExpected,
const CORCOMPILE_VERSION_INFO *pActual,
LoggableAssembly *pLogAsm)
{
WRAPPER_NO_CONTRACT;
return RuntimeVerifyNativeImageDependency(pExpected->signNativeImage,
pActual,
pLogAsm);
}
#endif // !DACCESS_COMPILE
#ifdef DEBUGGING_SUPPORTED
//
// Called through ICorDebugAppDomain2::SetDesiredNGENCompilerFlags to specify
// which kinds of ngen'd images fusion should load wrt debugging support
// Overrides any previous settings
//
void PEFile::SetNGENDebugFlags(BOOL fAllowOpt)
{
CONTRACTL
{
GC_NOTRIGGER;
NOTHROW;
MODE_ANY;
SUPPORTS_DAC;
}
CONTRACTL_END;
if (fAllowOpt)
s_NGENDebugFlags = CORCOMPILE_CONFIG_DEBUG_NONE;
else
s_NGENDebugFlags = CORCOMPILE_CONFIG_DEBUG;
}
//
// Called through ICorDebugAppDomain2::GetDesiredNGENCompilerFlags to determine
// which kinds of ngen'd images fusion should load wrt debugging support
//
void PEFile::GetNGENDebugFlags(BOOL *fAllowOpt)
{
CONTRACTL
{
GC_NOTRIGGER;
NOTHROW;
MODE_ANY;
SUPPORTS_DAC;
}
CONTRACTL_END;
CorCompileConfigFlags configFlags = PEFile::GetNativeImageConfigFlagsWithOverrides();
*fAllowOpt = ((configFlags & CORCOMPILE_CONFIG_DEBUG) == 0);
}
#endif // DEBUGGING_SUPPORTED
#ifndef DACCESS_COMPILE
#ifdef FEATURE_TREAT_NI_AS_MSIL_DURING_DIAGNOSTICS
//---------------------------------------------------------------------------------------
//
// Used in Apollo, this method determines whether profiling or debugging has requested
// the runtime to provide debuggable / profileable code. In other CLR builds, this would
// normally result in requiring the appropriate NGEN scenario be loaded (/Debug or
// /Profile) and to JIT if unavailable. In Apollo, however, these NGEN scenarios are
// never available, and even MSIL assemblies are often not available. So this function
// tells its caller to use the NGENd assembly as if it were an MSIL assembly--ignore the
// prejitted code and prebaked structures, and just JIT code and load classes from
// scratch.
//
// Return Value:
// nonzero iff NGENd images should be treated as MSIL images.
//
// static
BOOL PEFile::ShouldTreatNIAsMSIL()
{
LIMITED_METHOD_CONTRACT;
// Never use fragile native image content during ReadyToRun compilation. It would
// produces non-version resilient images because of wrong cached values for
// MethodTable::IsLayoutFixedInCurrentVersionBubble, etc.
if (IsReadyToRunCompilation())
return TRUE;
// Ask profiling API & config vars whether NGENd images should be avoided
// completely.
if (!NGENImagesAllowed())
return TRUE;
// Ask profiling and debugging if they're requesting us to use ngen /Debug or
// /Profile images (which aren't available under Apollo)
CorCompileConfigFlags configFlags = PEFile::GetNativeImageConfigFlagsWithOverrides();
if ((configFlags & (CORCOMPILE_CONFIG_DEBUG | CORCOMPILE_CONFIG_PROFILING)) != 0)
return TRUE;
return FALSE;
}
#endif // FEATURE_TREAT_NI_AS_MSIL_DURING_DIAGNOSTICS
#endif //!DACCESS_COMPILE
#endif // FEATURE_PREJIT
#ifndef DACCESS_COMPILE
// ------------------------------------------------------------
// Resource access
// ------------------------------------------------------------
void PEFile::GetEmbeddedResource(DWORD dwOffset, DWORD *cbResource, PBYTE *pbInMemoryResource)
{
CONTRACTL
{
INSTANCE_CHECK;
THROWS;
GC_TRIGGERS;
MODE_ANY;
INJECT_FAULT(ThrowOutOfMemory(););
}
CONTRACTL_END;
// NOTE: it's not clear whether to load this from m_image or m_loadedImage.
// m_loadedImage is probably preferable, but this may be called by security
// before the image is loaded.
PEImage *image;
#ifdef FEATURE_PREJIT
if (m_nativeImage != NULL)
image = m_nativeImage;
else
#endif
{
EnsureImageOpened();
image = GetILimage();
}
PEImageLayoutHolder theImage(image->GetLayout(PEImageLayout::LAYOUT_ANY,PEImage::LAYOUT_CREATEIFNEEDED));
if (!theImage->CheckResource(dwOffset))
ThrowHR(COR_E_BADIMAGEFORMAT);
COUNT_T size;
const void *resource = theImage->GetResource(dwOffset, &size);
*cbResource = size;
*pbInMemoryResource = (PBYTE) resource;
}
// ------------------------------------------------------------
// File loading
// ------------------------------------------------------------
PEAssembly *
PEFile::LoadAssembly(
mdAssemblyRef kAssemblyRef,
IMDInternalImport * pImport, // = NULL
LPCUTF8 szWinRtTypeNamespace, // = NULL
LPCUTF8 szWinRtTypeClassName) // = NULL
{
CONTRACT(PEAssembly *)
{
INSTANCE_CHECK;
THROWS;
GC_TRIGGERS;
MODE_ANY;
POSTCONDITION(CheckPointer(RETVAL));
INJECT_FAULT(COMPlusThrowOM(););
}
CONTRACT_END;
if (pImport == NULL)
pImport = GetPersistentMDImport();
if (((TypeFromToken(kAssemblyRef) != mdtAssembly) &&
(TypeFromToken(kAssemblyRef) != mdtAssemblyRef)) ||
(!pImport->IsValidToken(kAssemblyRef)))
{
ThrowHR(COR_E_BADIMAGEFORMAT);
}
AssemblySpec spec;
spec.InitializeSpec(kAssemblyRef, pImport, GetAppDomain()->FindAssembly(GetAssembly()), IsIntrospectionOnly());
if (szWinRtTypeClassName != NULL)
spec.SetWindowsRuntimeType(szWinRtTypeNamespace, szWinRtTypeClassName);
RETURN GetAppDomain()->BindAssemblySpec(&spec, TRUE, IsIntrospectionOnly());
}
// ------------------------------------------------------------
// Logging
// ------------------------------------------------------------
#ifdef FEATURE_PREJIT
void PEFile::ExternalLog(DWORD facility, DWORD level, const WCHAR *fmt, ...)
{
WRAPPER_NO_CONTRACT;
va_list args;
va_start(args, fmt);
ExternalVLog(facility, level, fmt, args);
va_end(args);
}
void PEFile::ExternalLog(DWORD level, const WCHAR *fmt, ...)
{
WRAPPER_NO_CONTRACT;
va_list args;
va_start(args, fmt);
ExternalVLog(LF_ZAP, level, fmt, args);
va_end(args);
}
void PEFile::ExternalLog(DWORD level, const char *msg)
{
WRAPPER_NO_CONTRACT;
// It is OK to use %S here. We know that msg is ASCII-only.
ExternalLog(level, W("%S"), msg);
}
void PEFile::ExternalVLog(DWORD facility, DWORD level, const WCHAR *fmt, va_list args)
{
CONTRACT_VOID
{
THROWS;
GC_TRIGGERS;
}
CONTRACT_END;
BOOL fOutputToDebugger = (level == LL_ERROR && IsDebuggerPresent());
BOOL fOutputToLogging = LoggingOn(facility, level);
if (!fOutputToDebugger && !fOutputToLogging)
return;
StackSString message;
message.VPrintf(fmt, args);
if (fOutputToLogging)
{
if (GetMDImport() != NULL)
LOG((facility, level, "%s: \"%s\"\n", (facility == LF_ZAP ? "ZAP" : "LOADER"), GetSimpleName()));
else
LOG((facility, level, "%s: \"%S\"\n", (facility == LF_ZAP ? "ZAP" : "LOADER"), ((const WCHAR *)GetPath())));
LOG((facility, level, "%S", message.GetUnicode()));
LOG((facility, level, "\n"));
}
if (fOutputToDebugger)
{
WszOutputDebugString(W("CLR:("));
StackSString codebase;
GetCodeBaseOrName(codebase);
WszOutputDebugString(codebase);
WszOutputDebugString(W(") "));
WszOutputDebugString(message);
WszOutputDebugString(W("\n"));
}
RETURN;
}
void PEFile::FlushExternalLog()
{
LIMITED_METHOD_CONTRACT;
}
#endif
BOOL PEFile::GetResource(LPCSTR szName, DWORD *cbResource,
PBYTE *pbInMemoryResource, DomainAssembly** pAssemblyRef,
LPCSTR *szFileName, DWORD *dwLocation,
StackCrawlMark *pStackMark, BOOL fSkipSecurityCheck,
BOOL fSkipRaiseResolveEvent, DomainAssembly* pDomainAssembly, AppDomain* pAppDomain)
{
CONTRACTL
{
INSTANCE_CHECK;
THROWS;
MODE_ANY;
INJECT_FAULT(COMPlusThrowOM(););
WRAPPER(GC_TRIGGERS);
}
CONTRACTL_END;
mdToken mdLinkRef;
DWORD dwResourceFlags;
DWORD dwOffset;
mdManifestResource mdResource;
Assembly* pAssembly = NULL;
PEFile* pPEFile = NULL;
ReleaseHolder pImport (GetMDImportWithRef());
if (SUCCEEDED(pImport->FindManifestResourceByName(szName, &mdResource)))
{
pPEFile = this;
IfFailThrow(pImport->GetManifestResourceProps(
mdResource,
NULL, //&szName,
&mdLinkRef,
&dwOffset,
&dwResourceFlags));
}
else
{
if (fSkipRaiseResolveEvent || pAppDomain == NULL)
return FALSE;
DomainAssembly* pParentAssembly = GetAppDomain()->FindAssembly(GetAssembly());
pAssembly = pAppDomain->RaiseResourceResolveEvent(pParentAssembly, szName);
if (pAssembly == NULL)
return FALSE;
pDomainAssembly = pAssembly->GetDomainAssembly(pAppDomain);
pPEFile = pDomainAssembly->GetFile();
if (FAILED(pAssembly->GetManifestImport()->FindManifestResourceByName(
szName,
&mdResource)))
{
return FALSE;
}
if (dwLocation != 0)
{
if (pAssemblyRef != NULL)
*pAssemblyRef = pDomainAssembly;
*dwLocation = *dwLocation | 2; // ResourceLocation.containedInAnotherAssembly
}
IfFailThrow(pPEFile->GetPersistentMDImport()->GetManifestResourceProps(
mdResource,
NULL, //&szName,
&mdLinkRef,
&dwOffset,
&dwResourceFlags));
}
switch(TypeFromToken(mdLinkRef)) {
case mdtAssemblyRef:
{
if (pDomainAssembly == NULL)
return FALSE;
AssemblySpec spec;
spec.InitializeSpec(mdLinkRef, GetPersistentMDImport(), pDomainAssembly, pDomainAssembly->GetFile()->IsIntrospectionOnly());
pDomainAssembly = spec.LoadDomainAssembly(FILE_LOADED);
if (dwLocation) {
if (pAssemblyRef)
*pAssemblyRef = pDomainAssembly;
*dwLocation = *dwLocation | 2; // ResourceLocation.containedInAnotherAssembly
}
return pDomainAssembly->GetResource(szName,
cbResource,
pbInMemoryResource,
pAssemblyRef,
szFileName,
dwLocation,
pStackMark,
fSkipSecurityCheck,
fSkipRaiseResolveEvent);
}
case mdtFile:
if (mdLinkRef == mdFileNil)
{
// The resource is embedded in the manifest file
#ifndef CROSSGEN_COMPILE
if (!IsMrPublic(dwResourceFlags) && pStackMark && !fSkipSecurityCheck)
{
Assembly *pCallersAssembly = SystemDomain::GetCallersAssembly(pStackMark);
if (pCallersAssembly && // full trust for interop
(!pCallersAssembly->GetManifestFile()->Equals(this)))
{
RefSecContext sCtx(AccessCheckOptions::kMemberAccess);
AccessCheckOptions accessCheckOptions(
AccessCheckOptions::kMemberAccess, /*accessCheckType*/
NULL, /*pAccessContext*/
FALSE, /*throwIfTargetIsInaccessible*/
(MethodTable *) NULL /*pTargetMT*/
);
// SL: return TRUE only if the caller is critical
// Desktop: return TRUE only if demanding MemberAccess succeeds
if (!accessCheckOptions.DemandMemberAccessOrFail(&sCtx, NULL, TRUE /*visibilityCheck*/))
return FALSE;
}
}
#endif // CROSSGEN_COMPILE
if (dwLocation) {
*dwLocation = *dwLocation | 5; // ResourceLocation.embedded |
// ResourceLocation.containedInManifestFile
return TRUE;
}
pPEFile->GetEmbeddedResource(dwOffset, cbResource, pbInMemoryResource);
return TRUE;
}
return FALSE;
default:
ThrowHR(COR_E_BADIMAGEFORMAT, BFA_INVALID_TOKEN_IN_MANIFESTRES);
}
}
void PEFile::GetPEKindAndMachine(DWORD* pdwKind, DWORD* pdwMachine)
{
WRAPPER_NO_CONTRACT;
if (IsResource() || IsDynamic())
{
if (pdwKind)
*pdwKind = 0;
if (pdwMachine)
*pdwMachine = 0;
return;
}
#ifdef FEATURE_PREJIT
if (IsNativeLoaded())
{
CONSISTENCY_CHECK(HasNativeImage());
m_nativeImage->GetNativeILPEKindAndMachine(pdwKind, pdwMachine);
return;
}
#ifndef DACCESS_COMPILE
if (!HasOpenedILimage())
{
//don't want to touch the IL image unless we already have
ReleaseHolder pNativeImage = GetNativeImageWithRef();
if (pNativeImage)
{
pNativeImage->GetNativeILPEKindAndMachine(pdwKind, pdwMachine);
return;
}
}
#endif // DACCESS_COMPILE
#endif // FEATURE_PREJIT
GetILimage()->GetPEKindAndMachine(pdwKind, pdwMachine);
return;
}
ULONG PEFile::GetILImageTimeDateStamp()
{
CONTRACTL
{
NOTHROW;
GC_NOTRIGGER;
MODE_ANY;
}
CONTRACTL_END;
#ifdef FEATURE_PREJIT
if (IsNativeLoaded())
{
CONSISTENCY_CHECK(HasNativeImage());
// The IL image's time stamp is copied to the native image.
CORCOMPILE_VERSION_INFO* pVersionInfo = GetLoadedNative()->GetNativeVersionInfoMaybeNull();
if (pVersionInfo == NULL)
{
return 0;
}
else
{
return pVersionInfo->sourceAssembly.timeStamp;
}
}
#endif // FEATURE_PREJIT
return GetLoadedIL()->GetTimeDateStamp();
}
// ================================================================================
// PEAssembly class - a PEFile which represents an assembly
// ================================================================================
// Statics initialization.
/* static */
void PEAssembly::Attach()
{
STANDARD_VM_CONTRACT;
}
PEAssembly::PEAssembly(
CoreBindResult* pBindResultInfo,
IMetaDataEmit* pEmit,
PEFile *creator,
BOOL system,
BOOL introspectionOnly/*=FALSE*/,
PEImage * pPEImageIL /*= NULL*/,
PEImage * pPEImageNI /*= NULL*/,
ICLRPrivAssembly * pHostAssembly /*= NULL*/)
: PEFile(pBindResultInfo ? (pBindResultInfo->GetPEImage() ? pBindResultInfo->GetPEImage() :
(pBindResultInfo->HasNativeImage() ? pBindResultInfo->GetNativeImage() : NULL)
): pPEImageIL? pPEImageIL:(pPEImageNI? pPEImageNI:NULL), FALSE),
m_creator(clr::SafeAddRef(creator)),
m_bIsFromGAC(FALSE),
m_bIsOnTpaList(FALSE)
,m_fProfileAssembly(0)
{
CONTRACTL
{
CONSTRUCTOR_CHECK;
PRECONDITION(CheckPointer(pEmit, NULL_OK));
PRECONDITION(CheckPointer(creator, NULL_OK));
PRECONDITION(pBindResultInfo == NULL || (pPEImageIL == NULL && pPEImageNI == NULL));
STANDARD_VM_CHECK;
}
CONTRACTL_END;
if (introspectionOnly)
{
if (!system) // Implementation restriction: mscorlib.dll cannot be loaded as introspection. The architecture depends on there being exactly one mscorlib.
{
m_flags |= PEFILE_INTROSPECTIONONLY;
}
}
m_flags |= PEFILE_ASSEMBLY;
if (system)
m_flags |= PEFILE_SYSTEM;
// We check the precondition above that either pBindResultInfo is null or both pPEImageIL and pPEImageNI are,
// so we'll only get a max of one native image passed in.
if (pPEImageNI != NULL)
{
SetNativeImage(pPEImageNI);
}
#ifdef FEATURE_PREJIT
if (pBindResultInfo && pBindResultInfo->HasNativeImage())
SetNativeImage(pBindResultInfo->GetNativeImage());
#endif
// If we have no native image, we require a mapping for the file.
if (!HasNativeImage() || !IsILOnly())
EnsureImageOpened();
// Initialize the status of the assembly being in the GAC, or being part of the TPA list, before
// we start to do work (like strong name verification) that relies on those states to be valid.
if(pBindResultInfo != nullptr)
{
m_bIsFromGAC = pBindResultInfo->IsFromGAC();
m_bIsOnTpaList = pBindResultInfo->IsOnTpaList();
}
// Check security related stuff
VerifyStrongName();
// Open metadata eagerly to minimize failure windows
if (pEmit == NULL)
OpenMDImport_Unsafe(); //constructor, cannot race with anything
else
{
_ASSERTE(!m_bHasPersistentMDImport);
IfFailThrow(GetMetaDataInternalInterfaceFromPublic(pEmit, IID_IMDInternalImport,
(void **)&m_pMDImport));
m_pEmitter = pEmit;
pEmit->AddRef();
m_bHasPersistentMDImport=TRUE;
m_MDImportIsRW_Debugger_Use_Only = TRUE;
}
// m_pMDImport can be external
// Make sure this is an assembly
if (!m_pMDImport->IsValidToken(TokenFromRid(1, mdtAssembly)))
ThrowHR(COR_E_ASSEMBLYEXPECTED);
// Verify name eagerly
LPCUTF8 szName = GetSimpleName();
if (!*szName)
{
ThrowHR(COR_E_BADIMAGEFORMAT, BFA_EMPTY_ASSEMDEF_NAME);
}
// Set the host assembly and binding context as the AssemblySpec initialization
// for CoreCLR will expect to have it set.
if (pHostAssembly != nullptr)
{
m_pHostAssembly = clr::SafeAddRef(pHostAssembly);
}
if(pBindResultInfo != nullptr)
{
// Cannot have both pHostAssembly and a coreclr based bind
_ASSERTE(pHostAssembly == nullptr);
pBindResultInfo->GetBindAssembly(&m_pHostAssembly);
}
#if _DEBUG
GetCodeBaseOrName(m_debugName);
m_debugName.Normalize();
m_pDebugName = m_debugName;
AssemblySpec spec;
spec.InitializeSpec(this);
spec.GetFileOrDisplayName(ASM_DISPLAYF_VERSION |
ASM_DISPLAYF_CULTURE |
ASM_DISPLAYF_PUBLIC_KEY_TOKEN,
m_sTextualIdentity);
#endif
}
PEAssembly *PEAssembly::Open(
PEAssembly * pParent,
PEImage * pPEImageIL,
PEImage * pPEImageNI,
ICLRPrivAssembly * pHostAssembly,
BOOL fIsIntrospectionOnly)
{
STANDARD_VM_CONTRACT;
PEAssembly * pPEAssembly = new PEAssembly(
nullptr, // BindResult
nullptr, // IMetaDataEmit
pParent, // PEFile creator
FALSE, // isSystem
fIsIntrospectionOnly,
pPEImageIL,
pPEImageNI,
pHostAssembly);
return pPEAssembly;
}
PEAssembly::~PEAssembly()
{
CONTRACTL
{
DESTRUCTOR_CHECK;
NOTHROW;
GC_TRIGGERS; // Fusion uses crsts on AddRef/Release
MODE_ANY;
}
CONTRACTL_END;
GCX_PREEMP();
if (m_creator != NULL)
m_creator->Release();
}
#ifndef DACCESS_COMPILE
void PEAssembly::ReleaseIL()
{
CONTRACTL
{
INSTANCE_CHECK;
NOTHROW;
GC_TRIGGERS;
MODE_ANY;
}
CONTRACTL_END;
GCX_PREEMP();
if (m_creator != NULL)
{
m_creator->Release();
m_creator=NULL;
}
PEFile::ReleaseIL();
}
#endif
/* static */
PEAssembly *PEAssembly::OpenSystem(IUnknown * pAppCtx)
{
STANDARD_VM_CONTRACT;
PEAssembly *result = NULL;
EX_TRY
{
result = DoOpenSystem(pAppCtx);
}
EX_HOOK
{
Exception *ex = GET_EXCEPTION();
// Rethrow non-transient exceptions as file load exceptions with proper
// context
if (!ex->IsTransient())
EEFileLoadException::Throw(SystemDomain::System()->BaseLibrary(), ex->GetHR(), ex);
}
EX_END_HOOK;
return result;
}
/* static */
PEAssembly *PEAssembly::DoOpenSystem(IUnknown * pAppCtx)
{
CONTRACT(PEAssembly *)
{
POSTCONDITION(CheckPointer(RETVAL));
STANDARD_VM_CHECK;
}
CONTRACT_END;
ETWOnStartup (FusionBinding_V1, FusionBindingEnd_V1);
CoreBindResult bindResult;
ReleaseHolder pPrivAsm;
IfFailThrow(CCoreCLRBinderHelper::BindToSystem(&pPrivAsm, !IsCompilationProcess() || g_fAllowNativeImages));
if(pPrivAsm != NULL)
{
bindResult.Init(pPrivAsm, TRUE, TRUE);
}
RETURN new PEAssembly(&bindResult, NULL, NULL, TRUE, FALSE);
}
#ifndef CROSSGEN_COMPILE
/* static */
PEAssembly *PEAssembly::OpenMemory(PEAssembly *pParentAssembly,
const void *flat, COUNT_T size,
BOOL isIntrospectionOnly/*=FALSE*/,
CLRPrivBinderLoadFile* pBinderToUse)
{
STANDARD_VM_CONTRACT;
PEAssembly *result = NULL;
EX_TRY
{
result = DoOpenMemory(pParentAssembly, flat, size, isIntrospectionOnly, pBinderToUse);
}
EX_HOOK
{
Exception *ex = GET_EXCEPTION();
// Rethrow non-transient exceptions as file load exceptions with proper
// context
if (!ex->IsTransient())
EEFileLoadException::Throw(pParentAssembly, flat, size, ex->GetHR(), ex);
}
EX_END_HOOK;
return result;
}
// Thread stress
class DoOpenFlatStress : APIThreadStress
{
public:
PEAssembly *pParentAssembly;
const void *flat;
COUNT_T size;
DoOpenFlatStress(PEAssembly *pParentAssembly, const void *flat, COUNT_T size)
: pParentAssembly(pParentAssembly), flat(flat), size(size) {LIMITED_METHOD_CONTRACT;}
void Invoke()
{
WRAPPER_NO_CONTRACT;
PEAssemblyHolder result(PEAssembly::OpenMemory(pParentAssembly, flat, size, FALSE));
}
};
/* static */
PEAssembly *PEAssembly::DoOpenMemory(
PEAssembly *pParentAssembly,
const void *flat,
COUNT_T size,
BOOL isIntrospectionOnly,
CLRPrivBinderLoadFile* pBinderToUse)
{
CONTRACT(PEAssembly *)
{
PRECONDITION(CheckPointer(flat));
PRECONDITION(CheckOverflow(flat, size));
PRECONDITION(CheckPointer(pParentAssembly));
STANDARD_VM_CHECK;
POSTCONDITION(CheckPointer(RETVAL));
}
CONTRACT_END;
// Thread stress
DoOpenFlatStress ts(pParentAssembly, flat, size);
// Note that we must have a flat image stashed away for two reasons.
// First, we need a private copy of the data which we can verify
// before doing the mapping. And secondly, we can only compute
// the strong name hash on a flat image.
PEImageHolder image(PEImage::LoadFlat(flat, size));
// Need to verify that this is a CLR assembly
if (!image->CheckILFormat())
ThrowHR(COR_E_BADIMAGEFORMAT, BFA_BAD_IL);
CoreBindResult bindResult;
ReleaseHolder assembly;
IfFailThrow(CCoreCLRBinderHelper::GetAssemblyFromImage(image, NULL, &assembly));
bindResult.Init(assembly,FALSE,FALSE);
RETURN new PEAssembly(&bindResult, NULL, pParentAssembly, FALSE, isIntrospectionOnly);
}
#endif // !CROSSGEN_COMPILE
PEAssembly* PEAssembly::Open(CoreBindResult* pBindResult,
BOOL isSystem, BOOL isIntrospectionOnly)
{
return new PEAssembly(pBindResult,NULL,NULL,isSystem,isIntrospectionOnly);
};
/* static */
PEAssembly *PEAssembly::Create(PEAssembly *pParentAssembly,
IMetaDataAssemblyEmit *pAssemblyEmit,
BOOL bIsIntrospectionOnly)
{
CONTRACT(PEAssembly *)
{
PRECONDITION(CheckPointer(pParentAssembly));
PRECONDITION(CheckPointer(pAssemblyEmit));
STANDARD_VM_CHECK;
POSTCONDITION(CheckPointer(RETVAL));
}
CONTRACT_END;
// Set up the metadata pointers in the PEAssembly. (This is the only identity
// we have.)
SafeComHolder pEmit;
pAssemblyEmit->QueryInterface(IID_IMetaDataEmit, (void **)&pEmit);
PEAssemblyHolder pFile(new PEAssembly(NULL, pEmit, pParentAssembly, FALSE, bIsIntrospectionOnly));
RETURN pFile.Extract();
}
#ifdef FEATURE_PREJIT
void PEAssembly::SetNativeImage(PEImage * image)
{
CONTRACTL
{
INSTANCE_CHECK;
STANDARD_VM_CHECK;
}
CONTRACTL_END;
image->Load();
if (CheckNativeImageVersion(image))
{
PEFile::SetNativeImage(image);
#if 0
//Enable this code if you want to make sure we never touch the flat layout in the presence of the
//ngen image.
//#if defined(_DEBUG)
//find all the layouts in the il image and make sure we never touch them.
unsigned ignored = 0;
PTR_PEImageLayout layout = m_ILimage->GetLayout(PEImageLayout::LAYOUT_FLAT, 0);
if (layout != NULL)
{
//cache a bunch of PE metadata in the PEDecoder
m_ILimage->CheckILFormat();
//we also need some of metadata (for the public key), so cache this too
DWORD verifyOutputFlags;
m_ILimage->VerifyStrongName(&verifyOutputFlags);
//fudge this by a few pages to make sure we can still mess with the PE headers
const size_t fudgeSize = 4096 * 4;
ClrVirtualProtect((void*)(((char *)layout->GetBase()) + fudgeSize),
layout->GetSize() - fudgeSize, 0, &ignored);
layout->Release();
}
#endif
}
else
{
ExternalLog(LL_WARNING, "Native image is not correct version.");
}
}
#endif // FEATURE_PREJIT
BOOL PEAssembly::IsSourceGAC()
{
WRAPPER_NO_CONTRACT;
return m_bIsFromGAC;
};
#endif // #ifndef DACCESS_COMPILE
#ifndef DACCESS_COMPILE
// ------------------------------------------------------------
// Hash support
// ------------------------------------------------------------
void PEAssembly::VerifyStrongName()
{
CONTRACTL
{
INSTANCE_CHECK;
THROWS;
GC_TRIGGERS;
MODE_ANY;
INJECT_FAULT(COMPlusThrowOM(););
}
CONTRACTL_END;
// If we've already done the signature checks, we don't need to do them again.
if (m_fStrongNameVerified)
{
return;
}
// Without FUSION/GAC, we need to verify SN on all assemblies, except dynamic assemblies.
if (IsDynamic())
{
m_flags |= PEFILE_SKIP_MODULE_HASH_CHECKS;
m_fStrongNameVerified = TRUE;
return;
}
// Next, verify the strong name, if necessary
// Check format of image. Note we must delay this until after the GAC status has been
// checked, to handle the case where we are not loading m_image.
EnsureImageOpened();
if (m_nativeImage == NULL && !GetILimage()->IsTrustedNativeImage())
{
if (!GetILimage()->CheckILFormat())
ThrowHR(COR_E_BADIMAGEFORMAT);
}
// Check the strong name if present.
if (IsIntrospectionOnly())
{
// For introspection assemblies, we don't need to check strong names and we don't
// need to do module hash checks.
m_flags |= PEFILE_SKIP_MODULE_HASH_CHECKS;
}
else
{
// Runtime policy on CoreCLR is to skip verification of ALL assemblies
m_flags |= PEFILE_SKIP_MODULE_HASH_CHECKS;
m_fStrongNameVerified = TRUE;
}
m_fStrongNameVerified = TRUE;
}
BOOL PEAssembly::IsProfileAssembly()
{
CONTRACTL
{
THROWS;
GC_TRIGGERS;
MODE_ANY;
}
CONTRACTL_END;
//
// For now, cache the result of the check below. This cache should be removed once/if the check below
// becomes cheap (e.g. does not access metadata anymore).
//
if (VolatileLoadWithoutBarrier(&m_fProfileAssembly) != 0)
{
return m_fProfileAssembly > 0;
}
//
// In order to be a platform (profile) assembly, you must be from a trusted location (TPA list)
// If we are binding by TPA list and this assembly is on it, IsSourceGAC is true => Assembly is Profile
// If the assembly is a WinMD, it is automatically trusted since all WinMD scenarios are full trust scenarios.
//
// The check for Silverlight strongname platform assemblies is legacy backdoor. It was introduced by accidental abstraction leak
// from the old Silverlight binder, people took advantage of it and we cannot easily get rid of it now. See DevDiv #710462.
//
BOOL bProfileAssembly = IsSourceGAC() && (IsSystem() || m_bIsOnTpaList);
m_fProfileAssembly = bProfileAssembly ? 1 : -1;
return bProfileAssembly;
}
// ------------------------------------------------------------
// Descriptive strings
// ------------------------------------------------------------
// Effective path is the path of nearest parent (creator) assembly which has a nonempty path.
const SString &PEAssembly::GetEffectivePath()
{
CONTRACTL
{
INSTANCE_CHECK;
NOTHROW;
GC_NOTRIGGER;
MODE_ANY;
}
CONTRACTL_END;
PEAssembly *pAssembly = this;
while (pAssembly->m_identity == NULL
|| pAssembly->m_identity->GetPath().IsEmpty())
{
if (pAssembly->m_creator)
pAssembly = pAssembly->m_creator->GetAssembly();
else // Unmanaged exe which loads byte[]/IStream assemblies
return SString::Empty();
}
return pAssembly->m_identity->GetPath();
}
// Codebase is the fusion codebase or path for the assembly. It is in URL format.
// Note this may be obtained from the parent PEFile if we don't have a path or fusion
// assembly.
//
// fCopiedName means to get the "shadow copied" path rather than the original path, if applicable
void PEAssembly::GetCodeBase(SString &result, BOOL fCopiedName/*=FALSE*/)
{
CONTRACTL
{
INSTANCE_CHECK;
THROWS;
GC_TRIGGERS;
MODE_ANY;
INJECT_FAULT(COMPlusThrowOM(););
}
CONTRACTL_END;
// All other cases use the file path.
result.Set(GetEffectivePath());
if (!result.IsEmpty())
PathToUrl(result);
}
/* static */
void PEAssembly::PathToUrl(SString &string)
{
CONTRACTL
{
PRECONDITION(PEImage::CheckCanonicalFullPath(string));
THROWS;
GC_NOTRIGGER;
MODE_ANY;
INJECT_FAULT(COMPlusThrowOM(););
}
CONTRACTL_END;
SString::Iterator i = string.Begin();
#if !defined(PLATFORM_UNIX)
if (i[0] == W('\\'))
{
// Network path
string.Insert(i, SL("file://"));
string.Skip(i, SL("file://"));
}
else
{
// Disk path
string.Insert(i, SL("file:///"));
string.Skip(i, SL("file:///"));
}
#else
// Unix doesn't have a distinction between a network or a local path
_ASSERTE( i[0] == W('\\') || i[0] == W('/'));
SString sss(SString::Literal, W("file://"));
string.Insert(i, sss);
string.Skip(i, sss);
#endif
while (string.Find(i, W('\\')))
{
string.Replace(i, W('/'));
}
}
void PEAssembly::UrlToPath(SString &string)
{
CONTRACT_VOID
{
THROWS;
GC_NOTRIGGER;
}
CONTRACT_END;
SString::Iterator i = string.Begin();
SString sss2(SString::Literal, W("file://"));
#if !defined(PLATFORM_UNIX)
SString sss3(SString::Literal, W("file:///"));
if (string.MatchCaseInsensitive(i, sss3))
string.Delete(i, 8);
else
#endif
if (string.MatchCaseInsensitive(i, sss2))
string.Delete(i, 7);
while (string.Find(i, W('/')))
{
string.Replace(i, W('\\'));
}
RETURN;
}
BOOL PEAssembly::FindLastPathSeparator(const SString &path, SString::Iterator &i)
{
#ifdef PLATFORM_UNIX
SString::Iterator slash = i;
SString::Iterator backSlash = i;
BOOL foundSlash = path.FindBack(slash, '/');
BOOL foundBackSlash = path.FindBack(backSlash, '\\');
if (!foundSlash && !foundBackSlash)
return FALSE;
else if (foundSlash && !foundBackSlash)
i = slash;
else if (!foundSlash && foundBackSlash)
i = backSlash;
else
i = (backSlash > slash) ? backSlash : slash;
return TRUE;
#else
return path.FindBack(i, '\\');
#endif //PLATFORM_UNIX
}
// ------------------------------------------------------------
// Logging
// ------------------------------------------------------------
#ifdef FEATURE_PREJIT
void PEAssembly::ExternalVLog(DWORD facility, DWORD level, const WCHAR *fmt, va_list args)
{
CONTRACT_VOID
{
THROWS;
GC_TRIGGERS;
}
CONTRACT_END;
PEFile::ExternalVLog(facility, level, fmt, args);
RETURN;
}
void PEAssembly::FlushExternalLog()
{
CONTRACT_VOID
{
THROWS;
GC_TRIGGERS;
}
CONTRACT_END;
RETURN;
}
#endif //FEATURE_PREJIT
// ------------------------------------------------------------
// Metadata access
// ------------------------------------------------------------
HRESULT PEFile::GetVersion(USHORT *pMajor, USHORT *pMinor, USHORT *pBuild, USHORT *pRevision)
{
CONTRACTL
{
INSTANCE_CHECK;
PRECONDITION(CheckPointer(pMajor, NULL_OK));
PRECONDITION(CheckPointer(pMinor, NULL_OK));
PRECONDITION(CheckPointer(pBuild, NULL_OK));
PRECONDITION(CheckPointer(pRevision, NULL_OK));
NOTHROW;
WRAPPER(GC_TRIGGERS);
MODE_ANY;
}
CONTRACTL_END;
AssemblyMetaDataInternal md;
HRESULT hr = S_OK;;
if (m_bHasPersistentMDImport)
{
_ASSERTE(GetPersistentMDImport()->IsValidToken(TokenFromRid(1, mdtAssembly)));
IfFailRet(GetPersistentMDImport()->GetAssemblyProps(TokenFromRid(1, mdtAssembly), NULL, NULL, NULL, NULL, &md, NULL));
}
else
{
ReleaseHolder pImport(GetMDImportWithRef());
_ASSERTE(pImport->IsValidToken(TokenFromRid(1, mdtAssembly)));
IfFailRet(pImport->GetAssemblyProps(TokenFromRid(1, mdtAssembly), NULL, NULL, NULL, NULL, &md, NULL));
}
if (pMajor != NULL)
*pMajor = md.usMajorVersion;
if (pMinor != NULL)
*pMinor = md.usMinorVersion;
if (pBuild != NULL)
*pBuild = md.usBuildNumber;
if (pRevision != NULL)
*pRevision = md.usRevisionNumber;
return hr;
}
void PEFile::EnsureImageOpened()
{
WRAPPER_NO_CONTRACT;
if (IsDynamic())
return;
#ifdef FEATURE_PREJIT
if(HasNativeImage())
m_nativeImage->GetLayout(PEImageLayout::LAYOUT_ANY,PEImage::LAYOUT_CREATEIFNEEDED)->Release();
else
#endif
GetILimage()->GetLayout(PEImageLayout::LAYOUT_ANY,PEImage::LAYOUT_CREATEIFNEEDED)->Release();
}
#endif // #ifndef DACCESS_COMPILE
#ifdef DACCESS_COMPILE
void
PEFile::EnumMemoryRegions(CLRDataEnumMemoryFlags flags)
{
WRAPPER_NO_CONTRACT;
SUPPORTS_DAC;
// sizeof(PEFile) == 0xb8
DAC_ENUM_VTHIS();
EMEM_OUT(("MEM: %p PEFile\n", dac_cast(this)));
#ifdef _DEBUG
// Not a big deal if it's NULL or fails.
m_debugName.EnumMemoryRegions(flags);
#endif
if (m_identity.IsValid())
{
m_identity->EnumMemoryRegions(flags);
}
if (GetILimage().IsValid())
{
GetILimage()->EnumMemoryRegions(flags);
}
#ifdef FEATURE_PREJIT
if (m_nativeImage.IsValid())
{
m_nativeImage->EnumMemoryRegions(flags);
DacEnumHostDPtrMem(m_nativeImage->GetLoadedLayout()->GetNativeVersionInfo());
}
#endif
}
void
PEAssembly::EnumMemoryRegions(CLRDataEnumMemoryFlags flags)
{
WRAPPER_NO_CONTRACT;
PEFile::EnumMemoryRegions(flags);
if (m_creator.IsValid())
{
m_creator->EnumMemoryRegions(flags);
}
}
#endif // #ifdef DACCESS_COMPILE
//-------------------------------------------------------------------------------
// Make best-case effort to obtain an image name for use in an error message.
//
// This routine must expect to be called before the this object is fully loaded.
// It can return an empty if the name isn't available or the object isn't initialized
// enough to get a name, but it mustn't crash.
//-------------------------------------------------------------------------------
LPCWSTR PEFile::GetPathForErrorMessages()
{
CONTRACTL
{
THROWS;
GC_TRIGGERS;
INJECT_FAULT(COMPlusThrowOM(););
SUPPORTS_DAC_HOST_ONLY;
}
CONTRACTL_END
if (!IsDynamic())
{
return m_identity->GetPathForErrorMessages();
}
else
{
return W("");
}
}
#ifdef DACCESS_COMPILE
TADDR PEFile::GetMDInternalRWAddress()
{
if (!m_MDImportIsRW_Debugger_Use_Only)
return 0;
else
{
// This line of code is a bit scary, but it is correct for now at least...
// 1) We are using 'm_pMDImport_Use_Accessor' directly, and not the accessor. The field is
// named this way to prevent debugger code that wants a host implementation of IMDInternalImport
// from accidentally trying to use this pointer. This pointer is a target pointer, not
// a host pointer. However in this function we do want the target pointer, so the usage is
// accurate.
// 2) ASSUMPTION: We are assuming that the only valid implementation of RW metadata is
// MDInternalRW. If that ever changes we would need some way to disambiguate, and
// probably this entire code path would need to be redesigned.
// 3) ASSUMPTION: We are assuming that no pointer adjustment is required to convert between
// IMDInternalImport*, IMDInternalImportENC* and MDInternalRW*. Ideally I was hoping to do this with a
// static_cast<> but the compiler complains that the ENC<->RW is an unrelated conversion.
return (TADDR) m_pMDImport_UseAccessor;
}
}
#endif
// Returns the ICLRPrivBinder* instance associated with the PEFile
PTR_ICLRPrivBinder PEFile::GetBindingContext()
{
LIMITED_METHOD_CONTRACT;
PTR_ICLRPrivBinder pBindingContext = NULL;
// CoreLibrary is always bound in context of the TPA Binder. However, since it gets loaded and published
// during EEStartup *before* DefaultContext Binder (aka TPAbinder) is initialized, we dont have a binding context to publish against.
// Thus, we will always return NULL for its binding context.
if (!IsSystem())
{
pBindingContext = dac_cast(GetHostAssembly());
if (!pBindingContext)
{
// If we do not have any binding context, check if we are dealing with
// a dynamically emitted assembly and if so, use its fallback load context
// binder reference.
if (IsDynamic())
{
pBindingContext = GetFallbackLoadContextBinder();
}
}
}
return pBindingContext;
}