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 | // debugshim.cpp |
6 | // |
7 | |
8 | // |
9 | //***************************************************************************** |
10 | |
11 | #include "debugshim.h" |
12 | #include "dbgutil.h" |
13 | #include <crtdbg.h> |
14 | #include <clrinternal.h> //has the CLR_ID_V4_DESKTOP guid in it |
15 | #include "palclr.h" |
16 | |
17 | #ifndef IMAGE_FILE_MACHINE_ARMNT |
18 | #define IMAGE_FILE_MACHINE_ARMNT 0x01c4 // ARM Thumb-2 Little-Endian |
19 | #endif |
20 | |
21 | #ifndef IMAGE_FILE_MACHINE_ARM64 |
22 | #define IMAGE_FILE_MACHINE_ARM64 0xAA64 // ARM64 Little-Endian |
23 | #endif |
24 | |
25 | // making the defines very clear, these represent the host architecture - aka |
26 | // the arch on which this code is running |
27 | #if defined(_X86_) |
28 | #define _HOST_X86_ |
29 | #elif defined(_AMD64_) |
30 | #define _HOST_AMD64_ |
31 | #elif defined(_ARM_) |
32 | #define _HOST_ARM_ |
33 | #elif defined(_ARM64_) |
34 | #define _HOST_ARM64_ |
35 | #endif |
36 | |
37 | //***************************************************************************** |
38 | // CLRDebuggingImpl implementation (ICLRDebugging) |
39 | //***************************************************************************** |
40 | |
41 | typedef HRESULT (STDAPICALLTYPE *OpenVirtualProcessImpl2FnPtr)(ULONG64 clrInstanceId, |
42 | IUnknown * pDataTarget, |
43 | LPCWSTR pDacModulePath, |
44 | CLR_DEBUGGING_VERSION * pMaxDebuggerSupportedVersion, |
45 | REFIID riid, |
46 | IUnknown ** ppInstance, |
47 | CLR_DEBUGGING_PROCESS_FLAGS * pdwFlags); |
48 | |
49 | typedef HRESULT (STDAPICALLTYPE *OpenVirtualProcessImplFnPtr)(ULONG64 clrInstanceId, |
50 | IUnknown * pDataTarget, |
51 | HMODULE hDacDll, |
52 | CLR_DEBUGGING_VERSION * pMaxDebuggerSupportedVersion, |
53 | REFIID riid, |
54 | IUnknown ** ppInstance, |
55 | CLR_DEBUGGING_PROCESS_FLAGS * pdwFlags); |
56 | |
57 | typedef HRESULT (STDAPICALLTYPE *OpenVirtualProcess2FnPtr)(ULONG64 clrInstanceId, |
58 | IUnknown * pDataTarget, |
59 | HMODULE hDacDll, |
60 | REFIID riid, |
61 | IUnknown ** ppInstance, |
62 | CLR_DEBUGGING_PROCESS_FLAGS * pdwFlags); |
63 | |
64 | typedef HMODULE (STDAPICALLTYPE *LoadLibraryWFnPtr)(LPCWSTR lpLibFileName); |
65 | |
66 | // Implementation of ICLRDebugging::OpenVirtualProcess |
67 | // |
68 | // Arguments: |
69 | // moduleBaseAddress - the address of the module which might be a CLR |
70 | // pDataTarget - the data target for inspecting the process |
71 | // pLibraryProvider - a callback for locating DBI and DAC |
72 | // pMaxDebuggerSupportedVersion - the max version of the CLR that this debugger will support debugging |
73 | // riidProcess - the IID of the interface that should be passed back in ppProcess |
74 | // ppProcess - output for the ICorDebugProcess# if this module is a CLR |
75 | // pVersion - the CLR version if this module is a CLR |
76 | // pFlags - output, see the CLR_DEBUGGING_PROCESS_FLAGS for more details. Right now this has only one possible |
77 | // value which indicates this runtime had an unhandled exception |
78 | STDMETHODIMP CLRDebuggingImpl::OpenVirtualProcess( |
79 | ULONG64 moduleBaseAddress, |
80 | IUnknown * pDataTarget, |
81 | ICLRDebuggingLibraryProvider * pLibraryProvider, |
82 | CLR_DEBUGGING_VERSION * pMaxDebuggerSupportedVersion, |
83 | REFIID riidProcess, |
84 | IUnknown ** ppProcess, |
85 | CLR_DEBUGGING_VERSION * pVersion, |
86 | CLR_DEBUGGING_PROCESS_FLAGS * pFlags) |
87 | { |
88 | //PRECONDITION(CheckPointer(pDataTarget)); |
89 | |
90 | HRESULT hr = S_OK; |
91 | ICorDebugDataTarget * pDt = NULL; |
92 | HMODULE hDbi = NULL; |
93 | HMODULE hDac = NULL; |
94 | LPWSTR pDacModulePath = NULL; |
95 | LPWSTR pDbiModulePath = NULL; |
96 | DWORD dbiTimestamp; |
97 | DWORD dbiSizeOfImage; |
98 | WCHAR dbiName[MAX_PATH_FNAME] = { 0 }; |
99 | DWORD dacTimestamp; |
100 | DWORD dacSizeOfImage; |
101 | WCHAR dacName[MAX_PATH_FNAME] = { 0 }; |
102 | CLR_DEBUGGING_VERSION version; |
103 | BOOL versionSupportedByCaller = FALSE; |
104 | |
105 | // argument checking |
106 | if ((ppProcess != NULL || pFlags != NULL) && pLibraryProvider == NULL) |
107 | { |
108 | hr = E_POINTER; // the library provider must be specified if either |
109 | // ppProcess or pFlags is non-NULL |
110 | } |
111 | else if ((ppProcess != NULL || pFlags != NULL) && pMaxDebuggerSupportedVersion == NULL) |
112 | { |
113 | hr = E_POINTER; // the max supported version must be specified if either |
114 | // ppProcess or pFlags is non-NULL |
115 | } |
116 | else if (pVersion != NULL && pVersion->wStructVersion != 0) |
117 | { |
118 | hr = CORDBG_E_UNSUPPORTED_VERSION_STRUCT; |
119 | } |
120 | else if (FAILED(pDataTarget->QueryInterface(__uuidof(ICorDebugDataTarget), (void**)&pDt))) |
121 | { |
122 | hr = CORDBG_E_MISSING_DATA_TARGET_INTERFACE; |
123 | } |
124 | |
125 | if (SUCCEEDED(hr)) |
126 | { |
127 | // get CLR version |
128 | // The expectation is that new versions of the CLR will continue to use the same GUID |
129 | // (unless there's a reason to hide them from older shims), but debuggers will tell us the |
130 | // CLR version they're designed for and mscordbi.dll can decide whether or not to accept it. |
131 | version.wStructVersion = 0; |
132 | hr = GetCLRInfo(pDt, |
133 | moduleBaseAddress, |
134 | &version, |
135 | &dbiTimestamp, |
136 | &dbiSizeOfImage, |
137 | dbiName, |
138 | MAX_PATH_FNAME, |
139 | &dacTimestamp, |
140 | &dacSizeOfImage, |
141 | dacName, |
142 | MAX_PATH_FNAME); |
143 | } |
144 | |
145 | // If we need to fetch either the process info or the flags info then we need to find |
146 | // mscordbi and DAC and do the version specific OVP work |
147 | if (SUCCEEDED(hr) && (ppProcess != NULL || pFlags != NULL)) |
148 | { |
149 | ICLRDebuggingLibraryProvider2* pLibraryProvider2; |
150 | if (SUCCEEDED(pLibraryProvider->QueryInterface(__uuidof(ICLRDebuggingLibraryProvider2), (void**)&pLibraryProvider2))) |
151 | { |
152 | if (FAILED(pLibraryProvider2->ProvideLibrary2(dbiName, dbiTimestamp, dbiSizeOfImage, &pDbiModulePath)) || |
153 | pDbiModulePath == NULL) |
154 | { |
155 | hr = CORDBG_E_LIBRARY_PROVIDER_ERROR; |
156 | } |
157 | |
158 | if (SUCCEEDED(hr)) |
159 | { |
160 | hDbi = LoadLibraryW(pDbiModulePath); |
161 | if (hDbi == NULL) |
162 | { |
163 | hr = HRESULT_FROM_WIN32(GetLastError()); |
164 | } |
165 | } |
166 | |
167 | if (SUCCEEDED(hr)) |
168 | { |
169 | // Adjust the timestamp and size of image if this DAC is a known buggy version and needs to be retargeted |
170 | RetargetDacIfNeeded(&dacTimestamp, &dacSizeOfImage); |
171 | |
172 | // Ask library provider for dac |
173 | if (FAILED(pLibraryProvider2->ProvideLibrary2(dacName, dacTimestamp, dacSizeOfImage, &pDacModulePath)) || |
174 | pDacModulePath == NULL) |
175 | { |
176 | hr = CORDBG_E_LIBRARY_PROVIDER_ERROR; |
177 | } |
178 | |
179 | if (SUCCEEDED(hr)) |
180 | { |
181 | hDac = LoadLibraryW(pDacModulePath); |
182 | if (hDac == NULL) |
183 | { |
184 | hr = HRESULT_FROM_WIN32(GetLastError()); |
185 | } |
186 | } |
187 | } |
188 | |
189 | pLibraryProvider2->Release(); |
190 | } |
191 | else { |
192 | // Ask library provider for dbi |
193 | if (FAILED(pLibraryProvider->ProvideLibrary(dbiName, dbiTimestamp, dbiSizeOfImage, &hDbi)) || |
194 | hDbi == NULL) |
195 | { |
196 | hr = CORDBG_E_LIBRARY_PROVIDER_ERROR; |
197 | } |
198 | |
199 | if (SUCCEEDED(hr)) |
200 | { |
201 | // Adjust the timestamp and size of image if this DAC is a known buggy version and needs to be retargeted |
202 | RetargetDacIfNeeded(&dacTimestamp, &dacSizeOfImage); |
203 | |
204 | // ask library provider for dac |
205 | if (FAILED(pLibraryProvider->ProvideLibrary(dacName, dacTimestamp, dacSizeOfImage, &hDac)) || |
206 | hDac == NULL) |
207 | { |
208 | hr = CORDBG_E_LIBRARY_PROVIDER_ERROR; |
209 | } |
210 | } |
211 | } |
212 | |
213 | *ppProcess = NULL; |
214 | |
215 | if (SUCCEEDED(hr) && pDacModulePath != NULL) |
216 | { |
217 | // Get access to the latest OVP implementation and call it |
218 | OpenVirtualProcessImpl2FnPtr ovpFn = (OpenVirtualProcessImpl2FnPtr)GetProcAddress(hDbi, "OpenVirtualProcessImpl2" ); |
219 | if (ovpFn != NULL) |
220 | { |
221 | hr = ovpFn(moduleBaseAddress, pDataTarget, pDacModulePath, pMaxDebuggerSupportedVersion, riidProcess, ppProcess, pFlags); |
222 | if (FAILED(hr)) |
223 | { |
224 | _ASSERTE(ppProcess == NULL || *ppProcess == NULL); |
225 | _ASSERTE(pFlags == NULL || *pFlags == 0); |
226 | } |
227 | } |
228 | #ifdef FEATURE_PAL |
229 | else |
230 | { |
231 | // On Linux/MacOS the DAC module handle needs to be re-created using the DAC PAL instance |
232 | // before being passed to DBI's OpenVirtualProcess* implementation. The DBI and DAC share |
233 | // the same PAL where dbgshim has it's own. |
234 | LoadLibraryWFnPtr loadLibraryWFn = (LoadLibraryWFnPtr)GetProcAddress(hDac, "LoadLibraryW" ); |
235 | if (loadLibraryWFn != NULL) |
236 | { |
237 | hDac = loadLibraryWFn(pDacModulePath); |
238 | if (hDac == NULL) |
239 | { |
240 | hr = E_HANDLE; |
241 | } |
242 | } |
243 | else |
244 | { |
245 | hr = E_HANDLE; |
246 | } |
247 | } |
248 | #endif // FEATURE_PAL |
249 | } |
250 | |
251 | // If no errors so far and "OpenVirtualProcessImpl2" doesn't exist |
252 | if (SUCCEEDED(hr) && *ppProcess == NULL) |
253 | { |
254 | // Get access to OVP and call it |
255 | OpenVirtualProcessImplFnPtr ovpFn = (OpenVirtualProcessImplFnPtr)GetProcAddress(hDbi, "OpenVirtualProcessImpl" ); |
256 | if (ovpFn == NULL) |
257 | { |
258 | // Fallback to CLR v4 Beta1 path, but skip some of the checking we'd normally do (maxSupportedVersion, etc.) |
259 | OpenVirtualProcess2FnPtr ovp2Fn = (OpenVirtualProcess2FnPtr)GetProcAddress(hDbi, "OpenVirtualProcess2" ); |
260 | if (ovp2Fn == NULL) |
261 | { |
262 | hr = CORDBG_E_LIBRARY_PROVIDER_ERROR; |
263 | } |
264 | else |
265 | { |
266 | hr = ovp2Fn(moduleBaseAddress, pDataTarget, hDac, riidProcess, ppProcess, pFlags); |
267 | } |
268 | } |
269 | else |
270 | { |
271 | // Have a CLR v4 Beta2+ DBI, call it and let it do the version check |
272 | hr = ovpFn(moduleBaseAddress, pDataTarget, hDac, pMaxDebuggerSupportedVersion, riidProcess, ppProcess, pFlags); |
273 | if (FAILED(hr)) |
274 | { |
275 | _ASSERTE(ppProcess == NULL || *ppProcess == NULL); |
276 | _ASSERTE(pFlags == NULL || *pFlags == 0); |
277 | } |
278 | } |
279 | } |
280 | } |
281 | |
282 | //version is still valid in some failure cases |
283 | if (pVersion != NULL && |
284 | (SUCCEEDED(hr) || |
285 | (hr == CORDBG_E_UNSUPPORTED_DEBUGGING_MODEL) || |
286 | (hr == CORDBG_E_UNSUPPORTED_FORWARD_COMPAT))) |
287 | { |
288 | memcpy(pVersion, &version, sizeof(CLR_DEBUGGING_VERSION)); |
289 | } |
290 | |
291 | if (pDacModulePath != NULL) |
292 | { |
293 | #ifdef FEATURE_PAL |
294 | free(pDacModulePath); |
295 | #else |
296 | CoTaskMemFree(pDacModulePath); |
297 | #endif |
298 | } |
299 | |
300 | if (pDbiModulePath != NULL) |
301 | { |
302 | #ifdef FEATURE_PAL |
303 | free(pDbiModulePath); |
304 | #else |
305 | CoTaskMemFree(pDbiModulePath); |
306 | #endif |
307 | } |
308 | |
309 | // free the data target we QI'ed earlier |
310 | if (pDt != NULL) |
311 | { |
312 | pDt->Release(); |
313 | } |
314 | |
315 | return hr; |
316 | } |
317 | |
318 | // Checks to see if this DAC is one of a known set of old DAC builds which contains an issue. |
319 | // If so we retarget to a newer compatible version which has the bug fixed. This is done |
320 | // by changing the PE information used to lookup the DAC. |
321 | // |
322 | // Arguments |
323 | // pdwTimeStamp - on input, the timestamp of DAC as embedded in the CLR image |
324 | // on output, a potentially new timestamp for an updated DAC to use |
325 | // instead |
326 | // pdwSizeOfImage - on input, the sizeOfImage of DAC as embedded in the CLR image |
327 | // on output, a potentially new sizeOfImage for an updated DAC to use |
328 | // instead |
329 | VOID CLRDebuggingImpl::RetargetDacIfNeeded(DWORD* pdwTimeStamp, |
330 | DWORD* pdwSizeOfImage) |
331 | { |
332 | |
333 | // This code is auto generated by the CreateRetargetTable tool |
334 | // on 3/4/2011 6:35 PM |
335 | // and then copy-pasted here. |
336 | // |
337 | // |
338 | // |
339 | // Retarget the GDR1 amd64 build |
340 | if( (*pdwTimeStamp == 0x4d536868) && (*pdwSizeOfImage == 0x17b000)) |
341 | { |
342 | *pdwTimeStamp = 0x4d71a160; |
343 | *pdwSizeOfImage = 0x17b000; |
344 | } |
345 | // Retarget the GDR1 x86 build |
346 | else if( (*pdwTimeStamp == 0x4d5368f2) && (*pdwSizeOfImage == 0x120000)) |
347 | { |
348 | *pdwTimeStamp = 0x4d71a14f; |
349 | *pdwSizeOfImage = 0x120000; |
350 | } |
351 | // Retarget the RTM amd64 build |
352 | else if( (*pdwTimeStamp == 0x4ba21fa7) && (*pdwSizeOfImage == 0x17b000)) |
353 | { |
354 | *pdwTimeStamp = 0x4d71a13c; |
355 | *pdwSizeOfImage = 0x17b000; |
356 | } |
357 | // Retarget the RTM x86 build |
358 | else if( (*pdwTimeStamp == 0x4ba1da25) && (*pdwSizeOfImage == 0x120000)) |
359 | { |
360 | *pdwTimeStamp = 0x4d71a128; |
361 | *pdwSizeOfImage = 0x120000; |
362 | } |
363 | // This code is auto generated by the CreateRetargetTable tool |
364 | // on 8/17/2011 1:28 AM |
365 | // and then copy-pasted here. |
366 | // |
367 | // |
368 | // |
369 | // Retarget the GDR2 amd64 build |
370 | else if( (*pdwTimeStamp == 0x4da428c7) && (*pdwSizeOfImage == 0x17b000)) |
371 | { |
372 | *pdwTimeStamp = 0x4e4b7bc2; |
373 | *pdwSizeOfImage = 0x17b000; |
374 | } |
375 | // Retarget the GDR2 x86 build |
376 | else if( (*pdwTimeStamp == 0x4da3fe52) && (*pdwSizeOfImage == 0x120000)) |
377 | { |
378 | *pdwTimeStamp = 0x4e4b7bb1; |
379 | *pdwSizeOfImage = 0x120000; |
380 | } |
381 | // End auto-generated code |
382 | } |
383 | |
384 | #define PE_FIXEDFILEINFO_SIGNATURE 0xFEEF04BD |
385 | |
386 | // The format of the special debugging resource we embed in CLRs starting in |
387 | // v4 |
388 | struct CLR_DEBUG_RESOURCE |
389 | { |
390 | DWORD dwVersion; |
391 | GUID signature; |
392 | DWORD dwDacTimeStamp; |
393 | DWORD dwDacSizeOfImage; |
394 | DWORD dwDbiTimeStamp; |
395 | DWORD dwDbiSizeOfImage; |
396 | }; |
397 | |
398 | // Checks to see if a module is a CLR and if so, fetches the debug data |
399 | // from the embedded resource |
400 | // |
401 | // Arguments |
402 | // pDataTarget - dataTarget for the process we are inspecting |
403 | // moduleBaseAddress - base address of a module we should inspect |
404 | // pVersion - output, the version of the CLR detected if this is a CLR |
405 | // pdwDbiTimeStamp - the timestamp of DBI as embedded in the CLR image |
406 | // pdwDbiSizeOfImage - the SizeOfImage of DBI as embedded in the CLR image |
407 | // pDbiName - output, the filename of DBI (as calculated by this function but that might change) |
408 | // dwDbiNameCharCount - input, the number of WCHARs in the buffer pointed to by pDbiName |
409 | // pdwDacTimeStampe - the timestamp of DAC as embedded in the CLR image |
410 | // pdwDacSizeOfImage - the SizeOfImage of DAC as embedded in the CLR image |
411 | // pDacName - output, the filename of DAC (as calculated by this function but that might change) |
412 | // dwDacNameCharCount - input, the number of WCHARs in the buffer pointed to by pDacName |
413 | HRESULT CLRDebuggingImpl::GetCLRInfo(ICorDebugDataTarget* pDataTarget, |
414 | ULONG64 moduleBaseAddress, |
415 | CLR_DEBUGGING_VERSION* pVersion, |
416 | DWORD* pdwDbiTimeStamp, |
417 | DWORD* pdwDbiSizeOfImage, |
418 | __out_z __inout_ecount(dwDbiNameCharCount) WCHAR* pDbiName, |
419 | DWORD dwDbiNameCharCount, |
420 | DWORD* pdwDacTimeStamp, |
421 | DWORD* pdwDacSizeOfImage, |
422 | __out_z __inout_ecount(dwDacNameCharCount) WCHAR* pDacName, |
423 | DWORD dwDacNameCharCount) |
424 | { |
425 | #ifndef FEATURE_PAL |
426 | WORD imageFileMachine = 0; |
427 | DWORD resourceSectionRVA = 0; |
428 | HRESULT hr = GetMachineAndResourceSectionRVA(pDataTarget, moduleBaseAddress, &imageFileMachine, &resourceSectionRVA); |
429 | |
430 | // We want the version resource which has type = RT_VERSION = 16, name = 1, language = 0x409 |
431 | DWORD versionResourceRVA = 0; |
432 | DWORD versionResourceSize = 0; |
433 | if(SUCCEEDED(hr)) |
434 | { |
435 | hr = GetResourceRvaFromResourceSectionRva(pDataTarget, moduleBaseAddress, resourceSectionRVA, 16, 1, 0x409, |
436 | &versionResourceRVA, &versionResourceSize); |
437 | } |
438 | |
439 | // At last we get our version info |
440 | VS_FIXEDFILEINFO fixedFileInfo = {0}; |
441 | if(SUCCEEDED(hr)) |
442 | { |
443 | // The version resource has 3 words, then the unicode string "VS_VERSION_INFO" |
444 | // (16 WCHARS including the null terminator) |
445 | // then padding to a 32-bit boundary, then the VS_FIXEDFILEINFO struct |
446 | DWORD fixedFileInfoRVA = ((versionResourceRVA + 3*2 + 16*2 + 3)/4)*4; |
447 | hr = ReadFromDataTarget(pDataTarget, moduleBaseAddress + fixedFileInfoRVA, (BYTE*)&fixedFileInfo, sizeof(fixedFileInfo)); |
448 | } |
449 | |
450 | //Verify the signature on the version resource |
451 | if(SUCCEEDED(hr) && fixedFileInfo.dwSignature != PE_FIXEDFILEINFO_SIGNATURE) |
452 | { |
453 | hr = CORDBG_E_NOT_CLR; |
454 | } |
455 | |
456 | // Record the version information |
457 | if(SUCCEEDED(hr)) |
458 | { |
459 | pVersion->wMajor = (WORD) (fixedFileInfo.dwProductVersionMS >> 16); |
460 | pVersion->wMinor = (WORD) (fixedFileInfo.dwProductVersionMS & 0xFFFF); |
461 | pVersion->wBuild = (WORD) (fixedFileInfo.dwProductVersionLS >> 16); |
462 | pVersion->wRevision = (WORD) (fixedFileInfo.dwProductVersionLS & 0xFFFF); |
463 | } |
464 | |
465 | // Now grab the special clr debug info resource |
466 | // We may need to scan a few different names searching though... |
467 | // 1) CLRDEBUGINFO<host_os><host_arch> where host_os = 'WINDOWS' or 'CORESYS' and host_arch = 'X86' or 'ARM' or 'AMD64' |
468 | // 2) For back-compat if the host os is windows and the host architecture matches the target then CLRDEBUGINFO is used with no suffix. |
469 | DWORD debugResourceRVA = 0; |
470 | DWORD debugResourceSize = 0; |
471 | BOOL useCrossPlatformNaming = FALSE; |
472 | if(SUCCEEDED(hr)) |
473 | { |
474 | // the initial state is that we haven't found a proper resource |
475 | HRESULT hrGetResource = E_FAIL; |
476 | |
477 | // First check for the resource which has type = RC_DATA = 10, name = "CLRDEBUGINFO<host_os><host_arch>", language = 0 |
478 | #if defined (HOST_IS_WINDOWS_OS) && defined(_HOST_X86_) |
479 | const WCHAR * resourceName = W("CLRDEBUGINFOWINDOWSX86" ); |
480 | #endif |
481 | |
482 | #if !defined (HOST_IS_WINDOWS_OS) && defined(_HOST_X86_) |
483 | const WCHAR * resourceName = W("CLRDEBUGINFOCORESYSX86" ); |
484 | #endif |
485 | |
486 | #if defined (HOST_IS_WINDOWS_OS) && defined(_HOST_AMD64_) |
487 | const WCHAR * resourceName = W("CLRDEBUGINFOWINDOWSAMD64" ); |
488 | #endif |
489 | |
490 | #if !defined (HOST_IS_WINDOWS_OS) && defined(_HOST_AMD64_) |
491 | const WCHAR * resourceName = W("CLRDEBUGINFOCORESYSAMD64" ); |
492 | #endif |
493 | |
494 | #if defined (HOST_IS_WINDOWS_OS) && defined(_HOST_ARM64_) |
495 | const WCHAR * resourceName = W("CLRDEBUGINFOWINDOWSARM64" ); |
496 | #endif |
497 | |
498 | #if !defined (HOST_IS_WINDOWS_OS) && defined(_HOST_ARM64_) |
499 | const WCHAR * resourceName = W("CLRDEBUGINFOCORESYSARM64" ); |
500 | #endif |
501 | |
502 | #if defined (HOST_IS_WINDOWS_OS) && defined(_HOST_ARM_) |
503 | const WCHAR * resourceName = W("CLRDEBUGINFOWINDOWSARM" ); |
504 | #endif |
505 | |
506 | #if !defined (HOST_IS_WINDOWS_OS) && defined(_HOST_ARM_) |
507 | const WCHAR * resourceName = W("CLRDEBUGINFOCORESYSARM" ); |
508 | #endif |
509 | |
510 | hrGetResource = GetResourceRvaFromResourceSectionRvaByName(pDataTarget, moduleBaseAddress, resourceSectionRVA, 10, resourceName, 0, |
511 | &debugResourceRVA, &debugResourceSize); |
512 | useCrossPlatformNaming = SUCCEEDED(hrGetResource); |
513 | |
514 | |
515 | #if defined(HOST_IS_WINDOWS_OS) && (defined(_HOST_X86_) || defined(_HOST_AMD64_) || defined(_HOST_ARM_)) |
516 | #if defined(_HOST_X86_) |
517 | #define _HOST_MACHINE_TYPE IMAGE_FILE_MACHINE_I386 |
518 | #elif defined(_HOST_AMD64_) |
519 | #define _HOST_MACHINE_TYPE IMAGE_FILE_MACHINE_AMD64 |
520 | #elif defined(_HOST_ARM_) |
521 | #define _HOST_MACHINE_TYPE IMAGE_FILE_MACHINE_ARMNT |
522 | #endif |
523 | |
524 | // if this is windows, and if host_arch matches target arch then we can fallback to searching for CLRDEBUGINFO on failure |
525 | if(FAILED(hrGetResource) && (imageFileMachine == _HOST_MACHINE_TYPE)) |
526 | { |
527 | hrGetResource = GetResourceRvaFromResourceSectionRvaByName(pDataTarget, moduleBaseAddress, resourceSectionRVA, 10, W("CLRDEBUGINFO" ), 0, |
528 | &debugResourceRVA, &debugResourceSize); |
529 | } |
530 | |
531 | #undef _HOST_MACHINE_TYPE |
532 | #endif |
533 | // if the search failed, we don't recognize the CLR |
534 | if(FAILED(hrGetResource)) |
535 | hr = CORDBG_E_NOT_CLR; |
536 | } |
537 | |
538 | CLR_DEBUG_RESOURCE debugResource; |
539 | if(SUCCEEDED(hr) && debugResourceSize != sizeof(debugResource)) |
540 | { |
541 | hr = CORDBG_E_NOT_CLR; |
542 | } |
543 | |
544 | // Get the special debug resource from the image and return the results |
545 | if(SUCCEEDED(hr)) |
546 | { |
547 | hr = ReadFromDataTarget(pDataTarget, moduleBaseAddress + debugResourceRVA, (BYTE*)&debugResource, sizeof(debugResource)); |
548 | } |
549 | if(SUCCEEDED(hr) && (debugResource.dwVersion != 0)) |
550 | { |
551 | hr = CORDBG_E_NOT_CLR; |
552 | } |
553 | |
554 | // The signature needs to match m_skuId exactly, except for m_skuId=CLR_ID_ONECORE_CLR which is |
555 | // also compatible with the older CLR_ID_PHONE_CLR signature. |
556 | if(SUCCEEDED(hr) && |
557 | (debugResource.signature != m_skuId) && |
558 | !( (debugResource.signature == CLR_ID_PHONE_CLR) && (m_skuId == CLR_ID_ONECORE_CLR) )) |
559 | { |
560 | hr = CORDBG_E_NOT_CLR; |
561 | } |
562 | |
563 | if(SUCCEEDED(hr) && |
564 | (debugResource.signature != CLR_ID_ONECORE_CLR) && |
565 | useCrossPlatformNaming) |
566 | { |
567 | FormatLongDacModuleName(pDacName, dwDacNameCharCount, imageFileMachine, &fixedFileInfo); |
568 | swprintf_s(pDbiName, dwDbiNameCharCount, W("%s_%s.dll" ), MAIN_DBI_MODULE_NAME_W, W("x86" )); |
569 | } |
570 | else |
571 | { |
572 | if(m_skuId == CLR_ID_V4_DESKTOP) |
573 | swprintf_s(pDacName, dwDacNameCharCount, W("%s.dll" ), CLR_DAC_MODULE_NAME_W); |
574 | else |
575 | swprintf_s(pDacName, dwDacNameCharCount, W("%s.dll" ), CORECLR_DAC_MODULE_NAME_W); |
576 | swprintf_s(pDbiName, dwDbiNameCharCount, W("%s.dll" ), MAIN_DBI_MODULE_NAME_W); |
577 | } |
578 | |
579 | if(SUCCEEDED(hr)) |
580 | { |
581 | *pdwDbiTimeStamp = debugResource.dwDbiTimeStamp; |
582 | *pdwDbiSizeOfImage = debugResource.dwDbiSizeOfImage; |
583 | *pdwDacTimeStamp = debugResource.dwDacTimeStamp; |
584 | *pdwDacSizeOfImage = debugResource.dwDacSizeOfImage; |
585 | } |
586 | |
587 | // any failure should be interpreted as this module not being a CLR |
588 | if(FAILED(hr)) |
589 | { |
590 | return CORDBG_E_NOT_CLR; |
591 | } |
592 | else |
593 | { |
594 | return S_OK; |
595 | } |
596 | #else |
597 | swprintf_s(pDacName, dwDacNameCharCount, W("%s" ), MAKEDLLNAME_W(CORECLR_DAC_MODULE_NAME_W)); |
598 | swprintf_s(pDbiName, dwDbiNameCharCount, W("%s" ), MAKEDLLNAME_W(MAIN_DBI_MODULE_NAME_W)); |
599 | |
600 | pVersion->wMajor = 0; |
601 | pVersion->wMinor = 0; |
602 | pVersion->wBuild = 0; |
603 | pVersion->wRevision = 0; |
604 | |
605 | *pdwDbiTimeStamp = 0; |
606 | *pdwDbiSizeOfImage = 0; |
607 | *pdwDacTimeStamp = 0; |
608 | *pdwDacSizeOfImage = 0; |
609 | |
610 | return S_OK; |
611 | #endif // FEATURE_PAL |
612 | } |
613 | |
614 | // Formats the long name for DAC |
615 | HRESULT CLRDebuggingImpl::FormatLongDacModuleName(__out_z __inout_ecount(cchBuffer) WCHAR * pBuffer, |
616 | DWORD cchBuffer, |
617 | DWORD targetImageFileMachine, |
618 | VS_FIXEDFILEINFO * pVersion) |
619 | { |
620 | |
621 | #ifndef HOST_IS_WINDOWS_OS |
622 | _ASSERTE(!"NYI" ); |
623 | return E_NOTIMPL; |
624 | #endif |
625 | |
626 | #if defined(_HOST_X86_) |
627 | const WCHAR* pHostArch = W("x86" ); |
628 | #elif defined(_HOST_AMD64_) |
629 | const WCHAR* pHostArch = W("amd64" ); |
630 | #elif defined(_HOST_ARM_) |
631 | const WCHAR* pHostArch = W("arm" ); |
632 | #elif defined(_HOST_ARM64_) |
633 | const WCHAR* pHostArch = W("arm64" ); |
634 | #else |
635 | _ASSERTE(!"Unknown host arch" ); |
636 | return E_NOTIMPL; |
637 | #endif |
638 | |
639 | const WCHAR* pDacBaseName = NULL; |
640 | if(m_skuId == CLR_ID_V4_DESKTOP) |
641 | pDacBaseName = CLR_DAC_MODULE_NAME_W; |
642 | else if(m_skuId == CLR_ID_CORECLR || m_skuId == CLR_ID_PHONE_CLR || m_skuId == CLR_ID_ONECORE_CLR) |
643 | pDacBaseName = CORECLR_DAC_MODULE_NAME_W; |
644 | else |
645 | { |
646 | _ASSERTE(!"Unknown SKU id" ); |
647 | return E_UNEXPECTED; |
648 | } |
649 | |
650 | const WCHAR* pTargetArch = NULL; |
651 | if(targetImageFileMachine == IMAGE_FILE_MACHINE_I386) |
652 | { |
653 | pTargetArch = W("x86" ); |
654 | } |
655 | else if(targetImageFileMachine == IMAGE_FILE_MACHINE_AMD64) |
656 | { |
657 | pTargetArch = W("amd64" ); |
658 | } |
659 | else if(targetImageFileMachine == IMAGE_FILE_MACHINE_ARMNT) |
660 | { |
661 | pTargetArch = W("arm" ); |
662 | } |
663 | else if(targetImageFileMachine == IMAGE_FILE_MACHINE_ARM64) |
664 | { |
665 | pTargetArch = W("arm64" ); |
666 | } |
667 | else |
668 | { |
669 | _ASSERTE(!"Unknown target image file machine type" ); |
670 | return E_INVALIDARG; |
671 | } |
672 | |
673 | const WCHAR* pBuildFlavor = W("" ); |
674 | if(pVersion->dwFileFlags & VS_FF_DEBUG) |
675 | { |
676 | if(pVersion->dwFileFlags & VS_FF_SPECIALBUILD) |
677 | pBuildFlavor = W(".dbg" ); |
678 | else |
679 | pBuildFlavor = W(".chk" ); |
680 | } |
681 | |
682 | // WARNING: if you change the formatting make sure you recalculate the maximum |
683 | // possible size string and verify callers pass a big enough buffer. This doesn't |
684 | // have to be a tight estimate, just make sure its >= the biggest possible DAC name |
685 | // and it can be calculated statically |
686 | DWORD minCchBuffer = |
687 | (DWORD) wcslen(CLR_DAC_MODULE_NAME_W) + (DWORD) wcslen(CORECLR_DAC_MODULE_NAME_W) + // max name |
688 | 10 + // max host arch |
689 | 10 + // max target arch |
690 | 40 + // max version |
691 | 10 + // max build flavor |
692 | (DWORD) wcslen(W("name_host_target_version.flavor.dll" )) + // max intermediate formatting chars |
693 | 1; // null terminator |
694 | |
695 | // validate the output buffer is larger than our estimate above |
696 | _ASSERTE(cchBuffer >= minCchBuffer); |
697 | if(!(cchBuffer >= minCchBuffer)) return E_INVALIDARG; |
698 | |
699 | swprintf_s(pBuffer, cchBuffer, W("%s_%s_%s_%u.%u.%u.%02u%s.dll" ), |
700 | pDacBaseName, |
701 | pHostArch, |
702 | pTargetArch, |
703 | pVersion->dwProductVersionMS >> 16, |
704 | pVersion->dwProductVersionMS & 0xFFFF, |
705 | pVersion->dwProductVersionLS >> 16, |
706 | pVersion->dwProductVersionLS & 0xFFFF, |
707 | pBuildFlavor); |
708 | return S_OK; |
709 | } |
710 | |
711 | // An implementation of ICLRDebugging::CanUnloadNow |
712 | // |
713 | // Arguments: |
714 | // hModule - a handle to a module provided earlier by ProvideLibrary |
715 | // |
716 | // Returns: |
717 | // S_OK if the library is no longer in use and can be unloaded, S_FALSE otherwise |
718 | // |
719 | STDMETHODIMP CLRDebuggingImpl::CanUnloadNow(HMODULE hModule) |
720 | { |
721 | // In V4 at least we don't support any unloading. |
722 | HRESULT hr = S_FALSE; |
723 | |
724 | return hr; |
725 | } |
726 | |
727 | |
728 | |
729 | STDMETHODIMP CLRDebuggingImpl::QueryInterface(REFIID riid, void **ppvObject) |
730 | { |
731 | HRESULT hr = S_OK; |
732 | |
733 | if (riid == __uuidof(IUnknown)) |
734 | { |
735 | IUnknown *pItf = static_cast<IUnknown *>(this); |
736 | pItf->AddRef(); |
737 | *ppvObject = pItf; |
738 | } |
739 | else if (riid == __uuidof(ICLRDebugging)) |
740 | { |
741 | ICLRDebugging *pItf = static_cast<ICLRDebugging *>(this); |
742 | pItf->AddRef(); |
743 | *ppvObject = pItf; |
744 | } |
745 | else |
746 | hr = E_NOINTERFACE; |
747 | |
748 | return hr; |
749 | } |
750 | |
751 | // Standard AddRef implementation |
752 | ULONG CLRDebuggingImpl::AddRef() |
753 | { |
754 | return InterlockedIncrement(&m_cRef); |
755 | } |
756 | |
757 | // Standard Release implementation. |
758 | ULONG CLRDebuggingImpl::Release() |
759 | { |
760 | _ASSERTE(m_cRef > 0); |
761 | |
762 | ULONG cRef = InterlockedDecrement(&m_cRef); |
763 | |
764 | if (cRef == 0) |
765 | delete this; // Relies on virtual dtor to work properly. |
766 | |
767 | return cRef; |
768 | } |
769 | |