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// Type-safe helper wrapper to get an EXCEPTION_RECORD slot as a CORDB_ADDRESS
6//
7// Arguments:
8// pRecord - exception record
9// idxSlot - slot to retrieve from.
10//
11// Returns:
12// contents of slot as a CordbAddress.
13CORDB_ADDRESS GetExceptionInfoAsAddress(const EXCEPTION_RECORD * pRecord, int idxSlot)
14{
15 _ASSERTE((idxSlot >= 0) && (idxSlot < EXCEPTION_MAXIMUM_PARAMETERS));
16
17 // ExceptionInformation is an array of ULONG_PTR. CORDB_ADDRESS is a 0-extended ULONG64.
18 // So the implicit cast will work here on x86. On 64-bit, it's basically a nop.
19 return pRecord->ExceptionInformation[idxSlot];
20}
21
22
23// Determine if an exception event is a Debug event for this flavor of the CLR.
24//
25// Arguments:
26// pRecord - exception record
27// pClrBaseAddress - clr Instance ID for which CLR in the target we're checking against.
28//
29// Returns:
30// NULL if the exception is not a CLR managed debug event for the given Clr instance.
31// Else, address in target process of managed debug event described by the exception (the payload).
32//
33// Notes:
34// This decodes events raised by code:Debugger.SendRawEvent
35// Anybody can spoof our exception, so this is not a reliably safe method.
36// With multiple CLRs in the same process, it's essential to use the proper pClrBaseAddress.
37CORDB_ADDRESS IsEventDebuggerNotification(
38 const EXCEPTION_RECORD * pRecord,
39 CORDB_ADDRESS pClrBaseAddress
40 )
41{
42 _ASSERTE(pRecord != NULL);
43
44 // Must specify a CLR instance.
45 _ASSERTE(pClrBaseAddress != NULL);
46
47 // If it's not even our exception code, then it's not ours.
48 if (pRecord->ExceptionCode != CLRDBG_NOTIFICATION_EXCEPTION_CODE)
49 {
50 return NULL;
51 }
52
53 //
54 // Format of an ExceptionInformation parameter is:
55 // 0: cookie (CLRDBG_EXCEPTION_DATA_CHECKSUM)
56 // 1: Base address of mscorwks. This identifies the instance of the CLR.
57 // 2: Target Address of DebuggerIPCEvent, which contains the "real" event.
58 //
59 if (pRecord->NumberParameters != 3)
60 {
61 return NULL;
62 }
63
64 // 1st argument should always be the cookie.
65 // If cookie doesn't match, very likely it's a stray exception that happens to be using
66 // our code.
67 DWORD cookie = (DWORD) pRecord->ExceptionInformation[0];
68 if (cookie != CLRDBG_EXCEPTION_DATA_CHECKSUM)
69 {
70 return NULL;
71 }
72
73 // TODO: We don't do this check in case of non-windows debugging now, because we don't support
74 // multi-instance debugging.
75#if !defined(FEATURE_DBGIPC_TRANSPORT_VM) && !defined(FEATURE_DBGIPC_TRANSPORT_DI)
76 // If base-address doesn't match, then it's likely an event from another version of the CLR
77 // in the target.
78 // We need to be careful here. CORDB_ADDRESS is a ULONG64, whereas ExceptionInformation[1]
79 // is ULONG_PTR. So on 32-bit, their sizes don't match.
80 CORDB_ADDRESS pTargetBase = GetExceptionInfoAsAddress(pRecord, 1);
81 if (pTargetBase != pClrBaseAddress)
82 {
83 return NULL;
84 }
85#endif
86
87 // It passes all the format checks. So now get the payload.
88 CORDB_ADDRESS ptrRemoteManagedEvent = GetExceptionInfoAsAddress(pRecord, 2);
89
90 return ptrRemoteManagedEvent;
91}
92
93#if defined(FEATURE_DBGIPC_TRANSPORT_VM) || defined(FEATURE_DBGIPC_TRANSPORT_DI)
94void InitEventForDebuggerNotification(DEBUG_EVENT * pDebugEvent,
95 CORDB_ADDRESS pClrBaseAddress,
96 DebuggerIPCEvent * pIPCEvent)
97{
98 pDebugEvent->dwDebugEventCode = EXCEPTION_DEBUG_EVENT;
99
100 pDebugEvent->u.Exception.dwFirstChance = TRUE;
101 pDebugEvent->u.Exception.ExceptionRecord.ExceptionCode = CLRDBG_NOTIFICATION_EXCEPTION_CODE;
102 pDebugEvent->u.Exception.ExceptionRecord.ExceptionFlags = 0;
103 pDebugEvent->u.Exception.ExceptionRecord.ExceptionRecord = NULL;
104 pDebugEvent->u.Exception.ExceptionRecord.ExceptionAddress = 0;
105
106 //
107 // Format of an ExceptionInformation parameter is:
108 // 0: cookie (CLRDBG_EXCEPTION_DATA_CHECKSUM)
109 // 1: Base address of mscorwks. This identifies the instance of the CLR.
110 // 2: Target Address of DebuggerIPCEvent, which contains the "real" event.
111 //
112 pDebugEvent->u.Exception.ExceptionRecord.NumberParameters = 3;
113 pDebugEvent->u.Exception.ExceptionRecord.ExceptionInformation[0] = CLRDBG_EXCEPTION_DATA_CHECKSUM;
114 pDebugEvent->u.Exception.ExceptionRecord.ExceptionInformation[1] = (ULONG_PTR)CORDB_ADDRESS_TO_PTR(pClrBaseAddress);
115 pDebugEvent->u.Exception.ExceptionRecord.ExceptionInformation[2] = (ULONG_PTR)pIPCEvent;
116
117 _ASSERTE(IsEventDebuggerNotification(&(pDebugEvent->u.Exception.ExceptionRecord), pClrBaseAddress) ==
118 PTR_TO_CORDB_ADDRESS(pIPCEvent));
119}
120#endif // defined(FEATURE_DBGIPC_TRANSPORT_VM) || defined(FEATURE_DBGIPC_TRANSPORT_DI)
121
122//-----------------------------------------------------------------------------
123// Helper to get the proper decorated name
124// Caller ensures that pBufSize is large enough. We'll assert just to check,
125// but no runtime failure.
126// pBuf - the output buffer to write the decorated name in
127// cBufSizeInChars - the size of the buffer in characters, including the null.
128// pPrefx - The undecorated name of the event.
129//-----------------------------------------------------------------------------
130void GetPidDecoratedName(__out_z __out_ecount(cBufSizeInChars) WCHAR * pBuf, int cBufSizeInChars, const WCHAR * pPrefix, DWORD pid)
131{
132 const WCHAR szGlobal[] = W("Global\\");
133 int szGlobalLen;
134 szGlobalLen = NumItems(szGlobal) - 1;
135
136 // Caller should always give us a big enough buffer.
137 _ASSERTE(cBufSizeInChars > (int) wcslen(pPrefix) + szGlobalLen);
138
139 // PERF: We are no longer calling GetSystemMetrics in an effort to prevent
140 // superfluous DLL loading on startup. Instead, we're prepending
141 // "Global\" to named kernel objects if we are on NT5 or above. The
142 // only bad thing that results from this is that you can't debug
143 // cross-session on NT4. Big bloody deal.
144 wcscpy_s(pBuf, cBufSizeInChars, szGlobal);
145 pBuf += szGlobalLen;
146 cBufSizeInChars -= szGlobalLen;
147
148 int ret;
149 ret = _snwprintf_s(pBuf, cBufSizeInChars, _TRUNCATE, pPrefix, pid);
150
151 // Since this is all determined at compile time, we know we should have enough buffer.
152 _ASSERTE (ret != STRUNCATE);
153}
154
155//-----------------------------------------------------------------------------
156// The 'internal' version of our IL to Native map (the DebuggerILToNativeMap struct)
157// has an extra field - ICorDebugInfo::SourceTypes source. The 'external/user-visible'
158// version (COR_DEBUG_IL_TO_NATIVE_MAP) lacks that field, so we need to translate our
159// internal version to the external version.
160// "Export" seemed more succinct than "CopyInternalToExternalILToNativeMap" :)
161//-----------------------------------------------------------------------------
162void ExportILToNativeMap(ULONG32 cMap, // [in] Min size of mapExt, mapInt
163 COR_DEBUG_IL_TO_NATIVE_MAP mapExt[], // [in] Filled in here
164 struct DebuggerILToNativeMap mapInt[],// [in] Source of info
165 SIZE_T sizeOfCode) // [in] Total size of method (bytes)
166{
167 ULONG32 iMap;
168 _ASSERTE(mapExt != NULL);
169 _ASSERTE(mapInt != NULL);
170
171 for(iMap=0; iMap < cMap; iMap++)
172 {
173 mapExt[iMap].ilOffset = mapInt[iMap].ilOffset ;
174 mapExt[iMap].nativeStartOffset = mapInt[iMap].nativeStartOffset ;
175 mapExt[iMap].nativeEndOffset = mapInt[iMap].nativeEndOffset ;
176
177 // An element that has an end offset of zero, means "till the end of
178 // the method". Pretty this up so that customers don't have to care about
179 // this.
180 if ((DWORD)mapInt[iMap].source & (DWORD)ICorDebugInfo::NATIVE_END_OFFSET_UNKNOWN)
181 {
182 mapExt[iMap].nativeEndOffset = (ULONG32)sizeOfCode;
183 }
184
185#if defined(_DEBUG)
186 {
187 // UnsafeGetConfigDWORD
188 SUPPRESS_ALLOCATION_ASSERTS_IN_THIS_SCOPE;
189 static int fReturnSourceTypeForTesting = -1;
190 if (fReturnSourceTypeForTesting == -1)
191 fReturnSourceTypeForTesting = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_ReturnSourceTypeForTesting);
192
193 if (fReturnSourceTypeForTesting)
194 {
195 // Steal the most significant four bits from the native end offset for the source type.
196 _ASSERTE( (mapExt[iMap].nativeEndOffset >> 28) == 0x0 );
197 _ASSERTE( (ULONG32)(mapInt[iMap].source) < 0xF );
198 mapExt[iMap].nativeEndOffset |= ((ULONG32)(mapInt[iMap].source) << 28);
199 }
200 }
201#endif // _DEBUG
202 }
203}
204