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)
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;
290 #error Unknown Processor.
295 // impl of interface method ICorDebugDataTarget::ReadVirtual
296 HRESULT STDMETHODCALLTYPE
297 ShimLocalDataTarget::ReadVirtual(
298 CORDB_ADDRESS address,
300 ULONG32 cbRequestSize,
303 ReturnFailureIfStateNotOk();
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.
311 HRESULT hrStatus = S_OK;
312 ULONG32 totalDone = 0;
316 while (cbRequestSize > 0)
318 // Calculate bytes to read and don't let read cross
320 readSize = OS_PAGE_SIZE - (ULONG32)(address & (OS_PAGE_SIZE - 1));
321 readSize = min(cbRequestSize, readSize);
323 if (!ReadProcessMemory(m_hProcess, (PVOID)(ULONG_PTR)address,
324 pBuffer, readSize, &read))
328 // If we haven't read anything indicate failure.
329 hrStatus = HRESULT_FROM_GetLastError();
334 totalDone += (ULONG32)read;
337 cbRequestSize -= (ULONG32)read;
340 *pcbRead = totalDone;
344 // impl of interface method ICorDebugMutableDataTarget::WriteVirtual
345 HRESULT STDMETHODCALLTYPE
346 ShimLocalDataTarget::WriteVirtual(
347 CORDB_ADDRESS pAddress,
348 const BYTE * pBuffer,
349 ULONG32 cbRequestSize)
351 ReturnFailureIfStateNotOk();
354 BOOL fWriteOk = WriteProcessMemory(m_hProcess, CORDB_ADDRESS_TO_PTR(pAddress), pBuffer, cbRequestSize, &cbWritten);
357 _ASSERTE(cbWritten == cbRequestSize); // MSDN docs say this must always be true
362 return HRESULT_FROM_GetLastError();
366 HRESULT STDMETHODCALLTYPE
367 ShimLocalDataTarget::GetThreadContext(
369 ULONG32 contextFlags,
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.
379 if (!CheckContextSizeForBuffer(contextSize, pContext))
384 HandleHolder hThread = OpenThread(
385 THREAD_GET_CONTEXT | THREAD_SET_CONTEXT | THREAD_QUERY_INFORMATION ,
386 FALSE, // thread handle is not inheritable.
391 DT_CONTEXT * pCtx = reinterpret_cast<DT_CONTEXT *>(pContext);
392 pCtx->ContextFlags = contextFlags;
394 if (DbiGetThreadContext(hThread, pCtx))
400 // hThread destructed automatically
404 // impl of interface method ICorDebugMutableDataTarget::SetThreadContext
405 HRESULT STDMETHODCALLTYPE
406 ShimLocalDataTarget::SetThreadContext(
409 const BYTE * pContext)
411 ReturnFailureIfStateNotOk();
414 if (!CheckContextSizeForBuffer(contextSize, pContext))
420 HandleHolder hThread = OpenThread(
421 THREAD_GET_CONTEXT | THREAD_SET_CONTEXT | THREAD_QUERY_INFORMATION,
422 FALSE, // thread handle is not inheritable.
427 if (DbiSetThreadContext(hThread, reinterpret_cast<const DT_CONTEXT *>(pContext)))
433 // hThread destructed automatically
437 // Public implementation of ICorDebugMutableDataTarget::ContinueStatusChanged
438 HRESULT STDMETHODCALLTYPE
439 ShimLocalDataTarget::ContinueStatusChanged(
441 CORDB_CONTINUE_STATUS dwContinueStatus)
443 ReturnFailureIfStateNotOk();
444 if (m_fpContinueStatusChanged != NULL)
446 return m_fpContinueStatusChanged(m_pContinueStatusChangedUserData, dwThreadId, dwContinueStatus);