1// Licensed to the .NET Foundation under one or more agreements.
2// The .NET Foundation licenses this file to you under the MIT license.
3// See the LICENSE file in the project root for more information.
4//*****************************************************************************
5// File: RsAssembly.cpp
6//
7
8//
9//*****************************************************************************
10#include "stdafx.h"
11#include "primitives.h"
12#include "safewrap.h"
13
14#include "check.h"
15
16#include <tlhelp32.h>
17#include "wtsapi32.h"
18
19#ifndef SM_REMOTESESSION
20#define SM_REMOTESESSION 0x1000
21#endif
22
23#include "corpriv.h"
24#include "../../dlls/mscorrc/resource.h"
25#include <limits.h>
26
27
28/* ------------------------------------------------------------------------- *
29 * Assembly class
30 * ------------------------------------------------------------------------- */
31CordbAssembly::CordbAssembly(CordbAppDomain * pAppDomain,
32 VMPTR_Assembly vmAssembly,
33 VMPTR_DomainAssembly vmDomainAssembly)
34
35 : CordbBase(pAppDomain->GetProcess(),
36 vmDomainAssembly.IsNull() ? VmPtrToCookie(vmAssembly) : VmPtrToCookie(vmDomainAssembly),
37 enumCordbAssembly),
38 m_vmAssembly(vmAssembly),
39 m_vmDomainAssembly(vmDomainAssembly),
40 m_pAppDomain(pAppDomain)
41{
42 _ASSERTE(!vmAssembly.IsNull());
43}
44
45/*
46 A list of which resources owned by this object are accounted for.
47
48 public:
49 CordbAppDomain *m_pAppDomain; // Assigned w/o addRef(), Deleted in ~CordbAssembly
50*/
51
52CordbAssembly::~CordbAssembly()
53{
54}
55
56HRESULT CordbAssembly::QueryInterface(REFIID id, void **ppInterface)
57{
58 if (id == IID_ICorDebugAssembly)
59 *ppInterface = static_cast<ICorDebugAssembly*>(this);
60 else if (id == IID_ICorDebugAssembly2)
61 *ppInterface = static_cast<ICorDebugAssembly2*>(this);
62 else if (id == IID_IUnknown)
63 *ppInterface = static_cast<IUnknown*>( static_cast<ICorDebugAssembly*>(this) );
64 else
65 {
66 *ppInterface = NULL;
67 return E_NOINTERFACE;
68 }
69
70 ExternalAddRef();
71 return S_OK;
72}
73
74// Neutered by AppDomain
75void CordbAssembly::Neuter()
76{
77 m_pAppDomain = NULL;
78 CordbBase::Neuter();
79}
80
81
82#ifdef _DEBUG
83//---------------------------------------------------------------------------------------
84// Callback helper for code:CordbAssembly::DbgAssertAssemblyDeleted
85//
86// Arguments
87// vmDomainAssembly - domain file in the enumeration
88// pUserData - pointer to the CordbAssembly that we just got an exit event for.
89//
90
91// static
92void CordbAssembly::DbgAssertAssemblyDeletedCallback(VMPTR_DomainAssembly vmDomainAssembly, void * pUserData)
93{
94 CordbAssembly * pThis = reinterpret_cast<CordbAssembly * >(pUserData);
95 INTERNAL_DAC_CALLBACK(pThis->GetProcess());
96
97 VMPTR_DomainAssembly vmAssemblyDeleted = pThis->m_vmDomainAssembly;
98
99 CONSISTENCY_CHECK_MSGF((vmAssemblyDeleted != vmDomainAssembly),
100 ("An Assembly Unload event was sent, but the assembly still shows up in the enumeration.\n vmAssemblyDeleted=%p\n",
101 VmPtrToCookie(vmAssemblyDeleted)));
102}
103
104//---------------------------------------------------------------------------------------
105// Assert that a assembly is no longer discoverable via enumeration.
106//
107// Notes:
108// See code:IDacDbiInterface#Enumeration for rules that we're asserting.
109// This is a debug only method. It's conceptually similar to
110// code:CordbProcess::DbgAssertAppDomainDeleted.
111//
112void CordbAssembly::DbgAssertAssemblyDeleted()
113{
114 GetProcess()->GetDAC()->EnumerateAssembliesInAppDomain(
115 GetAppDomain()->GetADToken(),
116 CordbAssembly::DbgAssertAssemblyDeletedCallback,
117 this);
118}
119#endif // _DEBUG
120
121/*
122 * GetProcess returns the process containing the assembly
123 */
124HRESULT CordbAssembly::GetProcess(ICorDebugProcess **ppProcess)
125{
126 PUBLIC_API_ENTRY(this);
127 FAIL_IF_NEUTERED(this);
128 VALIDATE_POINTER_TO_OBJECT(ppProcess, ICorDebugProcess **);
129
130 return (m_pAppDomain->GetProcess (ppProcess));
131}
132
133//
134// Returns the AppDomain that this assembly belongs to.
135//
136// Arguments:
137// ppAppDomain - a non-NULL pointer to store the AppDomain in.
138//
139// Return Value:
140// S_OK
141//
142// Notes:
143// On the debugger right-side we currently consider every assembly to belong
144// to a single AppDomain, and create multiple CordbAssembly instances (one
145// per AppDomain) to represent domain-neutral assemblies.
146//
147HRESULT CordbAssembly::GetAppDomain(ICorDebugAppDomain **ppAppDomain)
148{
149 PUBLIC_API_ENTRY(this);
150 FAIL_IF_NEUTERED(this);
151 VALIDATE_POINTER_TO_OBJECT(ppAppDomain, ICorDebugAppDomain **);
152
153 _ASSERTE(m_pAppDomain != NULL);
154
155 *ppAppDomain = static_cast<ICorDebugAppDomain *> (m_pAppDomain);
156 m_pAppDomain->ExternalAddRef();
157
158 return S_OK;
159}
160
161
162
163/*
164 * EnumerateModules enumerates all modules in the assembly
165 */
166HRESULT CordbAssembly::EnumerateModules(ICorDebugModuleEnum **ppModules)
167{
168 HRESULT hr = S_OK;
169 PUBLIC_API_BEGIN(this);
170 {
171 ValidateOrThrow(ppModules);
172 *ppModules = NULL;
173
174 m_pAppDomain->PrepopulateModules();
175
176 RSInitHolder<CordbEnumFilter> pModEnum(
177 new CordbEnumFilter(GetProcess(), GetProcess()->GetContinueNeuterList()));
178
179 RSInitHolder<CordbHashTableEnum> pEnum;
180
181 CordbHashTableEnum::BuildOrThrow(
182 this,
183 NULL, // ownership
184 &m_pAppDomain->m_modules,
185 IID_ICorDebugModuleEnum,
186 pEnum.GetAddr());
187
188 // this will build up an auxillary list. Don't need pEnum after this.
189 hr = pModEnum->Init(pEnum, this);
190 IfFailThrow(hr);
191
192 pModEnum.TransferOwnershipExternal(ppModules);
193
194 }
195 PUBLIC_API_END(hr);
196
197 return hr;
198}
199
200
201/*
202 * GetCodeBase returns the code base used to load the assembly
203 */
204HRESULT CordbAssembly::GetCodeBase(ULONG32 cchName,
205 ULONG32 *pcchName,
206 __out_ecount_part_opt(cchName, *pcchName) WCHAR szName[])
207{
208 PUBLIC_API_ENTRY(this);
209 FAIL_IF_NEUTERED(this);
210 VALIDATE_POINTER_TO_OBJECT_ARRAY(szName, WCHAR, cchName, true, true);
211 VALIDATE_POINTER_TO_OBJECT_OR_NULL(pcchName, ULONG32 *);
212
213 return E_NOTIMPL;
214}
215
216//
217// Gets the filename of the assembly
218//
219// Arguments:
220// cchName - number of characters available in szName, or 0 to query length
221// pcchName - optional pointer to store the real length of the filename
222// szName - buffer in which to copy the filename, or NULL if cchName is 0.
223//
224// Return value:
225// S_OK on success (even if there is no filename).
226// An error code if the filename could not be read for the assembly. This should
227// not happen unless the target is corrupt.
228//
229// Notes:
230// In-memory assemblies do not have a filename. In that case, for compatibility
231// this returns success and the string "<unknown>". We may want to change this
232// behavior in the future.
233//
234HRESULT CordbAssembly::GetName(ULONG32 cchName,
235 ULONG32 *pcchName,
236 __out_ecount_part_opt(cchName, *pcchName) WCHAR szName[])
237{
238 PUBLIC_API_ENTRY(this);
239 FAIL_IF_NEUTERED(this);
240 VALIDATE_POINTER_TO_OBJECT_ARRAY_OR_NULL(szName, WCHAR, cchName, true, true);
241 VALIDATE_POINTER_TO_OBJECT_OR_NULL(pcchName, ULONG32 *);
242
243 HRESULT hr = S_OK;
244 EX_TRY
245 {
246 // Lazily initialize our cache of the assembly filename.
247 // Note that if this fails, we'll try again next time this is called.
248 // This can be convenient for transient errors and debugging purposes, but could cause a
249 // performance problem if failure was common (it should not be).
250 if (!m_strAssemblyFileName.IsSet())
251 {
252 IDacDbiInterface * pDac = m_pProcess->GetDAC(); // throws
253 BOOL fNonEmpty = pDac->GetAssemblyPath(m_vmAssembly, &m_strAssemblyFileName); // throws
254 _ASSERTE(m_strAssemblyFileName.IsSet());
255
256
257 if (!fNonEmpty)
258 {
259 // File name is empty (eg. for an in-memory assembly)
260 _ASSERTE(m_strAssemblyFileName.IsEmpty());
261
262 // Construct a fake name
263 // This seems unwise - the assembly doesn't have a filename, we should probably just return
264 // an empty string and S_FALSE. This is a common case (in-memory assemblies), I don't see any reason to
265 // fake up a filename to pretend that it has a disk location when it doesn't.
266 // But I don't want to break tests at the moment that expect this.
267 // Note that all assemblies have a simple metadata name - perhaps we should have an additional API for that.
268 m_strAssemblyFileName.AssignCopy(W("<unknown>"));
269 }
270 }
271
272 // We should now have a non-empty string
273 _ASSERTE(m_strAssemblyFileName.IsSet());
274 _ASSERTE(!m_strAssemblyFileName.IsEmpty());
275
276 // Copy it out to our caller
277 }
278 EX_CATCH_HRESULT(hr);
279 if (FAILED(hr))
280 {
281 return hr;
282 }
283 return CopyOutString(m_strAssemblyFileName, cchName, pcchName, szName);
284}
285
286HRESULT CordbAssembly::IsFullyTrusted( BOOL *pbFullyTrusted )
287{
288 PUBLIC_API_ENTRY(this);
289 FAIL_IF_NEUTERED(this);
290 ATT_REQUIRE_STOPPED_MAY_FAIL(GetProcess());
291 VALIDATE_POINTER_TO_OBJECT(pbFullyTrusted, BOOL*);
292
293 if (m_vmDomainAssembly.IsNull())
294 return E_UNEXPECTED;
295
296 // Check for cached result
297 if( m_foptIsFullTrust.HasValue() )
298 {
299 *pbFullyTrusted = m_foptIsFullTrust.GetValue();
300 return S_OK;
301 }
302
303 HRESULT hr = S_OK;
304 EX_TRY
305 {
306
307 CordbProcess * pProcess = m_pAppDomain->GetProcess();
308 IDacDbiInterface * pDac = pProcess->GetDAC();
309
310 BOOL fIsFullTrust = pDac->IsAssemblyFullyTrusted(m_vmDomainAssembly);
311
312 // Once the trust level of an assembly is known, it cannot change.
313 m_foptIsFullTrust = fIsFullTrust;
314
315 *pbFullyTrusted = fIsFullTrust;
316 }
317 EX_CATCH_HRESULT(hr);
318 return hr;
319}
320
321