// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. //***************************************************************************** // File: RsAssembly.cpp // // //***************************************************************************** #include "stdafx.h" #include "primitives.h" #include "safewrap.h" #include "check.h" #include #include "wtsapi32.h" #ifndef SM_REMOTESESSION #define SM_REMOTESESSION 0x1000 #endif #include "corpriv.h" #include "../../dlls/mscorrc/resource.h" #include /* ------------------------------------------------------------------------- * * Assembly class * ------------------------------------------------------------------------- */ CordbAssembly::CordbAssembly(CordbAppDomain * pAppDomain, VMPTR_Assembly vmAssembly, VMPTR_DomainAssembly vmDomainAssembly) : CordbBase(pAppDomain->GetProcess(), vmDomainAssembly.IsNull() ? VmPtrToCookie(vmAssembly) : VmPtrToCookie(vmDomainAssembly), enumCordbAssembly), m_vmAssembly(vmAssembly), m_vmDomainAssembly(vmDomainAssembly), m_pAppDomain(pAppDomain) { _ASSERTE(!vmAssembly.IsNull()); } /* A list of which resources owned by this object are accounted for. public: CordbAppDomain *m_pAppDomain; // Assigned w/o addRef(), Deleted in ~CordbAssembly */ CordbAssembly::~CordbAssembly() { } HRESULT CordbAssembly::QueryInterface(REFIID id, void **ppInterface) { if (id == IID_ICorDebugAssembly) *ppInterface = static_cast(this); else if (id == IID_ICorDebugAssembly2) *ppInterface = static_cast(this); else if (id == IID_IUnknown) *ppInterface = static_cast( static_cast(this) ); else { *ppInterface = NULL; return E_NOINTERFACE; } ExternalAddRef(); return S_OK; } // Neutered by AppDomain void CordbAssembly::Neuter() { m_pAppDomain = NULL; CordbBase::Neuter(); } #ifdef _DEBUG //--------------------------------------------------------------------------------------- // Callback helper for code:CordbAssembly::DbgAssertAssemblyDeleted // // Arguments // vmDomainAssembly - domain file in the enumeration // pUserData - pointer to the CordbAssembly that we just got an exit event for. // // static void CordbAssembly::DbgAssertAssemblyDeletedCallback(VMPTR_DomainAssembly vmDomainAssembly, void * pUserData) { CordbAssembly * pThis = reinterpret_cast(pUserData); INTERNAL_DAC_CALLBACK(pThis->GetProcess()); VMPTR_DomainAssembly vmAssemblyDeleted = pThis->m_vmDomainAssembly; CONSISTENCY_CHECK_MSGF((vmAssemblyDeleted != vmDomainAssembly), ("An Assembly Unload event was sent, but the assembly still shows up in the enumeration.\n vmAssemblyDeleted=%p\n", VmPtrToCookie(vmAssemblyDeleted))); } //--------------------------------------------------------------------------------------- // Assert that a assembly is no longer discoverable via enumeration. // // Notes: // See code:IDacDbiInterface#Enumeration for rules that we're asserting. // This is a debug only method. It's conceptually similar to // code:CordbProcess::DbgAssertAppDomainDeleted. // void CordbAssembly::DbgAssertAssemblyDeleted() { GetProcess()->GetDAC()->EnumerateAssembliesInAppDomain( GetAppDomain()->GetADToken(), CordbAssembly::DbgAssertAssemblyDeletedCallback, this); } #endif // _DEBUG /* * GetProcess returns the process containing the assembly */ HRESULT CordbAssembly::GetProcess(ICorDebugProcess **ppProcess) { PUBLIC_API_ENTRY(this); FAIL_IF_NEUTERED(this); VALIDATE_POINTER_TO_OBJECT(ppProcess, ICorDebugProcess **); return (m_pAppDomain->GetProcess (ppProcess)); } // // Returns the AppDomain that this assembly belongs to. // // Arguments: // ppAppDomain - a non-NULL pointer to store the AppDomain in. // // Return Value: // S_OK // // Notes: // On the debugger right-side we currently consider every assembly to belong // to a single AppDomain, and create multiple CordbAssembly instances (one // per AppDomain) to represent domain-neutral assemblies. // HRESULT CordbAssembly::GetAppDomain(ICorDebugAppDomain **ppAppDomain) { PUBLIC_API_ENTRY(this); FAIL_IF_NEUTERED(this); VALIDATE_POINTER_TO_OBJECT(ppAppDomain, ICorDebugAppDomain **); _ASSERTE(m_pAppDomain != NULL); *ppAppDomain = static_cast (m_pAppDomain); m_pAppDomain->ExternalAddRef(); return S_OK; } /* * EnumerateModules enumerates all modules in the assembly */ HRESULT CordbAssembly::EnumerateModules(ICorDebugModuleEnum **ppModules) { HRESULT hr = S_OK; PUBLIC_API_BEGIN(this); { ValidateOrThrow(ppModules); *ppModules = NULL; m_pAppDomain->PrepopulateModules(); RSInitHolder pModEnum( new CordbEnumFilter(GetProcess(), GetProcess()->GetContinueNeuterList())); RSInitHolder pEnum; CordbHashTableEnum::BuildOrThrow( this, NULL, // ownership &m_pAppDomain->m_modules, IID_ICorDebugModuleEnum, pEnum.GetAddr()); // this will build up an auxillary list. Don't need pEnum after this. hr = pModEnum->Init(pEnum, this); IfFailThrow(hr); pModEnum.TransferOwnershipExternal(ppModules); } PUBLIC_API_END(hr); return hr; } /* * GetCodeBase returns the code base used to load the assembly */ HRESULT CordbAssembly::GetCodeBase(ULONG32 cchName, ULONG32 *pcchName, __out_ecount_part_opt(cchName, *pcchName) WCHAR szName[]) { PUBLIC_API_ENTRY(this); FAIL_IF_NEUTERED(this); VALIDATE_POINTER_TO_OBJECT_ARRAY(szName, WCHAR, cchName, true, true); VALIDATE_POINTER_TO_OBJECT_OR_NULL(pcchName, ULONG32 *); return E_NOTIMPL; } // // Gets the filename of the assembly // // Arguments: // cchName - number of characters available in szName, or 0 to query length // pcchName - optional pointer to store the real length of the filename // szName - buffer in which to copy the filename, or NULL if cchName is 0. // // Return value: // S_OK on success (even if there is no filename). // An error code if the filename could not be read for the assembly. This should // not happen unless the target is corrupt. // // Notes: // In-memory assemblies do not have a filename. In that case, for compatibility // this returns success and the string "". We may want to change this // behavior in the future. // HRESULT CordbAssembly::GetName(ULONG32 cchName, ULONG32 *pcchName, __out_ecount_part_opt(cchName, *pcchName) WCHAR szName[]) { PUBLIC_API_ENTRY(this); FAIL_IF_NEUTERED(this); VALIDATE_POINTER_TO_OBJECT_ARRAY_OR_NULL(szName, WCHAR, cchName, true, true); VALIDATE_POINTER_TO_OBJECT_OR_NULL(pcchName, ULONG32 *); HRESULT hr = S_OK; EX_TRY { // Lazily initialize our cache of the assembly filename. // Note that if this fails, we'll try again next time this is called. // This can be convenient for transient errors and debugging purposes, but could cause a // performance problem if failure was common (it should not be). if (!m_strAssemblyFileName.IsSet()) { IDacDbiInterface * pDac = m_pProcess->GetDAC(); // throws BOOL fNonEmpty = pDac->GetAssemblyPath(m_vmAssembly, &m_strAssemblyFileName); // throws _ASSERTE(m_strAssemblyFileName.IsSet()); if (!fNonEmpty) { // File name is empty (eg. for an in-memory assembly) _ASSERTE(m_strAssemblyFileName.IsEmpty()); // Construct a fake name // This seems unwise - the assembly doesn't have a filename, we should probably just return // an empty string and S_FALSE. This is a common case (in-memory assemblies), I don't see any reason to // fake up a filename to pretend that it has a disk location when it doesn't. // But I don't want to break tests at the moment that expect this. // Note that all assemblies have a simple metadata name - perhaps we should have an additional API for that. m_strAssemblyFileName.AssignCopy(W("")); } } // We should now have a non-empty string _ASSERTE(m_strAssemblyFileName.IsSet()); _ASSERTE(!m_strAssemblyFileName.IsEmpty()); // Copy it out to our caller } EX_CATCH_HRESULT(hr); if (FAILED(hr)) { return hr; } return CopyOutString(m_strAssemblyFileName, cchName, pcchName, szName); } HRESULT CordbAssembly::IsFullyTrusted( BOOL *pbFullyTrusted ) { PUBLIC_API_ENTRY(this); FAIL_IF_NEUTERED(this); ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess()); VALIDATE_POINTER_TO_OBJECT(pbFullyTrusted, BOOL*); if (m_vmDomainAssembly.IsNull()) return E_UNEXPECTED; // Check for cached result if( m_foptIsFullTrust.HasValue() ) { *pbFullyTrusted = m_foptIsFullTrust.GetValue(); return S_OK; } HRESULT hr = S_OK; EX_TRY { CordbProcess * pProcess = m_pAppDomain->GetProcess(); IDacDbiInterface * pDac = pProcess->GetDAC(); BOOL fIsFullTrust = pDac->IsAssemblyFullyTrusted(m_vmDomainAssembly); // Once the trust level of an assembly is known, it cannot change. m_foptIsFullTrust = fIsFullTrust; *pbFullyTrusted = fIsFullTrust; } EX_CATCH_HRESULT(hr); return hr; }