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// ==++==
6//
7
8//
9// ==--==
10#ifndef __exts_h__
11#define __exts_h__
12
13#define KDEXT_64BIT
14
15#include <windows.h>
16#include <winternl.h>
17
18#if defined(_MSC_VER)
19#pragma warning(disable:4245) // signed/unsigned mismatch
20#pragma warning(disable:4100) // unreferenced formal parameter
21#pragma warning(disable:4201) // nonstandard extension used : nameless struct/union
22#pragma warning(disable:4127) // conditional expression is constant
23#pragma warning(disable:4430) // missing type specifier: C++ doesn't support default-int
24#endif
25#include "strike.h"
26#include <wdbgexts.h>
27#include <dbgeng.h>
28#include <stdio.h>
29#include <stdlib.h>
30#include <string.h>
31
32// wdbgexts.h defines StackTrace which interferes with other parts of the
33// system that use the StackTrace identifier
34#ifdef StackTrace
35#undef StackTrace
36#endif
37
38#include "platformspecific.h"
39
40// We need to define the target address type. This has to be used in the
41// functions that read directly from the debuggee address space, vs. using
42// the DAC to read the DAC-ized data structures.
43#include "daccess.h"
44
45#include "gcinfo.h"
46
47// Convert between CLRDATA_ADDRESS and TADDR.
48#define TO_TADDR(cdaddr) ((TADDR)(cdaddr))
49#define TO_CDADDR(taddr) ((CLRDATA_ADDRESS)(LONG_PTR)(taddr))
50
51// We also need a "correction" macro: there are a number of places in the DAC
52// where instead of using the CLRDATA_ADDRESS sign-extension convention
53// we 0-extend (most notably DacpGcHeapDetails)
54#define NEED_DAC_CLRDATA_ADDRESS_CORRECTION 1
55#if NEED_DAC_CLRDATA_ADDRESS_CORRECTION == 1
56 // the macro below "corrects" a CDADDR to always represent the
57 // sign-extended equivalent ULONG64 value of the original TADDR
58 #define UL64_TO_CDA(ul64) (TO_CDADDR(TO_TADDR(ul64)))
59#else
60 #define UL64_TO_CDA(ul64) (ul64)
61#endif // NEED_DAC_CLRDATA_ADDRESS_CORRECTION 1
62
63// The macro below removes the sign extension, returning the
64// equivalent ULONG64 value to the original TADDR. Useful when
65// printing CDA values.
66#define CDA_TO_UL64(cda) ((ULONG64)(TO_TADDR(cda)))
67
68typedef struct _TADDR_RANGE
69{
70 TADDR start;
71 TADDR end;
72} TADDR_RANGE;
73
74typedef struct _TADDR_SEGINFO
75{
76 TADDR segAddr;
77 TADDR start;
78 TADDR end;
79} TADDR_SEGINFO;
80
81#include "util.h"
82
83#ifdef __cplusplus
84extern "C" {
85#endif
86
87// Cleanup tasks to be executed when the extension is unloaded
88class OnUnloadTask
89{
90public:
91 FORCEINLINE static void Register(void (*fn)())
92 {
93 // append a new unload task to the head of the list
94 OnUnloadTask *pNew = new OnUnloadTask(fn);
95 pNew->pNext = s_pUnloadTaskList;
96 s_pUnloadTaskList = pNew;
97 }
98
99 static void Run()
100 {
101 // walk the list of UnloadTasks and execute each in turn
102 OnUnloadTask* pCur = s_pUnloadTaskList;
103 while (pCur != NULL)
104 {
105 OnUnloadTask* pNext = pCur->pNext;
106 pCur->OnUnloadFn();
107 delete pCur;
108 pCur = pNext;
109 }
110 s_pUnloadTaskList = NULL;
111 }
112
113private:
114 OnUnloadTask(void(*fn)())
115 : OnUnloadFn(fn)
116 , pNext(NULL)
117 { }
118
119private:
120 void (*OnUnloadFn)();
121 OnUnloadTask* pNext;
122
123 static OnUnloadTask *s_pUnloadTaskList;
124};
125
126#ifndef MINIDUMP
127
128#define EXIT_API ExtRelease
129
130// Safe release and NULL.
131#define EXT_RELEASE(Unk) \
132 ((Unk) != NULL ? ((Unk)->Release(), (Unk) = NULL) : NULL)
133
134extern PDEBUG_CONTROL2 g_ExtControl;
135extern PDEBUG_DATA_SPACES g_ExtData;
136extern PDEBUG_SYMBOLS g_ExtSymbols;
137extern PDEBUG_SYSTEM_OBJECTS g_ExtSystem;
138extern PDEBUG_REGISTERS g_ExtRegisters;
139
140#ifndef FEATURE_PAL
141
142// Global variables initialized by query.
143extern PDEBUG_CLIENT g_ExtClient;
144extern PDEBUG_DATA_SPACES2 g_ExtData2;
145extern PDEBUG_SYMBOLS2 g_ExtSymbols2;
146extern PDEBUG_ADVANCED3 g_ExtAdvanced3;
147
148#else // FEATURE_PAL
149
150extern ILLDBServices* g_ExtServices;
151
152#endif // FEATURE_PAL
153
154HRESULT
155ExtQuery(PDEBUG_CLIENT client);
156
157HRESULT
158ArchQuery(void);
159
160void
161ExtRelease(void);
162
163#ifdef _DEBUG
164extern DWORD_PTR g_filterHint;
165#endif
166
167extern BOOL ControlC;
168
169inline BOOL IsInterrupt()
170{
171 if (!ControlC && g_ExtControl->GetInterrupt() == S_OK)
172 {
173 ExtOut("Command cancelled at the user's request.\n");
174 ControlC = TRUE;
175 }
176
177 return ControlC;
178}
179
180//
181// undef the wdbgexts
182//
183#undef DECLARE_API
184
185#define DECLARE_API(extension) \
186CPPMOD HRESULT CALLBACK extension(PDEBUG_CLIENT client, PCSTR args)
187
188class __ExtensionCleanUp
189{
190public:
191 __ExtensionCleanUp(){}
192 ~__ExtensionCleanUp(){ExtRelease();}
193};
194
195inline void EENotLoadedMessage(HRESULT Status)
196{
197 ExtOut("Failed to find runtime DLL (%s), 0x%08x\n", MAKEDLLNAME_A("coreclr"), Status);
198 ExtOut("Extension commands need it in order to have something to do.\n");
199}
200
201inline void DACMessage(HRESULT Status)
202{
203 ExtOut("Failed to load data access DLL, 0x%08x\n", Status);
204#ifndef FEATURE_PAL
205 ExtOut("Verify that 1) you have a recent build of the debugger (6.2.14 or newer)\n");
206 ExtOut(" 2) the file mscordacwks.dll that matches your version of coreclr.dll is \n");
207 ExtOut(" in the version directory or on the symbol path\n");
208 ExtOut(" 3) or, if you are debugging a dump file, verify that the file \n");
209 ExtOut(" mscordacwks_<arch>_<arch>_<version>.dll is on your symbol path.\n");
210 ExtOut(" 4) you are debugging on supported cross platform architecture as \n");
211 ExtOut(" the dump file. For example, an ARM dump file must be debugged\n");
212 ExtOut(" on an X86 or an ARM machine; an AMD64 dump file must be\n");
213 ExtOut(" debugged on an AMD64 machine.\n");
214 ExtOut("\n");
215 ExtOut("You can also run the debugger command .cordll to control the debugger's\n");
216 ExtOut("load of mscordacwks.dll. .cordll -ve -u -l will do a verbose reload.\n");
217 ExtOut("If that succeeds, the SOS command should work on retry.\n");
218 ExtOut("\n");
219 ExtOut("If you are debugging a minidump, you need to make sure that your executable\n");
220 ExtOut("path is pointing to coreclr.dll as well.\n");
221#else // FEATURE_PAL
222 if (Status == CORDBG_E_MISSING_DEBUGGER_EXPORTS)
223 {
224 ExtOut("You can run the debugger command 'setclrpath' to control the load of %s.\n", MAKEDLLNAME_A("mscordaccore"));
225 ExtOut("If that succeeds, the SOS command should work on retry.\n");
226 }
227 else
228 {
229 ExtOut("Can not load or initialize %s. The target runtime may not be initialized.\n", MAKEDLLNAME_A("mscordaccore"));
230 }
231#endif // FEATURE_PAL
232}
233
234HRESULT CheckEEDll();
235
236#define INIT_API_NOEE() \
237 HRESULT Status; \
238 __ExtensionCleanUp __extensionCleanUp; \
239 if ((Status = ExtQuery(client)) != S_OK) return Status; \
240 if ((Status = ArchQuery()) != S_OK) return Status; \
241 ControlC = FALSE; \
242 g_bDacBroken = TRUE; \
243 g_clrData = NULL; \
244 g_sos = NULL;
245
246#define INIT_API_EE() \
247 if ((Status = CheckEEDll()) != S_OK) \
248 { \
249 EENotLoadedMessage(Status); \
250 return Status; \
251 }
252
253#define INIT_API_NODAC() \
254 INIT_API_NOEE() \
255 INIT_API_EE()
256
257#define INIT_API_DAC() \
258 if ((Status = LoadClrDebugDll()) != S_OK) \
259 { \
260 DACMessage(Status); \
261 return Status; \
262 } \
263 g_bDacBroken = FALSE; \
264 /* If LoadClrDebugDll() succeeded make sure we release g_clrData. */ \
265 /* We may reconsider caching g_clrData in the future */ \
266 ToRelease<IXCLRDataProcess> spIDP(g_clrData); \
267 ToRelease<ISOSDacInterface> spISD(g_sos); \
268 ResetGlobals();
269
270#define INIT_API() \
271 INIT_API_NODAC() \
272 INIT_API_DAC()
273
274// Attempt to initialize DAC and SOS globals, but do not "return" on failure.
275// Instead, mark the failure to initialize the DAC by setting g_bDacBroken to TRUE.
276// This should be used from extension commands that should work OK even when no
277// runtime is loaded in the debuggee, e.g. DumpLog, DumpStack. These extensions
278// and functions they call should test g_bDacBroken before calling any DAC enabled
279// feature.
280#define INIT_API_NO_RET_ON_FAILURE() \
281 INIT_API_NOEE() \
282 if ((Status = CheckEEDll()) != S_OK) \
283 { \
284 ExtOut("Failed to find runtime DLL (%s), 0x%08x\n", MAKEDLLNAME_A("coreclr"), Status); \
285 ExtOut("Some functionality may be impaired\n"); \
286 } \
287 else if ((Status = LoadClrDebugDll()) != S_OK) \
288 { \
289 ExtOut("Failed to load data access DLL (%s), 0x%08x\n", MAKEDLLNAME_A("mscordaccore"), Status); \
290 ExtOut("Some functionality may be impaired\n"); \
291 } \
292 else \
293 { \
294 g_bDacBroken = FALSE; \
295 ResetGlobals(); \
296 } \
297 /* If LoadClrDebugDll() succeeded make sure we release g_clrData. */ \
298 /* We may reconsider caching g_clrData in the future */ \
299 ToRelease<ISOSDacInterface> spISD(g_sos); \
300 ToRelease<IXCLRDataProcess> spIDP(g_clrData);
301
302extern BOOL g_bDacBroken;
303
304#define PAGE_ALIGN64(Va) ((ULONG64)((Va) & ~((ULONG64) ((LONG64) (LONG) PageSize - 1))))
305
306extern ULONG PageSize;
307
308
309//-----------------------------------------------------------------------------------------
310//
311// Target platform abstraction
312//
313//-----------------------------------------------------------------------------------------
314
315// some needed forward declarations
316struct StackTrace_SimpleContext;
317struct GCEncodingInfo;
318struct SOSEHInfo;
319class GCDump;
320
321///
322/// IMachine interface
323///
324/// Note:
325/// The methods accepting target address args take them as size_t==DWORD_PTR==TADDR,
326/// which means this can only provide cross platform support for same-word size
327/// architectures (only ARM on x86 currently). Since this is not exposed outside SOS
328/// and since the some-word-size limitation exists across EE/DAC/SOS this is not an
329/// actual limitation.
330///
331
332class IMachine
333{
334public:
335 // Returns the IMAGE_FILE_MACHINE_*** constant corresponding to the target machine
336 virtual ULONG GetPlatform() const = 0;
337 // Returns the size of the CONTEXT for the target machine
338 virtual ULONG GetContextSize() const = 0;
339
340 // Disassembles a managed method specified by the IPBegin-IPEnd range
341 virtual void Unassembly(
342 TADDR IPBegin,
343 TADDR IPEnd,
344 TADDR IPAskedFor,
345 TADDR GCStressCodeCopy,
346 GCEncodingInfo *pGCEncodingInfo,
347 SOSEHInfo *pEHInfo,
348 BOOL bSuppressLines,
349 BOOL bDisplayOffsets) const = 0;
350
351 // Validates whether retAddr represents a return address by unassembling backwards.
352 // If the instruction before retAddr represents a target-specific call instruction
353 // it attempts to identify the target of the call. If successful it sets *whereCalled
354 // to the call target, otherwise it sets it to 0xffffffff.
355 virtual void IsReturnAddress(
356 TADDR retAddr,
357 TADDR* whereCalled) const = 0;
358
359 // If, while unwinding the stack, "PC" represents a known return address in
360 // KiUserExceptionDispatcher, "stack" is used to retrieve an exception context record
361 // in "cxr", and an exception record in "exr"
362 virtual BOOL GetExceptionContext (
363 TADDR stack,
364 TADDR PC,
365 TADDR *cxrAddr,
366 CROSS_PLATFORM_CONTEXT * cxr,
367 TADDR *exrAddr,
368 PEXCEPTION_RECORD exr) const = 0;
369
370 // Retrieves stack pointer, frame pointer, and instruction pointer from the target context
371 virtual TADDR GetSP(const CROSS_PLATFORM_CONTEXT & ctx) const = 0;
372 virtual TADDR GetBP(const CROSS_PLATFORM_CONTEXT & ctx) const = 0;
373 virtual TADDR GetIP(const CROSS_PLATFORM_CONTEXT & ctx) const = 0;
374
375 // Fills dest's data fields from a target specific context
376 virtual void FillSimpleContext(StackTrace_SimpleContext * dest, LPVOID srcCtx) const = 0;
377 // Fills a target specific context, destCtx, from the idx-th location in a target specific
378 // array of contexts that start at srcCtx
379 virtual void FillTargetContext(LPVOID destCtx, LPVOID srcCtx, int idx = 0) const = 0;
380
381 // Retrieve some target specific output strings
382 virtual LPCSTR GetDumpStackHeading() const = 0;
383 virtual LPCSTR GetDumpStackObjectsHeading() const = 0;
384 virtual LPCSTR GetSPName() const = 0;
385 // Retrieves the non-volatile registers reported to the GC
386 virtual void GetGCRegisters(LPCSTR** regNames, unsigned int* cntRegs) const = 0;
387
388 typedef void (*printfFtn)(const char* fmt, ...);
389 // Dumps the GCInfo
390 virtual void DumpGCInfo(GCInfoToken gcInfoToken, unsigned methodSize, printfFtn gcPrintf, bool encBytes, bool bPrintHeader) const = 0;
391
392protected:
393 IMachine() {}
394 virtual ~IMachine() {}
395
396private:
397 IMachine(const IMachine& machine); // undefined
398 IMachine & operator=(const IMachine&); // undefined
399}; // class IMachine
400
401
402extern IMachine* g_targetMachine;
403
404
405inline BOOL IsDbgTargetX86() { return g_targetMachine->GetPlatform() == IMAGE_FILE_MACHINE_I386; }
406inline BOOL IsDbgTargetAmd64() { return g_targetMachine->GetPlatform() == IMAGE_FILE_MACHINE_AMD64; }
407inline BOOL IsDbgTargetArm() { return g_targetMachine->GetPlatform() == IMAGE_FILE_MACHINE_ARMNT; }
408inline BOOL IsDbgTargetWin64() { return IsDbgTargetAmd64(); }
409
410/* Returns the instruction pointer for the given CONTEXT. We need this and its family of
411 * functions because certain headers are inconsistantly included on the various platforms,
412 * meaning that we cannot use GetIP and GetSP as defined by CLR.
413 */
414inline CLRDATA_ADDRESS GetIP(const CROSS_PLATFORM_CONTEXT& context)
415{
416 return TO_CDADDR(g_targetMachine->GetIP(context));
417}
418
419/* Returns the stack pointer for the given CONTEXT.
420 */
421inline CLRDATA_ADDRESS GetSP(const CROSS_PLATFORM_CONTEXT& context)
422{
423 return TO_CDADDR(g_targetMachine->GetSP(context));
424}
425
426/* Returns the base/frame pointer for the given CONTEXT.
427 */
428inline CLRDATA_ADDRESS GetBP(const CROSS_PLATFORM_CONTEXT& context)
429{
430 return TO_CDADDR(g_targetMachine->GetBP(context));
431}
432
433
434//-----------------------------------------------------------------------------------------
435//
436// api declaration macros & api access macros
437//
438//-----------------------------------------------------------------------------------------
439
440#ifndef FEATURE_PAL
441
442extern WINDBG_EXTENSION_APIS ExtensionApis;
443#define GetExpression (ExtensionApis.lpGetExpressionRoutine)
444
445extern ULONG TargetMachine;
446extern ULONG g_TargetClass;
447extern ULONG g_VDbgEng;
448
449#else // FEATURE_PAL
450
451#define GetExpression(exp) g_ExtServices->GetExpression(exp)
452
453#endif // FEATURE_PAL
454
455#define CACHE_SIZE DT_OS_PAGE_SIZE
456
457struct ReadVirtualCache
458{
459 BYTE m_cache[CACHE_SIZE];
460 TADDR m_startCache;
461 BOOL m_cacheValid;
462 ULONG m_cacheSize;
463
464 ReadVirtualCache() { Clear(); }
465 HRESULT Read(TADDR Offset, PVOID Buffer, ULONG BufferSize, PULONG lpcbBytesRead);
466 void Clear() { m_cacheValid = FALSE; m_cacheSize = CACHE_SIZE; }
467};
468
469extern ReadVirtualCache *rvCache;
470
471#define MOVE(dst, src) rvCache->Read(TO_TADDR(src), &(dst), sizeof(dst), NULL)
472#define MOVEBLOCK(dst, src, size) rvCache->Read(TO_TADDR(src), &(dst), size, NULL)
473
474#define moveN(dst, src) \
475{ \
476 HRESULT ret = MOVE(dst, src); \
477 if (FAILED(ret)) return ret; \
478}
479
480#define moveBlockN(dst, src, size) \
481{ \
482 HRESULT ret = MOVEBLOCK(dst, src, size); \
483 if (FAILED(ret)) return ret; \
484}
485
486// move cross-process: reads memory from the debuggee into
487// debugger address space and returns in case of error
488#define move_xp(dst, src) \
489{ \
490 HRESULT ret = MOVE(dst, src); \
491 if (FAILED(ret)) return; \
492}
493
494#define moveBlock(dst, src, size) \
495{ \
496 HRESULT ret = MOVEBLOCK(dst, src, size); \
497 if (FAILED(ret)) return; \
498}
499
500#ifdef __cplusplus
501#define CPPMOD extern "C"
502#else
503#define CPPMOD
504#endif
505
506#endif
507
508#ifdef __cplusplus
509}
510#endif
511
512#endif // __exts_h__
513
514