diff options
Diffstat (limited to 'src/debug/di/rsassembly.cpp')
-rw-r--r-- | src/debug/di/rsassembly.cpp | 320 |
1 files changed, 320 insertions, 0 deletions
diff --git a/src/debug/di/rsassembly.cpp b/src/debug/di/rsassembly.cpp new file mode 100644 index 0000000000..e390f77bc4 --- /dev/null +++ b/src/debug/di/rsassembly.cpp @@ -0,0 +1,320 @@ +// 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 <tlhelp32.h> +#include "wtsapi32.h" + +#ifndef SM_REMOTESESSION +#define SM_REMOTESESSION 0x1000 +#endif + +#include "corpriv.h" +#include "../../dlls/mscorrc/resource.h" +#include <limits.h> + + +/* ------------------------------------------------------------------------- * + * 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<ICorDebugAssembly*>(this); + else if (id == IID_ICorDebugAssembly2) + *ppInterface = static_cast<ICorDebugAssembly2*>(this); + else if (id == IID_IUnknown) + *ppInterface = static_cast<IUnknown*>( static_cast<ICorDebugAssembly*>(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<CordbAssembly * >(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<ICorDebugAppDomain *> (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<CordbEnumFilter> pModEnum( + new CordbEnumFilter(GetProcess(), GetProcess()->GetContinueNeuterList())); + + RSInitHolder<CordbHashTableEnum> 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 "<unknown>". 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("<unknown>")); + } + } + + // 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; +} + |