2 // Copyright (c) Microsoft. All rights reserved.
3 // Licensed under the MIT license. See LICENSE file in the project root for full license information.
5 //*****************************************************************************
8 // File: ShimLocalDataTarget.cpp
10 //*****************************************************************************
19 #include "shimdatatarget.h"
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
28 class ShimLocalDataTarget : public ShimDataTarget
31 ShimLocalDataTarget(DWORD processId, HANDLE hProcess);
33 ~ShimLocalDataTarget();
35 virtual void Dispose();
38 // ICorDebugMutableDataTarget.
41 virtual HRESULT STDMETHODCALLTYPE GetPlatform(
42 CorDebugPlatform *pPlatform);
44 virtual HRESULT STDMETHODCALLTYPE ReadVirtual(
45 CORDB_ADDRESS address,
50 virtual HRESULT STDMETHODCALLTYPE WriteVirtual(
51 CORDB_ADDRESS address,
55 virtual HRESULT STDMETHODCALLTYPE GetThreadContext(
61 virtual HRESULT STDMETHODCALLTYPE SetThreadContext(
64 const BYTE * context);
66 virtual HRESULT STDMETHODCALLTYPE ContinueStatusChanged(
68 CORDB_CONTINUE_STATUS dwContinueStatus);
70 virtual HRESULT STDMETHODCALLTYPE VirtualUnwind(
71 DWORD threadId, ULONG32 contextSize, PBYTE context);
74 // Handle to the process. We own this.
79 // Determines whether the target and host are running on compatible platforms.
81 // input: hTargetProcess - handle for the target process
82 // Return Value: TRUE iff both target and host are both Wow64 or neither is.
84 BOOL CompatibleHostAndTargetPlatforms(HANDLE hTargetProcess)
86 #if defined(FEATURE_PAL)
89 // get the platform for the host process
90 BOOL fHostProcessIsWow64 = FALSE;
91 BOOL fSuccess = FALSE;
92 HANDLE hHostProcess = GetCurrentProcess();
94 fSuccess = IsWow64Process(hHostProcess, &fHostProcessIsWow64);
95 CloseHandle(hHostProcess);
100 ThrowHR(HRESULT_FROM_GetLastError());
103 // get the platform for the target process
104 if (hTargetProcess == NULL)
106 ThrowHR(HRESULT_FROM_GetLastError());
109 BOOL fTargetProcessIsWow64 = FALSE;
110 fSuccess = IsWow64Process(hTargetProcess, &fTargetProcessIsWow64);
114 ThrowHR(HRESULT_FROM_GetLastError());
117 // We don't want to expose the IPC block if one process is x86 and
118 // the other is ia64 or amd64
119 if (fTargetProcessIsWow64 != fHostProcessIsWow64)
128 } // CompatibleHostAndTargetPlatforms
130 // Helper macro to check for failure conditions at the start of data-target methods.
131 #define ReturnFailureIfStateNotOk() \
137 //---------------------------------------------------------------------------------------
139 // ctor for ShimLocalDataTarget.
142 // processId - pid of live process.
143 // hProcess - handle to kernel process object.
146 // Shim takes ownership of handle hProcess.
149 ShimLocalDataTarget::ShimLocalDataTarget(DWORD processId, HANDLE hProcess)
153 m_processId = processId;
154 m_hProcess = hProcess;
158 m_fpContinueStatusChanged = NULL;
159 m_pContinueStatusChangedUserData = NULL;
162 //---------------------------------------------------------------------------------------
164 // dctor for ShimLocalDataTarget.
166 ShimLocalDataTarget::~ShimLocalDataTarget()
172 //---------------------------------------------------------------------------------------
174 // Dispose all resources and neuter the object.
179 // Release all resources (such as the handle to the process we got in the ctor).
180 // May be called multiple times.
181 // All other non-trivial APIs (eg, not IUnknown) will fail after this.
184 void ShimLocalDataTarget::Dispose()
186 if (m_hProcess != NULL)
188 CloseHandle(m_hProcess);
191 m_hr = CORDBG_E_OBJECT_NEUTERED;
194 //---------------------------------------------------------------------------------------
196 // Construction method for data-target
199 // processId - (input) live OS process ID to build a data-target for.
200 // ppDataTarget - (output) new data-target instance. This gets addreffed.
206 // pid must be for local, same architecture, process.
207 // Caller must have security permissions for OpenProcess()
208 // Caller must release *ppDataTarget.
211 HRESULT BuildPlatformSpecificDataTarget(MachineInfo machineInfo,
213 ShimDataTarget ** ppDataTarget)
216 HANDLE hProcess = NULL;
217 ShimLocalDataTarget * pLocalDataTarget = NULL;
219 *ppDataTarget = NULL;
221 hProcess = OpenProcess(
223 PROCESS_QUERY_INFORMATION |
225 PROCESS_VM_OPERATION |
232 if (hProcess == NULL)
234 hr = HRESULT_FROM_GetLastError();
240 if (!CompatibleHostAndTargetPlatforms(hProcess))
242 hr = CORDBG_E_UNCOMPATIBLE_PLATFORMS;
246 EX_CATCH_HRESULT(hr);
251 pLocalDataTarget = new (nothrow) ShimLocalDataTarget(processId, hProcess);
252 if (pLocalDataTarget == NULL)
258 // ShimLocalDataTarget now has ownership of Handle.
261 _ASSERTE(SUCCEEDED(hr));
262 *ppDataTarget = pLocalDataTarget;
263 pLocalDataTarget->AddRef(); // must addref out-parameters
268 if (hProcess != NULL)
270 CloseHandle(hProcess);
272 delete pLocalDataTarget;
278 // impl of interface method ICorDebugDataTarget::GetPlatform
279 HRESULT STDMETHODCALLTYPE
280 ShimLocalDataTarget::GetPlatform(
281 CorDebugPlatform *pPlatform)
284 #error ShimLocalDataTarget is not implemented on PAL systems yet
286 // Assume that we're running on Windows for now.
287 #if defined(DBG_TARGET_X86)
288 *pPlatform = CORDB_PLATFORM_WINDOWS_X86;
289 #elif defined(DBG_TARGET_AMD64)
290 *pPlatform = CORDB_PLATFORM_WINDOWS_AMD64;
291 #elif defined(DBG_TARGET_ARM)
292 *pPlatform = CORDB_PLATFORM_WINDOWS_ARM;
293 #elif defined(DBG_TARGET_ARM64)
294 *pPlatform = CORDB_PLATFORM_WINDOWS_ARM64;
296 #error Unknown Processor.
301 // impl of interface method ICorDebugDataTarget::ReadVirtual
302 HRESULT STDMETHODCALLTYPE
303 ShimLocalDataTarget::ReadVirtual(
304 CORDB_ADDRESS address,
306 ULONG32 cbRequestSize,
309 ReturnFailureIfStateNotOk();
312 // ReadProcessMemory will fail if any part of the
313 // region to read does not have read access. This
314 // routine attempts to read the largest valid prefix
315 // so it has to break up reads on page boundaries.
317 HRESULT hrStatus = S_OK;
318 ULONG32 totalDone = 0;
322 while (cbRequestSize > 0)
324 // Calculate bytes to read and don't let read cross
326 readSize = OS_PAGE_SIZE - (ULONG32)(address & (OS_PAGE_SIZE - 1));
327 readSize = min(cbRequestSize, readSize);
329 if (!ReadProcessMemory(m_hProcess, (PVOID)(ULONG_PTR)address,
330 pBuffer, readSize, &read))
334 // If we haven't read anything indicate failure.
335 hrStatus = HRESULT_FROM_GetLastError();
340 totalDone += (ULONG32)read;
343 cbRequestSize -= (ULONG32)read;
346 *pcbRead = totalDone;
350 // impl of interface method ICorDebugMutableDataTarget::WriteVirtual
351 HRESULT STDMETHODCALLTYPE
352 ShimLocalDataTarget::WriteVirtual(
353 CORDB_ADDRESS pAddress,
354 const BYTE * pBuffer,
355 ULONG32 cbRequestSize)
357 ReturnFailureIfStateNotOk();
360 BOOL fWriteOk = WriteProcessMemory(m_hProcess, CORDB_ADDRESS_TO_PTR(pAddress), pBuffer, cbRequestSize, &cbWritten);
363 _ASSERTE(cbWritten == cbRequestSize); // MSDN docs say this must always be true
368 return HRESULT_FROM_GetLastError();
372 HRESULT STDMETHODCALLTYPE
373 ShimLocalDataTarget::GetThreadContext(
375 ULONG32 contextFlags,
379 ReturnFailureIfStateNotOk();
380 // @dbgtodo - Ideally we should cache the thread handles so that we don't need to
381 // open and close the thread handles every time.
385 if (!CheckContextSizeForBuffer(contextSize, pContext))
390 HandleHolder hThread = OpenThread(
391 THREAD_GET_CONTEXT | THREAD_SET_CONTEXT | THREAD_QUERY_INFORMATION ,
392 FALSE, // thread handle is not inheritable.
397 DT_CONTEXT * pCtx = reinterpret_cast<DT_CONTEXT *>(pContext);
398 pCtx->ContextFlags = contextFlags;
400 if (DbiGetThreadContext(hThread, pCtx))
406 // hThread destructed automatically
410 // impl of interface method ICorDebugMutableDataTarget::SetThreadContext
411 HRESULT STDMETHODCALLTYPE
412 ShimLocalDataTarget::SetThreadContext(
415 const BYTE * pContext)
417 ReturnFailureIfStateNotOk();
420 if (!CheckContextSizeForBuffer(contextSize, pContext))
426 HandleHolder hThread = OpenThread(
427 THREAD_GET_CONTEXT | THREAD_SET_CONTEXT | THREAD_QUERY_INFORMATION,
428 FALSE, // thread handle is not inheritable.
433 if (DbiSetThreadContext(hThread, reinterpret_cast<const DT_CONTEXT *>(pContext)))
439 // hThread destructed automatically
443 // Public implementation of ICorDebugMutableDataTarget::ContinueStatusChanged
444 HRESULT STDMETHODCALLTYPE
445 ShimLocalDataTarget::ContinueStatusChanged(
447 CORDB_CONTINUE_STATUS dwContinueStatus)
449 ReturnFailureIfStateNotOk();
450 if (m_fpContinueStatusChanged != NULL)
452 return m_fpContinueStatusChanged(m_pContinueStatusChangedUserData, dwThreadId, dwContinueStatus);
457 //---------------------------------------------------------------------------------------
459 // Unwind the stack to the next frame.
462 // context filled in with the next frame
464 HRESULT STDMETHODCALLTYPE
465 ShimLocalDataTarget::VirtualUnwind(DWORD threadId, ULONG32 contextSize, PBYTE context)
468 _ASSERTE(!"ShimLocalDataTarget::VirtualUnwind NOT IMPLEMENTED");