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// CorDB.cpp
6//
7
8//
9// Dll* routines for entry points, and support for COM framework. The class
10// factory and other routines live in this module.
11//
12//*****************************************************************************
13#include "stdafx.h"
14#include "classfactory.h"
15#include "corsym.h"
16#include "contract.h"
17#include "metadataexports.h"
18#if defined(FEATURE_DBGIPC_TRANSPORT_DI)
19#include "dbgtransportsession.h"
20#include "dbgtransportmanager.h"
21#endif // FEATURE_DBGIPC_TRANSPORT_DI
22
23#if defined(PLATFORM_UNIX) || defined(__ANDROID__)
24// Local (in-process) debugging is not supported for UNIX and Android.
25#define SUPPORT_LOCAL_DEBUGGING 0
26#else
27#define SUPPORT_LOCAL_DEBUGGING 1
28#endif
29
30//********** Globals. *********************************************************
31#ifndef FEATURE_PAL
32HINSTANCE g_hInst; // Instance handle to this piece of code.
33#endif
34
35//-----------------------------------------------------------------------------
36// SxS Versioning story for Mscordbi (ICorDebug + friends)
37//-----------------------------------------------------------------------------
38
39//-----------------------------------------------------------------------------
40// In v1.0, we declared that mscordbi was a "shared" component, which means
41// that we promised to provide it from now until the end of time. So every CLR implementation
42// needs an Mscordbi that implements the everett guids for CorDebug + CorPublish.
43//
44// This works fine for CorPublish, which is truly shared.
45// CorDebug however is "versioned" not "shared" - each version of the CLR has its own disjoint copy.
46//
47// Thus creating a CorDebug object requires a version parameter.
48// CoCreateInstance doesn't have a the version param, so we use the new (v2.0+)
49// shim interface CreateDebuggingInterfaceFromVersion.
50//
51// ** So in summary: **
52// - Dlls don't do self-registration; they're registered by setup using .vrg files.
53// - All CLR versions (past + future) must have the same registry footprint w.r.t mscordbi.
54// This just means that all CLRs have the same mscordbi.vrg file.
55// - CorDebug is in fact versioned and each CLR version has its own copy.
56// - In v1.0/1.1, CorDebug was a CoClass. In v2.0+, it is not a CoClass and is created via the
57// CreateDebuggingInterfaceFromVersion shim API, which takes a version parameter.
58// - CorDebug must be SxS. V1.1 must only get the V1.1 version, and V2.0 must only get the V2.0 version.
59// V1.1: Clients will cocreate to get CorDebug. v1.1 will be the only mscordbi!DllGetClassObject
60// that provides a CorDebug, so CoCreateInstance will guarantee getting a v1.1 object.
61// V2.0: Clients use the new version-aware shim API, so it's not an issue.
62//
63// ** Preparing for Life in a Single-CLR world: **
64// In Orcas (v3), we expect to run on single-CLR. There will only be 1 mscordbi, and it will service all versions.
65// For whidbey (v2), we want to be able to flip a knob and pretend to be orcas (for testing purposes).
66//
67// Here's how to do that:
68// - copy whidbey mscordbi & dac over the everett mscordbi.
69// - When VS cocreates w/ the everett-guid, it will load the mscordbi on the everett path (
70// which will be whidbey dll), and ask for the everett guid.
71// - re-add CorDebug to the g_CoClasses list.
72
73
74//********** Locals. **********************************************************
75
76
77//********** Code. ************************************************************
78
79
80//*****************************************************************************
81// Standard public helper to create a Cordb object (ICorDebug instance).
82// This is used by the shim to get the Cordb object out of this module.
83// This is the creation path for V2.0+ for CorDebug using the in-process debugging
84// architecture (ICorDebug). In CLR v4+ debugger may choose to use the out-of-process
85// architecture to get an ICorDebugProcess directly (IClrDebugging::OpenVirtualProcess).
86//
87// This was used by the Mix07 release of Silverlight, but it didn't properly support versioning
88// and we no longer support it's debugger protocol so we require callers to use
89// code:CoreCLRCreateCordbObject instead.
90//*****************************************************************************
91STDAPI CreateCordbObject(int iDebuggerVersion, IUnknown ** ppCordb)
92{
93#if !defined(FEATURE_DBGIPC_TRANSPORT_DI) && !defined(FEATURE_CORESYSTEM)
94 // This API should not be called for Windows CoreCLR unless we are doing interop-debugging
95 // (which is only supported internally). Use code:CoreCLRCreateCordbObject instead.
96 if (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_DbgEnableMixedModeDebugging) == 0)
97 {
98 _ASSERTE(!"Deprecated entry point CreateCordbObject() is called on Windows CoreCLR\n");
99 return E_NOTIMPL;
100 }
101#endif // !defined(FEATURE_DBGIPC_TRANSPORT_DI) && !defined(FEATURE_CORESYSTEM)
102
103 if (ppCordb == NULL)
104 {
105 return E_INVALIDARG;
106 }
107 if (iDebuggerVersion != CorDebugVersion_2_0 && iDebuggerVersion != CorDebugVersion_4_0)
108 {
109 return E_INVALIDARG;
110 }
111
112 return Cordb::CreateObject(
113 (CorDebugInterfaceVersion)iDebuggerVersion, ProcessDescriptor::UNINITIALIZED_PID, /*lpApplicationGroupId*/ NULL, IID_ICorDebug, (void **) ppCordb);
114}
115
116//
117// Public API.
118// Telesto Creation path with Mac sandbox support - only way to debug a sandboxed application on Mac.
119// This supercedes code:CoreCLRCreateCordbObject
120//
121// Arguments:
122// iDebuggerVersion - version of ICorDebug interfaces that the debugger is requesting
123// pid - pid of debuggee that we're attaching to.
124// lpApplicationGroupId - A string representing the application group ID of a sandboxed
125// process running in Mac. Pass NULL if the process is not
126// running in a sandbox and other platforms.
127// hmodTargetCLR - module handle to clr in target pid that we're attaching to.
128// ppCordb - (out) the resulting ICorDebug object.
129//
130// Notes:
131// It's inconsistent that this takes a (handle, pid) but hands back an ICorDebug instead of an ICorDebugProcess.
132// Callers will need to call *ppCordb->DebugActiveProcess(pid).
133STDAPI CoreCLRCreateCordbObjectEx(int iDebuggerVersion, DWORD pid, LPCWSTR lpApplicationGroupId, HMODULE hmodTargetCLR, IUnknown ** ppCordb)
134{
135 if (ppCordb == NULL)
136 {
137 return E_INVALIDARG;
138 }
139 if ((iDebuggerVersion < CorDebugVersion_2_0) ||
140 (iDebuggerVersion > CorDebugLatestVersion))
141 {
142 return E_INVALIDARG;
143 }
144
145 //
146 // Create the ICorDebug object
147 //
148 RSExtSmartPtr<ICorDebug> pCordb;
149 Cordb::CreateObject((CorDebugInterfaceVersion)iDebuggerVersion, pid, lpApplicationGroupId, IID_ICorDebug, (void **) &pCordb);
150
151 //
152 // Associate it with the target instance
153 //
154 HRESULT hr = static_cast<Cordb*>(pCordb.GetValue())->SetTargetCLR(hmodTargetCLR);
155 if (FAILED(hr))
156 {
157 return hr;
158 }
159
160 //
161 // Assign to out parameter.
162 //
163 hr = pCordb->QueryInterface(IID_IUnknown, (void**) ppCordb);
164
165 // Implicit release of pUnk, pCordb
166 return hr;
167}
168
169//
170// Public API.
171// Telesto Creation path - only way to debug multi-instance.
172// This supercedes code:CreateCordbObject
173//
174// Arguments:
175// iDebuggerVersion - version of ICorDebug interfaces that the debugger is requesting
176// pid - pid of debuggee that we're attaching to.
177// hmodTargetCLR - module handle to clr in target pid that we're attaching to.
178// ppCordb - (out) the resulting ICorDebug object.
179//
180// Notes:
181// It's inconsistent that this takes a (handle, pid) but hands back an ICorDebug instead of an ICorDebugProcess.
182// Callers will need to call *ppCordb->DebugActiveProcess(pid).
183STDAPI CoreCLRCreateCordbObject(int iDebuggerVersion, DWORD pid, HMODULE hmodTargetCLR, IUnknown ** ppCordb)
184{
185 return CoreCLRCreateCordbObjectEx(iDebuggerVersion, pid, NULL, hmodTargetCLR, ppCordb);
186}
187
188
189
190
191
192//*****************************************************************************
193// The main dll entry point for this module. This routine is called by the
194// OS when the dll gets loaded. Control is simply deferred to the main code.
195//*****************************************************************************
196BOOL WINAPI DbgDllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)
197{
198 // Save off the instance handle for later use.
199 switch (dwReason)
200 {
201
202 case DLL_PROCESS_ATTACH:
203 {
204#ifndef FEATURE_PAL
205 g_hInst = hInstance;
206#else
207 int err = PAL_InitializeDLL();
208 if(err != 0)
209 {
210 return FALSE;
211 }
212#endif
213
214#if defined(_DEBUG)
215 static int BreakOnDILoad = -1;
216 if (BreakOnDILoad == -1)
217 BreakOnDILoad = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_BreakOnDILoad);
218
219 if (BreakOnDILoad)
220 {
221 _ASSERTE(!"DI Loaded");
222 }
223#endif
224
225#if defined(LOGGING)
226 {
227 PathString rcFile;
228 WszGetModuleFileName(hInstance, rcFile);
229 LOG((LF_CORDB, LL_INFO10000,
230 "DI::DbgDllMain: load right side support from file '%s'\n",
231 rcFile.GetUnicode()));
232 }
233#endif
234
235#ifdef RSCONTRACTS
236 // alloc a TLS slot
237 DbgRSThread::s_TlsSlot = TlsAlloc();
238 _ASSERTE(DbgRSThread::s_TlsSlot != TLS_OUT_OF_INDEXES);
239#endif
240
241#if defined(FEATURE_DBGIPC_TRANSPORT_DI)
242 g_pDbgTransportTarget = new (nothrow) DbgTransportTarget();
243 if (g_pDbgTransportTarget == NULL)
244 return FALSE;
245
246 if (FAILED(g_pDbgTransportTarget->Init()))
247 return FALSE;
248#endif // FEATURE_DBGIPC_TRANSPORT_DI
249 }
250 break;
251
252 case DLL_THREAD_DETACH:
253 {
254#ifdef STRESS_LOG
255 StressLog::ThreadDetach((ThreadStressLog*) ClrFlsGetValue(TlsIdx_StressLog));
256#endif
257
258#ifdef RSCONTRACTS
259 // DbgRSThread are lazily created when we call GetThread(),
260 // So we don't need to do anything in DLL_THREAD_ATTACH,
261 // But this is our only chance to destroy the thread object.
262 DbgRSThread * p = DbgRSThread::GetThread();
263
264 p->Destroy();
265#endif
266 }
267 break;
268
269 case DLL_PROCESS_DETACH:
270 {
271#if defined(FEATURE_DBGIPC_TRANSPORT_DI)
272 if (g_pDbgTransportTarget != NULL)
273 {
274 g_pDbgTransportTarget->Shutdown();
275 delete g_pDbgTransportTarget;
276 g_pDbgTransportTarget = NULL;
277 }
278#endif // FEATURE_DBGIPC_TRANSPORT_DI
279
280#ifdef RSCONTRACTS
281 TlsFree(DbgRSThread::s_TlsSlot);
282 DbgRSThread::s_TlsSlot = TLS_OUT_OF_INDEXES;
283#endif
284 }
285 break;
286 }
287
288 return TRUE;
289}
290
291
292// The obsolete v1 CLSID - see comment above for details.
293static const GUID CLSID_CorDebug_V1 = {0x6fef44d0,0x39e7,0x4c77, { 0xbe,0x8e,0xc9,0xf8,0xcf,0x98,0x86,0x30}};
294
295#if defined(FEATURE_DBGIPC_TRANSPORT_DI)
296
297// GUID for pipe-based debugging (Unix platforms)
298const GUID CLSID_CorDebug_Telesto = {0x8bd1daae, 0x188e, 0x42f4, {0xb0, 0x09, 0x08, 0xfa, 0xfd, 0x17, 0x81, 0x3b}};
299
300// The debug engine needs to implement an internal Visual Studio debugger interface (defined by the CPDE)
301// which augments launch and attach requests so that we can obtain information from the port supplier (the
302// network address of the target in our case). See RSPriv.h for the definition of the interface. (We have to
303// hard code the IID and interface definition because VS does not export it, but it's not much of an issue
304// since COM interfaces are completely immutable).
305const GUID IID_IDebugRemoteCorDebug = {0x83C91210, 0xA34F, 0x427c, {0xB3, 0x5F, 0x79, 0xC3, 0x99, 0x5B, 0x3C, 0x14}};
306#endif // FEATURE_DBGIPC_TRANSPORT_DI
307
308//*****************************************************************************
309// Called by COM to get a class factory for a given CLSID. If it is one we
310// support, instantiate a class factory object and prepare for create instance.
311//*****************************************************************************
312STDAPI DllGetClassObjectInternal( // Return code.
313 REFCLSID rclsid, // The class to desired.
314 REFIID riid, // Interface wanted on class factory.
315 LPVOID FAR *ppv) // Return interface pointer here.
316{
317 HRESULT hr;
318 CClassFactory *pClassFactory; // To create class factory object.
319 PFN_CREATE_OBJ pfnCreateObject = NULL;
320
321
322#if defined(FEATURE_DBG_PUBLISH)
323 if (rclsid == CLSID_CorpubPublish)
324 {
325 pfnCreateObject = CorpubPublish::CreateObject;
326 }
327 else
328#endif
329#if defined(FEATURE_DBGIPC_TRANSPORT_DI)
330 if (rclsid == CLSID_CorDebug_Telesto)
331 {
332 pfnCreateObject = Cordb::CreateObjectTelesto;
333 }
334#else // !FEATURE_DBGIPC_TRANSPORT_DI
335 if(rclsid == CLSID_CorDebug_V1)
336 {
337 if (0) // if (IsSingleCLR())
338 {
339 // Don't allow creating backwards objects until we ensure that the v2.0 Right-side
340 // is backwards compat. This may involve using CordbProcess::SupportsVersion to conditionally
341 // emulate old behavior.
342 // If emulating V1.0, QIs for V2.0 interfaces should fail.
343 _ASSERTE(!"Ensure that V2.0 RS is backwards compat");
344 pfnCreateObject = Cordb::CreateObjectV1;
345 }
346 }
347#endif // FEATURE_DBGIPC_TRANSPORT_DI
348
349 if (pfnCreateObject == NULL)
350 return (CLASS_E_CLASSNOTAVAILABLE);
351
352 // Allocate the new factory object. The ref count is set to 1 in the constructor.
353 pClassFactory = new (nothrow) CClassFactory(pfnCreateObject);
354 if (!pClassFactory)
355 return (E_OUTOFMEMORY);
356
357 // Pick the v-table based on the caller's request.
358 hr = pClassFactory->QueryInterface(riid, ppv);
359
360 // Always release the local reference, if QI failed it will be
361 // the only one and the object gets freed.
362 pClassFactory->Release();
363
364 return hr;
365}
366
367#if defined(FEATURE_DBGIPC_TRANSPORT_DI)
368// In V2 we started hiding DllGetClassObject because activation was no longer performed through COM directly
369// (we went through the shim). CoreCLR doesn't have a shim and we go back to the COM model so we re-expose
370// DllGetClassObject to make that work.
371
372STDAPI DllGetClassObject( // Return code.
373 REFCLSID rclsid, // The class to desired.
374 REFIID riid, // Interface wanted on class factory.
375 LPVOID FAR *ppv) // Return interface pointer here.
376{
377 return DllGetClassObjectInternal(rclsid, riid, ppv);
378}
379#endif // FEATURE_DBGIPC_TRANSPORT_DI
380
381
382//*****************************************************************************
383//
384//********** Class factory code.
385//
386//*****************************************************************************
387
388
389//*****************************************************************************
390// QueryInterface is called to pick a v-table on the co-class.
391//*****************************************************************************
392HRESULT STDMETHODCALLTYPE CClassFactory::QueryInterface(
393 REFIID riid,
394 void **ppvObject)
395{
396 HRESULT hr;
397
398 // Avoid confusion.
399 *ppvObject = NULL;
400
401 // Pick the right v-table based on the IID passed in.
402 if (riid == IID_IUnknown)
403 *ppvObject = (IUnknown *) this;
404 else if (riid == IID_IClassFactory)
405 *ppvObject = (IClassFactory *) this;
406
407 // If successful, add a reference for out pointer and return.
408 if (*ppvObject)
409 {
410 hr = S_OK;
411 AddRef();
412 }
413 else
414 hr = E_NOINTERFACE;
415 return (hr);
416}
417
418
419//*****************************************************************************
420// CreateInstance is called to create a new instance of the coclass for which
421// this class was created in the first place. The returned pointer is the
422// v-table matching the IID if there.
423//*****************************************************************************
424HRESULT STDMETHODCALLTYPE CClassFactory::CreateInstance(
425 IUnknown *pUnkOuter,
426 REFIID riid,
427 void **ppvObject)
428{
429 HRESULT hr;
430
431 // Avoid confusion.
432 *ppvObject = NULL;
433 _ASSERTE(m_pfnCreateObject);
434
435 // Aggregation is not supported by these objects.
436 if (pUnkOuter)
437 return (CLASS_E_NOAGGREGATION);
438
439 // Ask the object to create an instance of itself, and check the iid.
440 hr = (*m_pfnCreateObject)(riid, ppvObject);
441 return (hr);
442}
443
444
445HRESULT STDMETHODCALLTYPE CClassFactory::LockServer(
446 BOOL fLock)
447{
448//<TODO>@todo: hook up lock server logic.</TODO>
449 return (S_OK);
450}
451
452
453//*****************************************************************************
454// This helper provides access to the instance handle of the loaded image.
455//*****************************************************************************
456#ifndef FEATURE_PAL
457HINSTANCE GetModuleInst()
458{
459 return g_hInst;
460}
461#endif
462
463
464//-----------------------------------------------------------------------------
465// Substitute for mscoree
466//
467// Notes:
468// Mscordbi does not link with mscoree, provide a stub implementation.
469// Callers are in dead-code paths, but we still need to provide a stub. Ideally, we'd factor
470// out the callers too and then we wouldn't need an E_NOTIMPL stub.
471STDAPI GetRequestedRuntimeInfo(LPCWSTR pExe,
472 LPCWSTR pwszVersion,
473 LPCWSTR pConfigurationFile,
474 DWORD startupFlags,
475 DWORD runtimeInfoFlags,
476 __out_ecount_opt(dwDirectory) LPWSTR pDirectory,
477 DWORD dwDirectory,
478 DWORD *dwDirectoryLength,
479 __out_ecount_opt(cchBuffer) LPWSTR pVersion,
480 DWORD cchBuffer,
481 DWORD* dwlength)
482{
483 _ASSERTE(!"GetRequestedRuntimeInfo not impl");
484 return E_NOTIMPL;
485}
486
487//-----------------------------------------------------------------------------
488// Replacement for legacy shim API GetCORRequiredVersion(...) used in linked libraries.
489// Used in code:TiggerStorage::GetDefaultVersion#CallTo_CLRRuntimeHostInternal_GetImageVersionString.
490//
491// Notes:
492// Mscordbi does not statically link to mscoree.dll.
493// This is used in EnC for IMetadataEmit2::GetSaveSize to computer size of header.
494// see code:TiggerStorage::GetDefaultVersion.
495//
496// Implemented by returning the version we're built for. Mscordbi.dll has a tight coupling with
497// the CLR version, so this will match exactly the build version we're debugging.
498// One potential caveat is that the build version doesn't necessarily match the install string
499// (eg. we may install as "v4.0.x86chk" but that's not captured in the build version). But this should
500// be internal scenarios only, and shouldn't actually matter here. If it did, we could instead get
501// the last components of the directory name the current mscordbi.dll is located in.
502//
503HRESULT
504CLRRuntimeHostInternal_GetImageVersionString(
505 __out_ecount_part(*pcchBuffer, *pcchBuffer) LPWSTR wszBuffer,
506 DWORD *pcchBuffer)
507{
508 // Construct the cannoncial version string we're built as - eg. "v4.0.1234"
509 const WCHAR k_wszBuiltFor[] = W("v") VER_PRODUCTVERSION_NO_QFE_STR_L;
510
511 // Copy our buffer in
512 HRESULT hr = HRESULT_FROM_WIN32(wcscpy_s(wszBuffer, *pcchBuffer, k_wszBuiltFor));
513
514 // Hand out length regardless of success - like GetCORRequiredVersion
515 *pcchBuffer = _countof(k_wszBuiltFor);
516
517 return hr;
518} // CLRRuntimeHostInternal_GetImageVersionString
519
520
521#ifdef _TARGET_ARM_
522BOOL
523DbiGetThreadContext(HANDLE hThread,
524 DT_CONTEXT *lpContext)
525{
526 // if we aren't local debugging this isn't going to work
527#if !defined(_ARM_) || defined(FEATURE_DBGIPC_TRANSPORT_DI) || !SUPPORT_LOCAL_DEBUGGING
528 _ASSERTE(!"Can't use local GetThreadContext remotely, this needed to go to datatarget");
529 return FALSE;
530#else
531 BOOL res = FALSE;
532 if (((ULONG)lpContext) & ~0x10)
533 {
534 CONTEXT *ctx = (CONTEXT*)_aligned_malloc(sizeof(CONTEXT), 16);
535 if (ctx)
536 {
537 ctx->ContextFlags = lpContext->ContextFlags;
538 if (::GetThreadContext(hThread, ctx))
539 {
540 *lpContext = *(DT_CONTEXT*)ctx;
541 res = TRUE;
542 }
543
544 _aligned_free(ctx);
545 }
546 else
547 {
548 // malloc does not set the last error, but the caller of GetThreadContext
549 // will expect it to be set on failure.
550 SetLastError(ERROR_OUTOFMEMORY);
551 }
552 }
553 else
554 {
555 res = ::GetThreadContext(hThread, (CONTEXT*)lpContext);
556 }
557
558 return res;
559#endif
560}
561
562BOOL
563DbiSetThreadContext(HANDLE hThread,
564 const DT_CONTEXT *lpContext)
565{
566#if !defined(_ARM_) || defined(FEATURE_DBGIPC_TRANSPORT_DI) || !SUPPORT_LOCAL_DEBUGGING
567 _ASSERTE(!"Can't use local GetThreadContext remotely, this needed to go to datatarget");
568 return FALSE;
569#else
570 BOOL res = FALSE;
571 if (((ULONG)lpContext) & ~0x10)
572 {
573 CONTEXT *ctx = (CONTEXT*)_aligned_malloc(sizeof(CONTEXT), 16);
574 if (ctx)
575 {
576 *ctx = *(CONTEXT*)lpContext;
577 res = ::SetThreadContext(hThread, ctx);
578 _aligned_free(ctx);
579 }
580 else
581 {
582 // malloc does not set the last error, but the caller of SetThreadContext
583 // will expect it to be set on failure.
584 SetLastError(ERROR_OUTOFMEMORY);
585 }
586 }
587 else
588 {
589 res = ::SetThreadContext(hThread, (CONTEXT*)lpContext);
590 }
591
592 return res;
593#endif
594}
595#endif
596