Merge remote-tracking branch 'upstream/master' into dacglobals1
[platform/upstream/coreclr.git] / src / debug / di / shimlocaldatatarget.cpp
1 //
2 // Copyright (c) Microsoft. All rights reserved.
3 // Licensed under the MIT license. See LICENSE file in the project root for full license information.
4 //
5 //*****************************************************************************
6
7 // 
8 // File: ShimLocalDataTarget.cpp
9 //
10 //*****************************************************************************
11 #include "stdafx.h"
12 #include "safewrap.h"
13
14 #include "check.h" 
15
16 #include <limits.h>
17
18 #include "shimpriv.h"
19 #include "shimdatatarget.h"
20
21
22 // The Shim's Live data-target is allowed to call OS APIs directly.
23 // see code:RSDebuggingInfo#UseDataTarget.
24 #undef ReadProcessMemory
25 #undef WriteProcessMemory
26
27
28 class ShimLocalDataTarget : public ShimDataTarget
29 {
30 public:
31     ShimLocalDataTarget(DWORD processId, HANDLE hProcess);
32
33     ~ShimLocalDataTarget();
34
35     virtual void Dispose();
36
37     //
38     // ICorDebugMutableDataTarget.
39     //
40
41     virtual HRESULT STDMETHODCALLTYPE GetPlatform( 
42         CorDebugPlatform *pPlatform);
43
44     virtual HRESULT STDMETHODCALLTYPE ReadVirtual( 
45         CORDB_ADDRESS address,
46         BYTE * pBuffer,
47         ULONG32 request,
48         ULONG32 *pcbRead);
49
50     virtual HRESULT STDMETHODCALLTYPE WriteVirtual( 
51         CORDB_ADDRESS address,
52         const BYTE * pBuffer,
53         ULONG32 request);
54
55     virtual HRESULT STDMETHODCALLTYPE GetThreadContext(
56         DWORD dwThreadID,
57         ULONG32 contextFlags,
58         ULONG32 contextSize,
59         BYTE * context);
60
61     virtual HRESULT STDMETHODCALLTYPE SetThreadContext(
62         DWORD dwThreadID,
63         ULONG32 contextSize,
64         const BYTE * context);
65
66     virtual HRESULT STDMETHODCALLTYPE ContinueStatusChanged(
67         DWORD dwThreadId,
68         CORDB_CONTINUE_STATUS dwContinueStatus);
69
70 private:
71     // Handle to the process. We own this.
72     HANDLE m_hProcess;
73 };
74
75
76 // Determines whether the target and host are running on compatible platforms.
77 // Arguments:
78 //     input: hTargetProcess - handle for the target process
79 // Return Value: TRUE iff both target and host are both Wow64 or neither is. 
80 // Note: throws
81 BOOL CompatibleHostAndTargetPlatforms(HANDLE hTargetProcess)
82 {
83 #if defined(FEATURE_PAL)    
84     return TRUE;
85 #else
86     // get the platform for the host process
87     BOOL fHostProcessIsWow64 = FALSE;
88     BOOL fSuccess = FALSE;            
89     HANDLE hHostProcess = GetCurrentProcess();
90
91     fSuccess = IsWow64Process(hHostProcess, &fHostProcessIsWow64);
92     CloseHandle(hHostProcess);
93     hHostProcess = NULL;
94
95     if (!fSuccess)
96     {
97         ThrowHR(HRESULT_FROM_GetLastError());
98     }
99     
100     //  get the platform for the target process
101     if (hTargetProcess == NULL)
102     {
103         ThrowHR(HRESULT_FROM_GetLastError());
104     }
105
106     BOOL fTargetProcessIsWow64 = FALSE;
107     fSuccess = IsWow64Process(hTargetProcess, &fTargetProcessIsWow64);
108
109     if (!fSuccess)
110     {
111         ThrowHR(HRESULT_FROM_GetLastError());
112     }
113
114     // We don't want to expose the IPC block if one process is x86 and
115     // the other is ia64 or amd64
116     if (fTargetProcessIsWow64 != fHostProcessIsWow64)
117     {
118         return FALSE;
119     }
120     else
121     {
122         return TRUE;
123     }
124 #endif
125 } // CompatibleHostAndTargetPlatforms
126
127 // Helper macro to check for failure conditions at the start of data-target methods.
128 #define ReturnFailureIfStateNotOk() \
129     if (m_hr != S_OK) \
130     { \
131         return m_hr; \
132     }
133
134 //---------------------------------------------------------------------------------------
135 //
136 // ctor for ShimLocalDataTarget. 
137 //
138 // Arguments:
139 //      processId - pid of live process.
140 //      hProcess - handle to kernel process object.
141 //
142 // Assumptions:
143 //    Shim takes ownership of handle hProcess.
144 //
145
146 ShimLocalDataTarget::ShimLocalDataTarget(DWORD processId, HANDLE hProcess)    
147 {
148     m_ref = 0;
149
150     m_processId = processId;
151     m_hProcess = hProcess;
152
153     m_hr = S_OK;
154
155     m_fpContinueStatusChanged = NULL;
156     m_pContinueStatusChangedUserData = NULL;
157 }
158
159 //---------------------------------------------------------------------------------------
160 //
161 // dctor for ShimLocalDataTarget. 
162 //
163 ShimLocalDataTarget::~ShimLocalDataTarget()
164 {
165     Dispose();
166 }
167
168
169 //---------------------------------------------------------------------------------------
170 //
171 // Dispose all resources and neuter the object.
172 //
173 //
174 //
175 // Notes:
176 //    Release all resources (such as the handle to the process we got in the ctor).
177 //    May be called multiple times.
178 //    All other non-trivial APIs (eg, not IUnknown) will fail after this.
179 //
180
181 void ShimLocalDataTarget::Dispose()
182 {
183     if (m_hProcess != NULL)
184     {
185         CloseHandle(m_hProcess);
186         m_hProcess = NULL;
187     }
188     m_hr = CORDBG_E_OBJECT_NEUTERED;
189 }
190
191 //---------------------------------------------------------------------------------------
192 //
193 // Construction method for data-target
194 //
195 // Arguments:
196 //      processId - (input) live OS process ID to build a data-target for.
197 //      ppDataTarget - (output) new data-target instance. This gets addreffed.
198 //
199 // Return Value:
200 //    S_OK on success.
201 //
202 // Assumptions:
203 //    pid must be for local, same architecture, process.
204 //    Caller must have security permissions for OpenProcess()
205 //    Caller must release *ppDataTarget.
206 //
207
208 HRESULT BuildPlatformSpecificDataTarget(MachineInfo machineInfo,
209                                         DWORD processId, 
210                                         ShimDataTarget ** ppDataTarget)
211 {
212     HRESULT hr = S_OK;
213     HANDLE hProcess = NULL;
214     ShimLocalDataTarget * pLocalDataTarget = NULL;
215     
216     *ppDataTarget = NULL;
217     
218     hProcess = OpenProcess(
219         PROCESS_DUP_HANDLE        |
220         PROCESS_QUERY_INFORMATION |
221         PROCESS_TERMINATE         |
222         PROCESS_VM_OPERATION      |
223         PROCESS_VM_READ           |
224         PROCESS_VM_WRITE          |
225         SYNCHRONIZE,
226         FALSE,
227         processId);
228
229     if (hProcess == NULL)
230     {
231         hr = HRESULT_FROM_GetLastError();
232         goto Label_Exit;
233     }
234
235     EX_TRY
236     {
237         if (!CompatibleHostAndTargetPlatforms(hProcess))
238         {
239             hr = CORDBG_E_UNCOMPATIBLE_PLATFORMS;
240             goto Label_Exit;
241         }
242     }
243     EX_CATCH_HRESULT(hr);
244     if (FAILED(hr))
245     {
246         goto Label_Exit;
247     }
248     pLocalDataTarget = new (nothrow) ShimLocalDataTarget(processId, hProcess);
249     if (pLocalDataTarget == NULL)
250     {
251         hr = E_OUTOFMEMORY;
252         goto Label_Exit;
253     }
254
255     // ShimLocalDataTarget now has ownership of Handle.
256     hProcess = NULL;
257
258     _ASSERTE(SUCCEEDED(hr));
259     *ppDataTarget = pLocalDataTarget;
260     pLocalDataTarget->AddRef(); // must addref out-parameters
261
262 Label_Exit:
263     if (FAILED(hr))
264     {
265         if (hProcess != NULL)
266         {
267             CloseHandle(hProcess);
268         }
269         delete pLocalDataTarget;
270     }    
271
272     return hr;
273 }
274
275 // impl of interface method ICorDebugDataTarget::GetPlatform
276 HRESULT STDMETHODCALLTYPE
277 ShimLocalDataTarget::GetPlatform( 
278         CorDebugPlatform *pPlatform)
279 {
280     // Assume that we're running on Windows for now.
281 #if defined(DBG_TARGET_X86)
282     *pPlatform = CORDB_PLATFORM_WINDOWS_X86;
283 #elif defined(DBG_TARGET_AMD64)
284     *pPlatform = CORDB_PLATFORM_WINDOWS_AMD64;
285 #elif defined(DBG_TARGET_ARM)
286     *pPlatform = CORDB_PLATFORM_WINDOWS_ARM;
287 #elif defined(DBG_TARGET_ARM64)
288     *pPlatform = CORDB_PLATFORM_WINDOWS_ARM64;
289 #else
290 #error Unknown Processor.
291 #endif
292     return S_OK;
293 }
294
295 // impl of interface method ICorDebugDataTarget::ReadVirtual
296 HRESULT STDMETHODCALLTYPE
297 ShimLocalDataTarget::ReadVirtual( 
298     CORDB_ADDRESS address,
299     PBYTE pBuffer,
300     ULONG32 cbRequestSize,
301     ULONG32 *pcbRead)
302 {
303     ReturnFailureIfStateNotOk();
304
305
306     // ReadProcessMemory will fail if any part of the
307     // region to read does not have read access.  This
308     // routine attempts to read the largest valid prefix
309     // so it has to break up reads on page boundaries.
310
311     HRESULT hrStatus = S_OK;
312     ULONG32 totalDone = 0;
313     SIZE_T read;
314     ULONG32 readSize;
315
316     while (cbRequestSize > 0)
317     {
318         // Calculate bytes to read and don't let read cross
319         // a page boundary.
320         readSize = OS_PAGE_SIZE - (ULONG32)(address & (OS_PAGE_SIZE - 1));
321         readSize = min(cbRequestSize, readSize);
322
323         if (!ReadProcessMemory(m_hProcess, (PVOID)(ULONG_PTR)address,
324                                pBuffer, readSize, &read))
325         {
326             if (totalDone == 0)
327             {
328                 // If we haven't read anything indicate failure.
329                 hrStatus = HRESULT_FROM_GetLastError();
330             }
331             break;
332         }
333
334         totalDone += (ULONG32)read;
335         address += read;
336         pBuffer += read;
337         cbRequestSize -= (ULONG32)read;
338     }
339
340     *pcbRead = totalDone;
341     return hrStatus;
342 }
343
344 // impl of interface method ICorDebugMutableDataTarget::WriteVirtual
345 HRESULT STDMETHODCALLTYPE
346 ShimLocalDataTarget::WriteVirtual( 
347     CORDB_ADDRESS pAddress,
348     const BYTE * pBuffer,
349     ULONG32 cbRequestSize)
350 {
351     ReturnFailureIfStateNotOk();
352
353     SIZE_T cbWritten;
354     BOOL fWriteOk = WriteProcessMemory(m_hProcess, CORDB_ADDRESS_TO_PTR(pAddress), pBuffer, cbRequestSize, &cbWritten);
355     if (fWriteOk)
356     {
357         _ASSERTE(cbWritten == cbRequestSize);  // MSDN docs say this must always be true
358         return S_OK;
359     }
360     else
361     {
362         return HRESULT_FROM_GetLastError();
363     }
364 }
365
366 HRESULT STDMETHODCALLTYPE
367 ShimLocalDataTarget::GetThreadContext(
368     DWORD dwThreadID,
369     ULONG32 contextFlags,
370     ULONG32 contextSize,
371     BYTE * pContext)
372 {
373     ReturnFailureIfStateNotOk();
374     // @dbgtodo - Ideally we should cache the thread handles so that we don't need to 
375     // open and close the thread handles every time.
376
377     HRESULT hr = E_FAIL;
378
379     if (!CheckContextSizeForBuffer(contextSize, pContext))
380     {
381         return E_INVALIDARG;
382     }
383     
384     HandleHolder hThread = OpenThread(
385         THREAD_GET_CONTEXT | THREAD_SET_CONTEXT | THREAD_QUERY_INFORMATION , 
386         FALSE, // thread handle is not inheritable. 
387         dwThreadID);
388
389     if (hThread != NULL)
390     {
391         DT_CONTEXT * pCtx = reinterpret_cast<DT_CONTEXT *>(pContext);
392         pCtx->ContextFlags = contextFlags;
393
394         if (DbiGetThreadContext(hThread, pCtx))
395         {
396             hr = S_OK;
397         }
398     }
399
400     // hThread destructed automatically
401     return hr;
402 }
403
404 // impl of interface method ICorDebugMutableDataTarget::SetThreadContext
405 HRESULT STDMETHODCALLTYPE
406 ShimLocalDataTarget::SetThreadContext(
407     DWORD dwThreadID,
408     ULONG32 contextSize,
409     const BYTE * pContext)
410 {
411     ReturnFailureIfStateNotOk();
412     HRESULT hr = E_FAIL;
413
414     if (!CheckContextSizeForBuffer(contextSize, pContext))
415     {
416         return E_INVALIDARG;
417     }
418
419     
420     HandleHolder hThread = OpenThread(
421         THREAD_GET_CONTEXT | THREAD_SET_CONTEXT | THREAD_QUERY_INFORMATION, 
422         FALSE, // thread handle is not inheritable.
423         dwThreadID);
424
425     if (hThread != NULL)
426     {
427         if (DbiSetThreadContext(hThread, reinterpret_cast<const DT_CONTEXT *>(pContext)))
428         {
429             hr = S_OK;
430         }
431     }
432
433     // hThread destructed automatically
434     return hr;
435 }
436
437 // Public implementation of ICorDebugMutableDataTarget::ContinueStatusChanged
438 HRESULT STDMETHODCALLTYPE
439 ShimLocalDataTarget::ContinueStatusChanged(
440     DWORD dwThreadId,
441     CORDB_CONTINUE_STATUS dwContinueStatus)
442 {
443     ReturnFailureIfStateNotOk();
444     if (m_fpContinueStatusChanged != NULL)
445     {
446         return m_fpContinueStatusChanged(m_pContinueStatusChangedUserData, dwThreadId, dwContinueStatus);
447     }
448     return E_NOTIMPL;
449 }