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);
71 // Handle to the process. We own this.
76 // Determines whether the target and host are running on compatible platforms.
78 // input: hTargetProcess - handle for the target process
79 // Return Value: TRUE iff both target and host are both Wow64 or neither is.
81 BOOL CompatibleHostAndTargetPlatforms(HANDLE hTargetProcess)
83 #if defined(FEATURE_PAL)
86 // get the platform for the host process
87 BOOL fHostProcessIsWow64 = FALSE;
88 BOOL fSuccess = FALSE;
89 HANDLE hHostProcess = GetCurrentProcess();
91 fSuccess = IsWow64Process(hHostProcess, &fHostProcessIsWow64);
92 CloseHandle(hHostProcess);
97 ThrowHR(HRESULT_FROM_GetLastError());
100 // get the platform for the target process
101 if (hTargetProcess == NULL)
103 ThrowHR(HRESULT_FROM_GetLastError());
106 BOOL fTargetProcessIsWow64 = FALSE;
107 fSuccess = IsWow64Process(hTargetProcess, &fTargetProcessIsWow64);
111 ThrowHR(HRESULT_FROM_GetLastError());
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)
125 } // CompatibleHostAndTargetPlatforms
127 // Helper macro to check for failure conditions at the start of data-target methods.
128 #define ReturnFailureIfStateNotOk() \
134 //---------------------------------------------------------------------------------------
136 // ctor for ShimLocalDataTarget.
139 // processId - pid of live process.
140 // hProcess - handle to kernel process object.
143 // Shim takes ownership of handle hProcess.
146 ShimLocalDataTarget::ShimLocalDataTarget(DWORD processId, HANDLE hProcess)
150 m_processId = processId;
151 m_hProcess = hProcess;
155 m_fpContinueStatusChanged = NULL;
156 m_pContinueStatusChangedUserData = NULL;
159 //---------------------------------------------------------------------------------------
161 // dctor for ShimLocalDataTarget.
163 ShimLocalDataTarget::~ShimLocalDataTarget()
169 //---------------------------------------------------------------------------------------
171 // Dispose all resources and neuter the object.
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.
181 void ShimLocalDataTarget::Dispose()
183 if (m_hProcess != NULL)
185 CloseHandle(m_hProcess);
188 m_hr = CORDBG_E_OBJECT_NEUTERED;
191 //---------------------------------------------------------------------------------------
193 // Construction method for data-target
196 // processId - (input) live OS process ID to build a data-target for.
197 // ppDataTarget - (output) new data-target instance. This gets addreffed.
203 // pid must be for local, same architecture, process.
204 // Caller must have security permissions for OpenProcess()
205 // Caller must release *ppDataTarget.
208 HRESULT BuildPlatformSpecificDataTarget(MachineInfo machineInfo,
210 ShimDataTarget ** ppDataTarget)
213 HANDLE hProcess = NULL;
214 ShimLocalDataTarget * pLocalDataTarget = NULL;
216 *ppDataTarget = NULL;
218 hProcess = OpenProcess(
220 PROCESS_QUERY_INFORMATION |
222 PROCESS_VM_OPERATION |
229 if (hProcess == NULL)
231 hr = HRESULT_FROM_GetLastError();
237 if (!CompatibleHostAndTargetPlatforms(hProcess))
239 hr = CORDBG_E_UNCOMPATIBLE_PLATFORMS;
243 EX_CATCH_HRESULT(hr);
248 pLocalDataTarget = new (nothrow) ShimLocalDataTarget(processId, hProcess);
249 if (pLocalDataTarget == NULL)
255 // ShimLocalDataTarget now has ownership of Handle.
258 _ASSERTE(SUCCEEDED(hr));
259 *ppDataTarget = pLocalDataTarget;
260 pLocalDataTarget->AddRef(); // must addref out-parameters
265 if (hProcess != NULL)
267 CloseHandle(hProcess);
269 delete pLocalDataTarget;
275 // impl of interface method ICorDebugDataTarget::GetPlatform
276 HRESULT STDMETHODCALLTYPE
277 ShimLocalDataTarget::GetPlatform(
278 CorDebugPlatform *pPlatform)
281 #error ShimLocalDataTarget is not implemented on PAL systems yet
283 // Assume that we're running on Windows for now.
284 #if defined(DBG_TARGET_X86)
285 *pPlatform = CORDB_PLATFORM_WINDOWS_X86;
286 #elif defined(DBG_TARGET_AMD64)
287 *pPlatform = CORDB_PLATFORM_WINDOWS_AMD64;
288 #elif defined(DBG_TARGET_ARM)
289 *pPlatform = CORDB_PLATFORM_WINDOWS_ARM;
290 #elif defined(DBG_TARGET_ARM64)
291 *pPlatform = CORDB_PLATFORM_WINDOWS_ARM64;
293 #error Unknown Processor.
298 // impl of interface method ICorDebugDataTarget::ReadVirtual
299 HRESULT STDMETHODCALLTYPE
300 ShimLocalDataTarget::ReadVirtual(
301 CORDB_ADDRESS address,
303 ULONG32 cbRequestSize,
306 ReturnFailureIfStateNotOk();
309 // ReadProcessMemory will fail if any part of the
310 // region to read does not have read access. This
311 // routine attempts to read the largest valid prefix
312 // so it has to break up reads on page boundaries.
314 HRESULT hrStatus = S_OK;
315 ULONG32 totalDone = 0;
319 while (cbRequestSize > 0)
321 // Calculate bytes to read and don't let read cross
323 readSize = OS_PAGE_SIZE - (ULONG32)(address & (OS_PAGE_SIZE - 1));
324 readSize = min(cbRequestSize, readSize);
326 if (!ReadProcessMemory(m_hProcess, (PVOID)(ULONG_PTR)address,
327 pBuffer, readSize, &read))
331 // If we haven't read anything indicate failure.
332 hrStatus = HRESULT_FROM_GetLastError();
337 totalDone += (ULONG32)read;
340 cbRequestSize -= (ULONG32)read;
343 *pcbRead = totalDone;
347 // impl of interface method ICorDebugMutableDataTarget::WriteVirtual
348 HRESULT STDMETHODCALLTYPE
349 ShimLocalDataTarget::WriteVirtual(
350 CORDB_ADDRESS pAddress,
351 const BYTE * pBuffer,
352 ULONG32 cbRequestSize)
354 ReturnFailureIfStateNotOk();
357 BOOL fWriteOk = WriteProcessMemory(m_hProcess, CORDB_ADDRESS_TO_PTR(pAddress), pBuffer, cbRequestSize, &cbWritten);
360 _ASSERTE(cbWritten == cbRequestSize); // MSDN docs say this must always be true
365 return HRESULT_FROM_GetLastError();
369 HRESULT STDMETHODCALLTYPE
370 ShimLocalDataTarget::GetThreadContext(
372 ULONG32 contextFlags,
376 ReturnFailureIfStateNotOk();
377 // @dbgtodo - Ideally we should cache the thread handles so that we don't need to
378 // open and close the thread handles every time.
382 if (!CheckContextSizeForBuffer(contextSize, pContext))
387 HandleHolder hThread = OpenThread(
388 THREAD_GET_CONTEXT | THREAD_SET_CONTEXT | THREAD_QUERY_INFORMATION ,
389 FALSE, // thread handle is not inheritable.
394 DT_CONTEXT * pCtx = reinterpret_cast<DT_CONTEXT *>(pContext);
395 pCtx->ContextFlags = contextFlags;
397 if (DbiGetThreadContext(hThread, pCtx))
403 // hThread destructed automatically
407 // impl of interface method ICorDebugMutableDataTarget::SetThreadContext
408 HRESULT STDMETHODCALLTYPE
409 ShimLocalDataTarget::SetThreadContext(
412 const BYTE * pContext)
414 ReturnFailureIfStateNotOk();
417 if (!CheckContextSizeForBuffer(contextSize, pContext))
423 HandleHolder hThread = OpenThread(
424 THREAD_GET_CONTEXT | THREAD_SET_CONTEXT | THREAD_QUERY_INFORMATION,
425 FALSE, // thread handle is not inheritable.
430 if (DbiSetThreadContext(hThread, reinterpret_cast<const DT_CONTEXT *>(pContext)))
436 // hThread destructed automatically
440 // Public implementation of ICorDebugMutableDataTarget::ContinueStatusChanged
441 HRESULT STDMETHODCALLTYPE
442 ShimLocalDataTarget::ContinueStatusChanged(
444 CORDB_CONTINUE_STATUS dwContinueStatus)
446 ReturnFailureIfStateNotOk();
447 if (m_fpContinueStatusChanged != NULL)
449 return m_fpContinueStatusChanged(m_pContinueStatusChangedUserData, dwThreadId, dwContinueStatus);