diff options
37 files changed, 671 insertions, 214 deletions
diff --git a/src/debug/debug-pal/CMakeLists.txt b/src/debug/debug-pal/CMakeLists.txt index c96d7f9e06..83e400c28f 100644 --- a/src/debug/debug-pal/CMakeLists.txt +++ b/src/debug/debug-pal/CMakeLists.txt @@ -12,6 +12,7 @@ if(WIN32) set(TWO_WAY_PIPE_SOURCES win/twowaypipe.cpp + win/processdescriptor.cpp ) endif(WIN32) @@ -24,6 +25,7 @@ if(CLR_CMAKE_PLATFORM_UNIX) set(TWO_WAY_PIPE_SOURCES unix/twowaypipe.cpp + unix/processdescriptor.cpp ) endif(CLR_CMAKE_PLATFORM_UNIX) diff --git a/src/debug/debug-pal/unix/processdescriptor.cpp b/src/debug/debug-pal/unix/processdescriptor.cpp new file mode 100644 index 0000000000..6a4975dc6b --- /dev/null +++ b/src/debug/debug-pal/unix/processdescriptor.cpp @@ -0,0 +1,21 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +#include <pal.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <limits.h> +#include <pal_assert.h> +#include "processdescriptor.h" + +ProcessDescriptor ProcessDescriptor::FromCurrentProcess() +{ +#ifdef __APPLE__ + return Create(GetCurrentProcessId(), PAL_GetApplicationGroupId()); +#else + return Create(GetCurrentProcessId(), nullptr); +#endif +} diff --git a/src/debug/debug-pal/unix/twowaypipe.cpp b/src/debug/debug-pal/unix/twowaypipe.cpp index 9dfe1e3ecb..b0acb1df7b 100644 --- a/src/debug/debug-pal/unix/twowaypipe.cpp +++ b/src/debug/debug-pal/unix/twowaypipe.cpp @@ -14,15 +14,14 @@ // Creates a server side of the pipe. // Id is used to create pipes names and uniquely identify the pipe on the machine. // true - success, false - failure (use GetLastError() for more details) -bool TwoWayPipe::CreateServer(DWORD id) +bool TwoWayPipe::CreateServer(const ProcessDescriptor& pd) { _ASSERTE(m_state == NotInitialized); if (m_state != NotInitialized) return false; - m_id = id; - PAL_GetTransportPipeName(m_inPipeName, id, "in"); - PAL_GetTransportPipeName(m_outPipeName, id, "out"); + PAL_GetTransportPipeName(m_inPipeName, pd.m_Pid, pd.m_ApplicationGroupId, "in"); + PAL_GetTransportPipeName(m_outPipeName, pd.m_Pid, pd.m_ApplicationGroupId, "out"); unlink(m_inPipeName); @@ -47,16 +46,15 @@ bool TwoWayPipe::CreateServer(DWORD id) // Connects to a previously opened server side of the pipe. // Id is used to locate the pipe on the machine. // true - success, false - failure (use GetLastError() for more details) -bool TwoWayPipe::Connect(DWORD id) +bool TwoWayPipe::Connect(const ProcessDescriptor& pd) { _ASSERTE(m_state == NotInitialized); if (m_state != NotInitialized) return false; - m_id = id; //"in" and "out" are switched deliberately, because we're on the client - PAL_GetTransportPipeName(m_inPipeName, id, "out"); - PAL_GetTransportPipeName(m_outPipeName, id, "in"); + PAL_GetTransportPipeName(m_inPipeName, pd.m_Pid, pd.m_ApplicationGroupId, "out"); + PAL_GetTransportPipeName(m_outPipeName, pd.m_Pid, pd.m_ApplicationGroupId, "in"); // Pipe opening order is reversed compared to WaitForConnection() // in order to avaid deadlock. diff --git a/src/debug/debug-pal/win/processdescriptor.cpp b/src/debug/debug-pal/win/processdescriptor.cpp new file mode 100644 index 0000000000..fbc0bf0486 --- /dev/null +++ b/src/debug/debug-pal/win/processdescriptor.cpp @@ -0,0 +1,14 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +#include <windows.h> +#include <stdio.h> +#include <wchar.h> +#include <assert.h> +#include "processdescriptor.h" + +ProcessDescriptor ProcessDescriptor::FromCurrentProcess() +{ + return FromPid(GetCurrentProcessId()); +} diff --git a/src/debug/debug-pal/win/twowaypipe.cpp b/src/debug/debug-pal/win/twowaypipe.cpp index 6e4c9127d7..fb788b068f 100644 --- a/src/debug/debug-pal/win/twowaypipe.cpp +++ b/src/debug/debug-pal/win/twowaypipe.cpp @@ -17,19 +17,19 @@ // Creates a server side of the pipe. // Id is used to create pipes names and uniquely identify the pipe on the machine. // true - success, false - failure (use GetLastError() for more details) -bool TwoWayPipe::CreateServer(DWORD id) +bool TwoWayPipe::CreateServer(const ProcessDescriptor& pd) { _ASSERTE(m_state == NotInitialized); if (m_state != NotInitialized) return false; - m_inboundPipe = CreateOneWayPipe(id, true); + m_inboundPipe = CreateOneWayPipe(pd.m_Pid, true); if (m_inboundPipe == INVALID_HANDLE_VALUE) { return false; } - m_outboundPipe = CreateOneWayPipe(id, false); + m_outboundPipe = CreateOneWayPipe(pd.m_Pid, false); if (m_outboundPipe == INVALID_HANDLE_VALUE) { CloseHandle(m_inboundPipe); @@ -45,19 +45,19 @@ bool TwoWayPipe::CreateServer(DWORD id) // Connects to a previously opened server side of the pipe. // Id is used to locate the pipe on the machine. // true - success, false - failure (use GetLastError() for more details) -bool TwoWayPipe::Connect(DWORD id) +bool TwoWayPipe::Connect(const ProcessDescriptor& pd) { _ASSERTE(m_state == NotInitialized); if (m_state != NotInitialized) return false; - m_inboundPipe = OpenOneWayPipe(id, true); + m_inboundPipe = OpenOneWayPipe(pd.m_Pid, true); if (m_inboundPipe == INVALID_HANDLE_VALUE) { return false; } - m_outboundPipe = OpenOneWayPipe(id, false); + m_outboundPipe = OpenOneWayPipe(pd.m_Pid, false); if (m_outboundPipe == INVALID_HANDLE_VALUE) { CloseHandle(m_inboundPipe); diff --git a/src/debug/di/cordb.cpp b/src/debug/di/cordb.cpp index ae74c34b54..4febb5125a 100644 --- a/src/debug/di/cordb.cpp +++ b/src/debug/di/cordb.cpp @@ -87,9 +87,6 @@ HINSTANCE g_hInst; // Instance handle to this piece of code // This was used by the Mix07 release of Silverlight, but it didn't properly support versioning // and we no longer support it's debugger protocol so we require callers to use // code:CoreCLRCreateCordbObject instead. -// -// This is also still used on Mac - multi-instance debugging and debugger -// versioning isn't really implemented there yet. This probably needs to change. //***************************************************************************** STDAPI CreateCordbObject(int iDebuggerVersion, IUnknown ** ppCordb) { @@ -112,24 +109,28 @@ STDAPI CreateCordbObject(int iDebuggerVersion, IUnknown ** ppCordb) return E_INVALIDARG; } - return Cordb::CreateObject((CorDebugInterfaceVersion)iDebuggerVersion, IID_ICorDebug, (void **) ppCordb); + return Cordb::CreateObject( + (CorDebugInterfaceVersion)iDebuggerVersion, ProcessDescriptor::UNINITIALIZED_PID, /*lpApplicationGroupId*/ NULL, IID_ICorDebug, (void **) ppCordb); } // // Public API. -// Telesto Creation path - only way to debug multi-instance. -// This supercedes code:CreateCordbObject +// Telesto Creation path with Mac sandbox support - only way to debug a sandboxed application on Mac. +// This supercedes code:CoreCLRCreateCordbObject // // Arguments: // iDebuggerVersion - version of ICorDebug interfaces that the debugger is requesting // pid - pid of debuggee that we're attaching to. +// lpApplicationGroupId - A string representing the application group ID of a sandboxed +// process running in Mac. Pass NULL if the process is not +// running in a sandbox and other platforms. // hmodTargetCLR - module handle to clr in target pid that we're attaching to. // ppCordb - (out) the resulting ICorDebug object. // // Notes: // It's inconsistent that this takes a (handle, pid) but hands back an ICorDebug instead of an ICorDebugProcess. // Callers will need to call *ppCordb->DebugActiveProcess(pid). -STDAPI CoreCLRCreateCordbObject(int iDebuggerVersion, DWORD pid, HMODULE hmodTargetCLR, IUnknown ** ppCordb) +STDAPI CoreCLRCreateCordbObjectEx(int iDebuggerVersion, DWORD pid, LPCWSTR lpApplicationGroupId, HMODULE hmodTargetCLR, IUnknown ** ppCordb) { if (ppCordb == NULL) { @@ -145,10 +146,8 @@ STDAPI CoreCLRCreateCordbObject(int iDebuggerVersion, DWORD pid, HMODULE hmodTar // Create the ICorDebug object // RSExtSmartPtr<ICorDebug> pCordb; - Cordb::CreateObject((CorDebugInterfaceVersion)iDebuggerVersion, IID_ICorDebug, (void **) &pCordb); + Cordb::CreateObject((CorDebugInterfaceVersion)iDebuggerVersion, pid, lpApplicationGroupId, IID_ICorDebug, (void **) &pCordb); - // @dbgtodo - we should stash the pid and validate that it's the same pid we're attaching to in ICorDebug::DebugActiveProcess. - // // Associate it with the target instance // @@ -167,6 +166,25 @@ STDAPI CoreCLRCreateCordbObject(int iDebuggerVersion, DWORD pid, HMODULE hmodTar return hr; } +// +// Public API. +// Telesto Creation path - only way to debug multi-instance. +// This supercedes code:CreateCordbObject +// +// Arguments: +// iDebuggerVersion - version of ICorDebug interfaces that the debugger is requesting +// pid - pid of debuggee that we're attaching to. +// hmodTargetCLR - module handle to clr in target pid that we're attaching to. +// ppCordb - (out) the resulting ICorDebug object. +// +// Notes: +// It's inconsistent that this takes a (handle, pid) but hands back an ICorDebug instead of an ICorDebugProcess. +// Callers will need to call *ppCordb->DebugActiveProcess(pid). +STDAPI CoreCLRCreateCordbObject(int iDebuggerVersion, DWORD pid, HMODULE hmodTargetCLR, IUnknown ** ppCordb) +{ + return CoreCLRCreateCordbObjectEx(iDebuggerVersion, pid, NULL, hmodTargetCLR, ppCordb); +} + diff --git a/src/debug/di/dbgtransportmanager.cpp b/src/debug/di/dbgtransportmanager.cpp index 8c1079dc33..b897547057 100644 --- a/src/debug/di/dbgtransportmanager.cpp +++ b/src/debug/di/dbgtransportmanager.cpp @@ -47,12 +47,13 @@ void DbgTransportTarget::Shutdown() // Given a PID attempt to find or create a DbgTransportSession instance to manage a connection to a runtime in // that process. Returns E_UNEXPECTED if the process can't be found. Also returns a handle that can be waited // on for process termination. -HRESULT DbgTransportTarget::GetTransportForProcess(DWORD dwPID, - DbgTransportSession **ppTransport, - HANDLE *phProcessHandle) +HRESULT DbgTransportTarget::GetTransportForProcess(const ProcessDescriptor *pProcessDescriptor, + DbgTransportSession **ppTransport, + HANDLE *phProcessHandle) { RSLockHolder lock(&m_sLock); HRESULT hr = S_OK; + DWORD dwPID = pProcessDescriptor->m_Pid; ProcessEntry *entry = LocateProcessByPID(dwPID); @@ -78,7 +79,7 @@ HRESULT DbgTransportTarget::GetTransportForProcess(DWORD dwPID } // Initialize it (this immediately starts the remote connection process). - hr = transport->Init(dwPID, hProcess); + hr = transport->Init(*pProcessDescriptor, hProcess); if (FAILED(hr)) { transport->Shutdown(); diff --git a/src/debug/di/dbgtransportmanager.h b/src/debug/di/dbgtransportmanager.h index 3a8013eae8..e1aeaa4623 100644 --- a/src/debug/di/dbgtransportmanager.h +++ b/src/debug/di/dbgtransportmanager.h @@ -20,13 +20,16 @@ // Usual lifecycle looks like this: // Debug a new process: // * CreateProcess(&pid) -// * GetTransportForProcess(pid, &transport) +// * On Mac, Optionally obtain an application group ID from a user +// * Create a ProcessDescriptor pd +// * GetTransportForProcess(&pd, &transport) // * ReleaseTransport(transport) // * KillProcess(pid) // Attach to an existing process: -// * Obtain pid from a user -// * GetTransportForProcess(pid, &transport) +// * Obtain pid (and optionally application group ID on Mac) from a user +// * Create a ProcessDescriptor pd +// * GetTransportForProcess(&pd, &transport) // * ReleaseTransport(transport) class DbgTransportTarget @@ -37,7 +40,7 @@ public: // Given a PID attempt to find or create a DbgTransportSession instance to manage a connection to a // runtime in that process. Returns E_UNEXPECTED if the process can't be found. Also returns a handle that // can be waited on for process termination. - HRESULT GetTransportForProcess(DWORD dwPID, DbgTransportSession **ppTransport, HANDLE *phProcessHandle); + HRESULT GetTransportForProcess(const ProcessDescriptor *pProcessDescriptor, DbgTransportSession **ppTransport, HANDLE *phProcessHandle); // Give back a previously acquired transport (if nobody else is using the transport it will close down the // connection at this point). diff --git a/src/debug/di/dbgtransportpipeline.cpp b/src/debug/di/dbgtransportpipeline.cpp index 88f6220c8e..645cf41987 100644 --- a/src/debug/di/dbgtransportpipeline.cpp +++ b/src/debug/di/dbgtransportpipeline.cpp @@ -91,7 +91,7 @@ public: LPPROCESS_INFORMATION lpProcessInformation); // Attach - virtual HRESULT DebugActiveProcess(MachineInfo machineInfo, DWORD processId); + virtual HRESULT DebugActiveProcess(MachineInfo machineInfo, const ProcessDescriptor& processDescriptor); // Detach virtual HRESULT DebugActiveProcessStop(DWORD processId); @@ -224,8 +224,10 @@ HRESULT DbgTransportPipeline::CreateProcessUnderDebugger( if (SUCCEEDED(hr)) { + ProcessDescriptor processDescriptor = ProcessDescriptor::Create(lpProcessInformation->dwProcessId, NULL); + // Establish a connection to the actual runtime to be debugged. - hr = m_pProxy->GetTransportForProcess(lpProcessInformation->dwProcessId, + hr = m_pProxy->GetTransportForProcess(&processDescriptor, &m_pTransport, &m_hProcess); if (SUCCEEDED(hr)) @@ -283,7 +285,7 @@ HRESULT DbgTransportPipeline::CreateProcessUnderDebugger( } // Attach the debugger to this process. -HRESULT DbgTransportPipeline::DebugActiveProcess(MachineInfo machineInfo, DWORD processId) +HRESULT DbgTransportPipeline::DebugActiveProcess(MachineInfo machineInfo, const ProcessDescriptor& processDescriptor) { // INativeEventPipeline has a 1:1 relationship with CordbProcess. _ASSERTE(!IsTransportRunning()); @@ -293,7 +295,7 @@ HRESULT DbgTransportPipeline::DebugActiveProcess(MachineInfo machineInfo, DWORD m_pProxy = g_pDbgTransportTarget; // Establish a connection to the actual runtime to be debugged. - hr = m_pProxy->GetTransportForProcess(processId, &m_pTransport, &m_hProcess); + hr = m_pProxy->GetTransportForProcess(&processDescriptor, &m_pTransport, &m_hProcess); if (SUCCEEDED(hr)) { // TODO: Pass this timeout as a parameter all the way from debugger @@ -313,7 +315,7 @@ HRESULT DbgTransportPipeline::DebugActiveProcess(MachineInfo machineInfo, DWORD if (SUCCEEDED(hr)) { - m_dwProcessId = processId; + m_dwProcessId = processDescriptor.m_Pid; m_fRunning = TRUE; } else diff --git a/src/debug/di/eventchannel.h b/src/debug/di/eventchannel.h index 5a4ff23ea8..1739d7568e 100644 --- a/src/debug/di/eventchannel.h +++ b/src/debug/di/eventchannel.h @@ -257,7 +257,7 @@ public: HRESULT NewEventChannelForThisPlatform(CORDB_ADDRESS pLeftSideDCB, ICorDebugMutableDataTarget * pMutableDataTarget, - DWORD dwProcessId, + const ProcessDescriptor * pProcessDescriptor, MachineInfo machineInfo, IEventChannel ** ppEventChannel); diff --git a/src/debug/di/eventredirectionpipeline.cpp b/src/debug/di/eventredirectionpipeline.cpp index 23405d643a..46c4997530 100644 --- a/src/debug/di/eventredirectionpipeline.cpp +++ b/src/debug/di/eventredirectionpipeline.cpp @@ -293,13 +293,13 @@ HRESULT EventRedirectionPipeline::CreateProcessUnderDebugger( // Attach -HRESULT EventRedirectionPipeline::DebugActiveProcess(MachineInfo machineInfo, DWORD processId) +HRESULT EventRedirectionPipeline::DebugActiveProcess(MachineInfo machineInfo, const ProcessDescriptor& processDescriptor) { - m_dwProcessId = processId; + m_dwProcessId = processDescriptor.m_Pid; // Use redirected pipeline // Spin up debugger to attach to target. - return AttachDebuggerToTarget(m_AttachParams.Value(), processId); + return AttachDebuggerToTarget(m_AttachParams.Value(), processDescriptor.m_Pid); } // Detach diff --git a/src/debug/di/eventredirectionpipeline.h b/src/debug/di/eventredirectionpipeline.h index 87549c1150..0da16175ca 100644 --- a/src/debug/di/eventredirectionpipeline.h +++ b/src/debug/di/eventredirectionpipeline.h @@ -59,7 +59,7 @@ public: LPPROCESS_INFORMATION lpProcessInformation); // Attach - virtual HRESULT DebugActiveProcess(MachineInfo machineInfo, DWORD processId); + virtual HRESULT DebugActiveProcess(MachineInfo machineInfo, const ProcessDescriptor& processDescriptor); // Detach virtual HRESULT DebugActiveProcessStop(DWORD processId); diff --git a/src/debug/di/localeventchannel.cpp b/src/debug/di/localeventchannel.cpp index f31c46f7bb..e89affaf57 100644 --- a/src/debug/di/localeventchannel.cpp +++ b/src/debug/di/localeventchannel.cpp @@ -115,7 +115,7 @@ private: // Allocate and return an old-style event channel object for this target platform. HRESULT NewEventChannelForThisPlatform(CORDB_ADDRESS pLeftSideDCB, ICorDebugMutableDataTarget * pMutableDataTarget, - DWORD dwProcessId, + const ProcessDescriptor * pProcessDescriptor, MachineInfo machineInfo, IEventChannel ** ppEventChannel) { diff --git a/src/debug/di/nativepipeline.h b/src/debug/di/nativepipeline.h index 9611d84404..5d22de9ed8 100644 --- a/src/debug/di/nativepipeline.h +++ b/src/debug/di/nativepipeline.h @@ -67,7 +67,7 @@ public: LPPROCESS_INFORMATION lpProcessInformation) = 0; // Attach - virtual HRESULT DebugActiveProcess(MachineInfo machineInfo, DWORD processId) = 0; + virtual HRESULT DebugActiveProcess(MachineInfo machineInfo, const ProcessDescriptor& processDescriptor) = 0; // Detach virtual HRESULT DebugActiveProcessStop(DWORD processId) =0; diff --git a/src/debug/di/process.cpp b/src/debug/di/process.cpp index 01b474bde6..2343122317 100644 --- a/src/debug/di/process.cpp +++ b/src/debug/di/process.cpp @@ -100,12 +100,13 @@ STDAPI OpenVirtualProcessImpl( // - there is no w32et thread (all threads are effectively an event thread) // - the stop state is 'live', which corresponds to CordbProcess not knowing what // its stop state really is (because that is now controlled by the shim). + ProcessDescriptor pd = ProcessDescriptor::CreateUninitialized(); IfFailThrow(CordbProcess::OpenVirtualProcess( clrInstanceId, pDataTarget, // takes a reference hDacModule, NULL, // Cordb - (DWORD) 0, // 0 for V3 cases (pShim == NULL). + &pd, // 0 for V3 cases (pShim == NULL). NULL, // no Shim in V3 cases &pProcess)); @@ -820,7 +821,7 @@ HRESULT CordbProcess::OpenVirtualProcess( IUnknown * pDataTarget, HMODULE hDacModule, Cordb* pCordb, - DWORD dwProcessID, + const ProcessDescriptor * pProcessDescriptor, ShimProcess * pShim, CordbProcess ** ppProcess) { @@ -849,7 +850,7 @@ HRESULT CordbProcess::OpenVirtualProcess( HRESULT hr = S_OK; RSUnsafeExternalSmartPtr<CordbProcess> pProcess; - pProcess.Assign(new (nothrow) CordbProcess(clrInstanceId, pDataTarget, hDacModule, pCordb, dwProcessID, pShim)); + pProcess.Assign(new (nothrow) CordbProcess(clrInstanceId, pDataTarget, hDacModule, pCordb, pProcessDescriptor, pShim)); if (pProcess == NULL) { @@ -916,12 +917,13 @@ CordbProcess::CordbProcess(ULONG64 clrInstanceId, IUnknown * pDataTarget, HMODULE hDacModule, Cordb * pCordb, - DWORD dwProcessID, + const ProcessDescriptor * pProcessDescriptor, ShimProcess * pShim) - : CordbBase(NULL, dwProcessID, enumCordbProcess), + : CordbBase(NULL, pProcessDescriptor->m_Pid, enumCordbProcess), m_fDoDelayedManagedAttached(false), m_cordb(pCordb), m_handle(NULL), + m_processDescriptor(*pProcessDescriptor), m_detached(false), m_uninitializedStop(false), m_exiting(false), @@ -1194,7 +1196,7 @@ HRESULT ShimProcess::CreateProcess( HRESULT ShimProcess::DebugActiveProcess( Cordb * pCordb, ICorDebugRemoteTarget * pRemoteTarget, - DWORD dwProcessID, + const ProcessDescriptor * pProcessDescriptor, BOOL fWin32Attach ) { @@ -1216,7 +1218,7 @@ HRESULT ShimProcess::DebugActiveProcess( // If this succeeds, new CordbProcess will add a ref to the ShimProcess hr = pShim->GetWin32EventThread()->SendDebugActiveProcessEvent(pShim->GetMachineInfo(), - dwProcessID, + pProcessDescriptor, fWin32Attach == TRUE, NULL); IfFailThrow(hr); @@ -3318,22 +3320,22 @@ HRESULT CordbProcess::GetID(DWORD *pdwProcessId) *pdwProcessId = 0; ThrowHR(E_NOTIMPL); } - *pdwProcessId = GetPid(); + *pdwProcessId = GetProcessDescriptor()->m_Pid; } EX_CATCH_HRESULT(hr); return hr; } -// Helper to get PID internally. We know we'll always succeed. +// Helper to get process descriptor internally. We know we'll always succeed. // This is more convient for internal callers since they can just use it as an expression // without having to check HRESULTS. -DWORD CordbProcess::GetPid() +const ProcessDescriptor* CordbProcess::GetProcessDescriptor() { // This shouldn't be used in V3 paths, in which case it's set to 0. Only the shim should be // calling this. Assert to catch anybody else. - _ASSERTE(m_id != 0); + _ASSERTE(m_processDescriptor.IsInitialized()); - return (DWORD) m_id; + return &m_processDescriptor; } @@ -7517,7 +7519,7 @@ void CordbProcess::GetEventBlock(BOOL * pfBlockExists) IfFailThrow(NewEventChannelForThisPlatform(pLeftSideDCB, m_pMutableDataTarget, - GetPid(), + GetProcessDescriptor(), m_pShim->GetMachineInfo(), &m_pEventChannel)); _ASSERTE(m_pEventChannel != NULL); @@ -13809,9 +13811,10 @@ void CordbWin32EventThread::CreateProcess() { // Process ID is filled in after process is succesfully created. DWORD dwProcessId = m_actionData.createData.lpProcessInformation->dwProcessId; + ProcessDescriptor pd = ProcessDescriptor::FromPid(dwProcessId); RSUnsafeExternalSmartPtr<CordbProcess> pProcess; - hr = m_pShim->InitializeDataTarget(dwProcessId); + hr = m_pShim->InitializeDataTarget(&pd); if (SUCCEEDED(hr)) { @@ -13819,7 +13822,7 @@ void CordbWin32EventThread::CreateProcess() // OpenVirtualProcess. This will then connect to the first CLR // loaded. const ULONG64 cFirstClrLoaded = 0; - hr = CordbProcess::OpenVirtualProcess(cFirstClrLoaded, m_pShim->GetDataTarget(), NULL, m_cordb, dwProcessId, m_pShim, &pProcess); + hr = CordbProcess::OpenVirtualProcess(cFirstClrLoaded, m_pShim->GetDataTarget(), NULL, m_cordb, &pd, m_pShim, &pProcess); } // Shouldn't happen on a create, only an attach @@ -13866,7 +13869,7 @@ void CordbWin32EventThread::CreateProcess() // HRESULT CordbWin32EventThread::SendDebugActiveProcessEvent( MachineInfo machineInfo, - DWORD pid, + const ProcessDescriptor *pProcessDescriptor, bool fWin32Attach, CordbProcess *pProcess) { @@ -13875,7 +13878,7 @@ HRESULT CordbWin32EventThread::SendDebugActiveProcessEvent( LockSendToWin32EventThreadMutex(); m_actionData.attachData.machineInfo = machineInfo; - m_actionData.attachData.processId = pid; + m_actionData.attachData.processDescriptor = *pProcessDescriptor; #if !defined(FEATURE_DBGIPC_TRANSPORT_DI) m_actionData.attachData.fWin32Attach = fWin32Attach; #endif @@ -14004,17 +14007,16 @@ void CordbWin32EventThread::AttachProcess() HRESULT hr = S_OK; - DWORD dwProcessId = m_actionData.attachData.processId; + ProcessDescriptor processDescriptor = m_actionData.attachData.processDescriptor; bool fNativeAttachSucceeded = false; - // Always do OS attach to the target. // By this point, the pid should be valid (because OpenProcess above), pending some race where the process just exited. // The OS will enforce that only 1 debugger is attached. // Common failure paths here would be: access denied, double-attach { hr = m_pNativePipeline->DebugActiveProcess(m_actionData.attachData.machineInfo, - dwProcessId); + processDescriptor); if (FAILED(hr)) { goto LExit; @@ -14023,7 +14025,7 @@ void CordbWin32EventThread::AttachProcess() } - hr = m_pShim->InitializeDataTarget(m_actionData.attachData.processId); + hr = m_pShim->InitializeDataTarget(&processDescriptor); if (FAILED(hr)) { goto LExit; @@ -14034,7 +14036,7 @@ void CordbWin32EventThread::AttachProcess() // loaded. { const ULONG64 cFirstClrLoaded = 0; - hr = CordbProcess::OpenVirtualProcess(cFirstClrLoaded, m_pShim->GetDataTarget(), NULL, m_cordb, dwProcessId, m_pShim, &pProcess); + hr = CordbProcess::OpenVirtualProcess(cFirstClrLoaded, m_pShim->GetDataTarget(), NULL, m_cordb, &processDescriptor, m_pShim, &pProcess); if (FAILED(hr)) { goto LExit; @@ -14081,7 +14083,7 @@ LExit: // If we succeed to do a native-attach, but then failed elsewhere, try to native-detach. if (fNativeAttachSucceeded) { - m_pNativePipeline->DebugActiveProcessStop(dwProcessId); + m_pNativePipeline->DebugActiveProcessStop(processDescriptor.m_Pid); } if (pProcess != NULL) @@ -14509,7 +14511,7 @@ void CordbWin32EventThread::ExitProcess(bool fDetach) // Eventually, the Debugger owns the detach pipeline, so this won't be necessary. if (fDetach && (m_pProcess != NULL)) { - HRESULT hr = m_pNativePipeline->DebugActiveProcessStop(m_pProcess->GetPid()); + HRESULT hr = m_pNativePipeline->DebugActiveProcessStop(m_pProcess->GetProcessDescriptor()->m_Pid); // We don't expect detach to fail (we check earlier for common conditions that // may cause it to fail) diff --git a/src/debug/di/remoteeventchannel.cpp b/src/debug/di/remoteeventchannel.cpp index fc1d57a60f..ab810008a3 100644 --- a/src/debug/di/remoteeventchannel.cpp +++ b/src/debug/di/remoteeventchannel.cpp @@ -90,7 +90,7 @@ private: // Allocate and return an old-style event channel object for this target platform. HRESULT NewEventChannelForThisPlatform(CORDB_ADDRESS pLeftSideDCB, ICorDebugMutableDataTarget * pMutableDataTarget, - DWORD dwProcessId, + const ProcessDescriptor * pProcessDescriptor, MachineInfo machineInfo, IEventChannel ** ppEventChannel) { @@ -105,7 +105,7 @@ HRESULT NewEventChannelForThisPlatform(CORDB_ADDRESS pLeftSideDCB, DbgTransportTarget * pProxy = g_pDbgTransportTarget; DbgTransportSession * pTransport = NULL; - hr = pProxy->GetTransportForProcess(dwProcessId, &pTransport, &hDummy); + hr = pProxy->GetTransportForProcess(pProcessDescriptor, &pTransport, &hDummy); if (FAILED(hr)) { goto Label_Exit; diff --git a/src/debug/di/rsmain.cpp b/src/debug/di/rsmain.cpp index 750cb1d8ee..f2f9b794c1 100644 --- a/src/debug/di/rsmain.cpp +++ b/src/debug/di/rsmain.cpp @@ -957,13 +957,17 @@ namespace /* ------------------------------------------------------------------------- * * Cordb class * ------------------------------------------------------------------------- */ - - Cordb::Cordb(CorDebugInterfaceVersion iDebuggerVersion) + : Cordb(iDebuggerVersion, ProcessDescriptor::CreateUninitialized()) +{ +} + +Cordb::Cordb(CorDebugInterfaceVersion iDebuggerVersion, const ProcessDescriptor& pd) : CordbBase(NULL, 0, enumCordb), m_processes(11), m_initialized(false), - m_debuggerSpecifiedVersion(iDebuggerVersion) + m_debuggerSpecifiedVersion(iDebuggerVersion), + m_pd(pd) #ifdef FEATURE_CORESYSTEM , m_targetCLR(0) @@ -980,6 +984,10 @@ Cordb::Cordb(CorDebugInterfaceVersion iDebuggerVersion) Cordb::~Cordb() { LOG((LF_CORDB, LL_INFO10, "C::~C Terminating Cordb object.\n")); + if (m_pd.m_ApplicationGroupId != NULL) + { + delete [] m_pd.m_ApplicationGroupId; + } g_pRSDebuggingInfo->m_Cordb = NULL; } @@ -1819,6 +1827,12 @@ HRESULT Cordb::DebugActiveProcessCommon(ICorDebugRemoteTarget * pRemoteTarget, ThrowHR(E_FAIL); } + // Verify that given process ID, matches the process ID for which the object was created + if (m_pd.IsInitialized() && m_pd.m_Pid != dwProcessId) + { + ThrowHR(E_INVALIDARG); + } + // See the comment in Cordb::CreateProcess _ASSERTE(CorDebugInvalidVersion != m_debuggerSpecifiedVersion); @@ -1845,7 +1859,7 @@ HRESULT Cordb::DebugActiveProcessCommon(ICorDebugRemoteTarget * pRemoteTarget, hr = ShimProcess::DebugActiveProcess( this, pRemoteTarget, - dwProcessId, + &m_pd, fWin32Attach == TRUE); // If that worked, then there will be a process object... @@ -2065,7 +2079,7 @@ void Cordb::EnsureCanLaunchOrAttach(BOOL fWin32DebuggingEnabled) HRESULT Cordb::CreateObjectV1(REFIID id, void **object) { - return CreateObject(CorDebugVersion_1_0, id, object); + return CreateObject(CorDebugVersion_1_0, ProcessDescriptor::UNINITIALIZED_PID, NULL, id, object); } #if defined(FEATURE_DBGIPC_TRANSPORT_DI) @@ -2073,21 +2087,52 @@ HRESULT Cordb::CreateObjectV1(REFIID id, void **object) // same debug engine version as V2, though this may change in the future. HRESULT Cordb::CreateObjectTelesto(REFIID id, void ** pObject) { - return CreateObject(CorDebugVersion_2_0, id, pObject); + return CreateObject(CorDebugVersion_2_0, ProcessDescriptor::UNINITIALIZED_PID, NULL, id, pObject); } #endif // FEATURE_DBGIPC_TRANSPORT_DI // Static // Used to create an instance for a ClassFactory (thus an external ref). -HRESULT Cordb::CreateObject(CorDebugInterfaceVersion iDebuggerVersion, REFIID id, void **object) +HRESULT Cordb::CreateObject(CorDebugInterfaceVersion iDebuggerVersion, DWORD pid, LPCWSTR lpApplicationGroupId, REFIID id, void **object) { if (id != IID_IUnknown && id != IID_ICorDebug) return (E_NOINTERFACE); - Cordb *db = new (nothrow) Cordb(iDebuggerVersion); + LPSTR applicationGroupId = NULL; + if (lpApplicationGroupId != NULL) + { + // Get length of target string + int cbMultiByte = WideCharToMultiByte(CP_ACP, 0, lpApplicationGroupId, -1, NULL, 0, NULL, NULL); + if (cbMultiByte == 0) + { + return E_FAIL; + } + + applicationGroupId = new (nothrow) CHAR[cbMultiByte]; + if (applicationGroupId == NULL) + { + return (E_OUTOFMEMORY); + } + + /* Convert to ASCII */ + cbMultiByte = WideCharToMultiByte(CP_ACP, 0, lpApplicationGroupId, -1, applicationGroupId, cbMultiByte, NULL, NULL); + if (cbMultiByte == 0) + { + return E_FAIL; + } + } + + ProcessDescriptor pd = ProcessDescriptor::Create(pid, applicationGroupId); + + Cordb *db = new (nothrow) Cordb(iDebuggerVersion, pd); if (db == NULL) + { + if (applicationGroupId != NULL) + delete [] applicationGroupId; + return (E_OUTOFMEMORY); + } *object = static_cast<ICorDebug*> (db); db->ExternalAddRef(); diff --git a/src/debug/di/rspriv.h b/src/debug/di/rspriv.h index 8b388ee684..fef79d80ca 100644 --- a/src/debug/di/rspriv.h +++ b/src/debug/di/rspriv.h @@ -44,6 +44,7 @@ struct MachineInfo; +#include "processdescriptor.h" #include "nativepipeline.h" #include "stringcopyholder.h" @@ -2226,7 +2227,7 @@ public: #if defined(FEATURE_DBGIPC_TRANSPORT_DI) static COM_METHOD CreateObjectTelesto(REFIID id, void ** pObject); #endif // FEATURE_DBGIPC_TRANSPORT_DI - static COM_METHOD CreateObject(CorDebugInterfaceVersion iDebuggerVersion, REFIID id, void **object); + static COM_METHOD CreateObject(CorDebugInterfaceVersion iDebuggerVersion, DWORD pid, LPCWSTR lpApplicationGroupId, REFIID id, void **object); //----------------------------------------------------------- // ICorDebugRemote @@ -2297,6 +2298,9 @@ public: CordbAppDomain *appDomain, DebuggerIPCEvent* event); +private: + Cordb(CorDebugInterfaceVersion iDebuggerVersion, const ProcessDescriptor& pd); + //----------------------------------------------------------- // Data members //----------------------------------------------------------- @@ -2332,6 +2336,9 @@ private: // This is the version of the ICorDebug APIs that the debugger believes it's consuming. CorDebugInterfaceVersion m_debuggerSpecifiedVersion; + // Store information about the process to be debugged + ProcessDescriptor m_pd; + //Note - this code could be useful outside coresystem, but keeping the change localized // because we are late in the win8 release #ifdef FEATURE_CORESYSTEM @@ -2804,8 +2811,8 @@ void DeleteIPCEventHelper(DebuggerIPCEvent *pDel); class IProcessShimHooks { public: - // Get the OS Process ID of the target. - virtual DWORD GetPid() = 0; + // Get the OS Process descriptor of the target. + virtual const ProcessDescriptor* GetProcessDescriptor() = 0; // Request a synchronization for attach. // This essentially just sends an AsyncBreak to the left-side. Once the target is @@ -2932,7 +2939,7 @@ class CordbProcess : #endif { // Ctor is private. Use OpenVirtualProcess instead. - CordbProcess(ULONG64 clrInstanceId, IUnknown * pDataTarget, HMODULE hDacModule, Cordb * pCordb, DWORD dwProcessID, ShimProcess * pShim); + CordbProcess(ULONG64 clrInstanceId, IUnknown * pDataTarget, HMODULE hDacModule, Cordb * pCordb, const ProcessDescriptor * pProcessDescriptor, ShimProcess * pShim); public: @@ -2952,7 +2959,7 @@ public: IUnknown * pDataTarget, HMODULE hDacModule, Cordb * pCordb, - DWORD dwProcessID, + const ProcessDescriptor * pProcessDescriptor, ShimProcess * pShim, CordbProcess ** ppProcess); @@ -3266,8 +3273,8 @@ public: bool IsThreadSuspendedOrHijacked(ICorDebugThread * pICorDebugThread); - // Helper to get PID internally. - DWORD GetPid(); + // Helper to get ProcessDescriptor internally. + const ProcessDescriptor* GetProcessDescriptor(); HRESULT GetRuntimeOffsets(); @@ -3708,6 +3715,9 @@ private: // wait on for process termination. HANDLE m_handle; + // Process descriptor - holds PID and App group ID for Mac debugging + ProcessDescriptor m_processDescriptor; + public: // Wrapper to get the OS process handle. This is unsafe because it breaks the data-target abstraction. // The only things that need this should be calls to DuplicateHandle, and some shimming work. @@ -10084,7 +10094,7 @@ public: CorDebugCreateProcessFlags corDebugFlags); HRESULT SendDebugActiveProcessEvent(MachineInfo machineInfo, - DWORD pid, + const ProcessDescriptor *pProcessDescriptor, bool fWin32Attach, CordbProcess *pProcess); @@ -10183,12 +10193,12 @@ private: struct { - MachineInfo machineInfo; - DWORD processId; + MachineInfo machineInfo; + ProcessDescriptor processDescriptor; #if !defined(FEATURE_DBGIPC_TRANSPORT_DI) - bool fWin32Attach; + bool fWin32Attach; #endif - CordbProcess *pProcess; + CordbProcess *pProcess; // Wrapper to determine if we're interop-debugging. bool IsInteropDebugging() @@ -10715,7 +10725,7 @@ private: class CorpubProcess : public CordbCommonBase, public ICorPublishProcess { public: - CorpubProcess(DWORD dwProcessId, + CorpubProcess(const ProcessDescriptor * pProcessDescriptor, bool fManaged, HANDLE hProcess, HANDLE hMutex, @@ -10774,7 +10784,7 @@ public: bool IsExited(); public: - DWORD m_dwProcessId; + ProcessDescriptor m_processDescriptor; private: bool m_fIsManaged; diff --git a/src/debug/di/shimdatatarget.h b/src/debug/di/shimdatatarget.h index adcbae8056..09e199d98e 100644 --- a/src/debug/di/shimdatatarget.h +++ b/src/debug/di/shimdatatarget.h @@ -126,7 +126,7 @@ protected: // HRESULT BuildPlatformSpecificDataTarget(MachineInfo machineInfo, - DWORD processId, + const ProcessDescriptor * pProcessDescriptor, ShimDataTarget ** ppDataTarget); #endif // SHIMDATATARGET_H_ diff --git a/src/debug/di/shimlocaldatatarget.cpp b/src/debug/di/shimlocaldatatarget.cpp index 36ea611af2..5057f46291 100644 --- a/src/debug/di/shimlocaldatatarget.cpp +++ b/src/debug/di/shimlocaldatatarget.cpp @@ -208,7 +208,7 @@ void ShimLocalDataTarget::Dispose() // HRESULT BuildPlatformSpecificDataTarget(MachineInfo machineInfo, - DWORD processId, + const ProcessDescriptor * pProcessDescriptor, ShimDataTarget ** ppDataTarget) { HRESULT hr = S_OK; @@ -226,7 +226,7 @@ HRESULT BuildPlatformSpecificDataTarget(MachineInfo machineInfo, PROCESS_VM_WRITE | SYNCHRONIZE, FALSE, - processId); + pProcessDescriptor->m_Pid); if (hProcess == NULL) { @@ -247,7 +247,7 @@ HRESULT BuildPlatformSpecificDataTarget(MachineInfo machineInfo, { goto Label_Exit; } - pLocalDataTarget = new (nothrow) ShimLocalDataTarget(processId, hProcess); + pLocalDataTarget = new (nothrow) ShimLocalDataTarget(pProcessDescriptor->m_Pid, hProcess); if (pLocalDataTarget == NULL) { hr = E_OUTOFMEMORY; diff --git a/src/debug/di/shimpriv.h b/src/debug/di/shimpriv.h index 4c9eb7ab86..279feb978d 100644 --- a/src/debug/di/shimpriv.h +++ b/src/debug/di/shimpriv.h @@ -358,7 +358,7 @@ public: // 3. Create OS-debugging pipeline. This establishes the physical OS process and gets us a pid/handle // 4. pShim->InitializeDataTarget - this creates a reader/writer abstraction around the OS process. // 5. pShim->SetProcess() - this connects the Shim to the ICDProcess object. - HRESULT InitializeDataTarget(DWORD processId); + HRESULT InitializeDataTarget(const ProcessDescriptor * pProcessDescriptor); void SetProcess(ICorDebugProcess * pProcess); //----------------------------------------------------------- @@ -384,7 +384,7 @@ public: static HRESULT DebugActiveProcess( Cordb * pCordb, ICorDebugRemoteTarget * pRemoteTarget, - DWORD pid, + const ProcessDescriptor * pProcessDescriptor, BOOL win32Attach ); diff --git a/src/debug/di/shimprocess.cpp b/src/debug/di/shimprocess.cpp index 684173abca..b45bbe4ff4 100644 --- a/src/debug/di/shimprocess.cpp +++ b/src/debug/di/shimprocess.cpp @@ -132,7 +132,7 @@ void ShimProcess::SetProcess(ICorDebugProcess * pProcess) if (pProcess != NULL) { // Verify that DataTarget + new process have the same pid? - _ASSERTE(m_pProcess->GetPid() == m_pLiveDataTarget->GetPid()); + _ASSERTE(m_pProcess->GetProcessDescriptor()->m_Pid == m_pLiveDataTarget->GetPid()); } } @@ -152,12 +152,12 @@ void ShimProcess::SetProcess(ICorDebugProcess * pProcess) // Notes: // Only call this once, during the initialization dance. // -HRESULT ShimProcess::InitializeDataTarget(DWORD processId) +HRESULT ShimProcess::InitializeDataTarget(const ProcessDescriptor * pProcessDescriptor) { _ASSERTE(m_pLiveDataTarget == NULL); - HRESULT hr = BuildPlatformSpecificDataTarget(GetMachineInfo(), processId, &m_pLiveDataTarget); + HRESULT hr = BuildPlatformSpecificDataTarget(GetMachineInfo(), pProcessDescriptor, &m_pLiveDataTarget); if (FAILED(hr)) { _ASSERTE(m_pLiveDataTarget == NULL); diff --git a/src/debug/di/shimremotedatatarget.cpp b/src/debug/di/shimremotedatatarget.cpp index f971b0d65a..7e6b8375ee 100644 --- a/src/debug/di/shimremotedatatarget.cpp +++ b/src/debug/di/shimremotedatatarget.cpp @@ -154,7 +154,7 @@ void ShimRemoteDataTarget::Dispose() // HRESULT BuildPlatformSpecificDataTarget(MachineInfo machineInfo, - DWORD processId, + const ProcessDescriptor * pProcessDescriptor, ShimDataTarget ** ppDataTarget) { HandleHolder hDummy; @@ -164,7 +164,7 @@ HRESULT BuildPlatformSpecificDataTarget(MachineInfo machineInfo, DbgTransportTarget * pProxy = g_pDbgTransportTarget; DbgTransportSession * pTransport = NULL; - hr = pProxy->GetTransportForProcess(processId, &pTransport, &hDummy); + hr = pProxy->GetTransportForProcess(pProcessDescriptor, &pTransport, &hDummy); if (FAILED(hr)) { goto Label_Exit; @@ -176,7 +176,7 @@ HRESULT BuildPlatformSpecificDataTarget(MachineInfo machineInfo, goto Label_Exit; } - pRemoteDataTarget = new (nothrow) ShimRemoteDataTarget(processId, pProxy, pTransport); + pRemoteDataTarget = new (nothrow) ShimRemoteDataTarget(pProcessDescriptor->m_Pid, pProxy, pTransport); if (pRemoteDataTarget == NULL) { hr = E_OUTOFMEMORY; diff --git a/src/debug/di/windowspipeline.cpp b/src/debug/di/windowspipeline.cpp index c3050e3290..6e58f0be15 100644 --- a/src/debug/di/windowspipeline.cpp +++ b/src/debug/di/windowspipeline.cpp @@ -74,7 +74,7 @@ public: LPPROCESS_INFORMATION lpProcessInformation); // Attach - virtual HRESULT DebugActiveProcess(MachineInfo machineInfo, DWORD processId); + virtual HRESULT DebugActiveProcess(MachineInfo machineInfo, const ProcessDescriptor& processDescriptor); // Detach virtual HRESULT DebugActiveProcessStop(DWORD processId); @@ -205,15 +205,15 @@ HRESULT WindowsNativePipeline::CreateProcessUnderDebugger( } // Attach the debugger to this process. -HRESULT WindowsNativePipeline::DebugActiveProcess(MachineInfo machineInfo, DWORD processId) +HRESULT WindowsNativePipeline::DebugActiveProcess(MachineInfo machineInfo, const ProcessDescriptor& processDescriptor) { HRESULT hr = E_FAIL; - BOOL ret = ::DebugActiveProcess(processId); + BOOL ret = ::DebugActiveProcess(processDescriptor.m_Pid); if (ret) { hr = S_OK; - m_dwProcessId = processId; + m_dwProcessId = processDescriptor.m_Pid; UpdateDebugSetProcessKillOnExit(); } else @@ -233,7 +233,7 @@ HRESULT WindowsNativePipeline::DebugActiveProcess(MachineInfo machineInfo, DWORD // attached. But I think it's better to only return the specific error code if we know for sure // the case is true. BOOL fIsDebuggerPresent = FALSE; - if (SUCCEEDED(IsRemoteDebuggerPresent(processId, &fIsDebuggerPresent))) + if (SUCCEEDED(IsRemoteDebuggerPresent(processDescriptor.m_Pid, &fIsDebuggerPresent))) { if (fIsDebuggerPresent) { diff --git a/src/debug/inc/dbgtransportsession.h b/src/debug/inc/dbgtransportsession.h index 5187202753..390226781f 100644 --- a/src/debug/inc/dbgtransportsession.h +++ b/src/debug/inc/dbgtransportsession.h @@ -318,7 +318,7 @@ public: // requires the addresses of a couple of runtime data structures to service certain debugger requests that // may be delivered once the session is established. #ifdef RIGHT_SIDE_COMPILE - HRESULT Init(DWORD pid, HANDLE hProcessExited); + HRESULT Init(const ProcessDescriptor& pd, HANDLE hProcessExited); #else HRESULT Init(DebuggerIPCControlBlock * pDCB, AppDomainEnumerationIPCBlock * pADB); #endif // RIGHT_SIDE_COMPILE @@ -717,11 +717,11 @@ private: #ifdef RIGHT_SIDE_COMPILE // On the RS the transport thread needs to know the IP address and port number to Connect() to. - DWORD m_pid; // Id of a process we're talking to. + ProcessDescriptor m_pd; // Descriptor of a process we're talking to. - HANDLE m_hProcessExited; // event which will be signaled when the debuggee is terminated + HANDLE m_hProcessExited; // event which will be signaled when the debuggee is terminated - bool m_fDebuggerAttached; + bool m_fDebuggerAttached; #endif // Debugger event handling. To improve performance we allow the debugger to send as many events as it diff --git a/src/debug/inc/processdescriptor.h b/src/debug/inc/processdescriptor.h new file mode 100644 index 0000000000..89840b26a8 --- /dev/null +++ b/src/debug/inc/processdescriptor.h @@ -0,0 +1,38 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. +//***************************************************************************** + +#ifndef _PROCESSCONTEXT_H +#define _PROCESSCONTEXT_H + +struct ProcessDescriptor +{ + const static DWORD UNINITIALIZED_PID = 0; + + static ProcessDescriptor Create(DWORD pid, LPCSTR applicationGroupId) + { + ProcessDescriptor pd; + pd.m_Pid = pid; + pd.m_ApplicationGroupId = applicationGroupId; + + return pd; + } + + static ProcessDescriptor FromCurrentProcess(); + static ProcessDescriptor FromPid(DWORD pid) + { + return Create(pid, nullptr); + } + static ProcessDescriptor CreateUninitialized() + { + return Create(UNINITIALIZED_PID, nullptr); + } + + bool IsInitialized() const { return m_Pid != UNINITIALIZED_PID; } + + DWORD m_Pid; + LPCSTR m_ApplicationGroupId; +}; + +#endif // _PROCESSCONTEXT_H
\ No newline at end of file diff --git a/src/debug/inc/twowaypipe.h b/src/debug/inc/twowaypipe.h index 6bc0f9f39e..250e40fcb8 100644 --- a/src/debug/inc/twowaypipe.h +++ b/src/debug/inc/twowaypipe.h @@ -6,6 +6,8 @@ #ifndef TwoWayPipe_H #define TwoWayPipe_H +#include "processdescriptor.h" + #ifdef FEATURE_PAL #define INVALID_PIPE -1 #else @@ -43,14 +45,14 @@ public: } // Creates a server side of the pipe. - // Id is used to create pipes names and uniquely identify the pipe on the machine. + // pd is used to create pipes names and uniquely identify the pipe on the machine. // true - success, false - failure (use GetLastError() for more details) - bool CreateServer(DWORD id); + bool CreateServer(const ProcessDescriptor& pd); // Connects to a previously opened server side of the pipe. - // Id is used to locate the pipe on the machine. + // pd is used to locate the pipe on the machine. // true - success, false - failure (use GetLastError() for more details) - bool Connect(DWORD id); + bool Connect(const ProcessDescriptor& pd); // Waits for incoming client connections, assumes GetState() == Created // true - success, false - failure (use GetLastError() for more details) @@ -83,7 +85,6 @@ private: #ifdef FEATURE_PAL - int m_id; // id that was passed to CreateServer() or Connect() int m_inboundPipe, m_outboundPipe; // two one sided pipes used for communication char m_inPipeName[MAX_DEBUGGER_TRANSPORT_PIPE_NAME_LENGTH]; // filename of the inbound pipe char m_outPipeName[MAX_DEBUGGER_TRANSPORT_PIPE_NAME_LENGTH]; // filename of the outbound pipe diff --git a/src/debug/shared/dbgtransportsession.cpp b/src/debug/shared/dbgtransportsession.cpp index f526f91a81..3c0eadb81c 100644 --- a/src/debug/shared/dbgtransportsession.cpp +++ b/src/debug/shared/dbgtransportsession.cpp @@ -73,7 +73,7 @@ DbgTransportSession::~DbgTransportSession() // addresses of a couple of runtime data structures to service certain debugger requests that may be delivered // once the session is established. #ifdef RIGHT_SIDE_COMPILE -HRESULT DbgTransportSession::Init(DWORD pid, HANDLE hProcessExited) +HRESULT DbgTransportSession::Init(const ProcessDescriptor& pd, HANDLE hProcessExited) #else // RIGHT_SIDE_COMPILE HRESULT DbgTransportSession::Init(DebuggerIPCControlBlock *pDCB, AppDomainEnumerationIPCBlock *pADB) #endif // RIGHT_SIDE_COMPILE @@ -104,7 +104,7 @@ HRESULT DbgTransportSession::Init(DebuggerIPCControlBlock *pDCB, AppDomainEnumer #ifdef RIGHT_SIDE_COMPILE - m_pid = pid; + m_pd = pd; if (!DuplicateHandle(GetCurrentProcess(), hProcessExited, @@ -131,8 +131,8 @@ HRESULT DbgTransportSession::Init(DebuggerIPCControlBlock *pDCB, AppDomainEnumer if (m_hSessionOpenEvent == NULL) return E_OUTOFMEMORY; #else // RIGHT_SIDE_COMPILE - DWORD pid = GetCurrentProcessId(); - if (!m_pipe.CreateServer(pid)) { + ProcessDescriptor pd = ProcessDescriptor::FromCurrentProcess(); + if (!m_pipe.CreateServer(pd)) { return E_OUTOFMEMORY; } #endif // RIGHT_SIDE_COMPILE @@ -1289,7 +1289,7 @@ void DbgTransportSession::TransportWorker() eStatus = SCS_NetworkFailure; else { - if (m_pipe.Connect(m_pid)) + if (m_pipe.Connect(m_pd)) { eStatus = SCS_Success; } @@ -1315,8 +1315,8 @@ void DbgTransportSession::TransportWorker() eStatus = SCS_NetworkFailure; else { - DWORD pid = GetCurrentProcessId(); - if ((m_pipe.GetState() == TwoWayPipe::Created || m_pipe.CreateServer(pid)) && + ProcessDescriptor pd = ProcessDescriptor::FromCurrentProcess(); + if ((m_pipe.GetState() == TwoWayPipe::Created || m_pipe.CreateServer(pd)) && m_pipe.WaitForConnection()) { eStatus = SCS_Success; diff --git a/src/dlls/dbgshim/dbgshim.cpp b/src/dlls/dbgshim/dbgshim.cpp index 1e5c876888..020dbdc564 100644 --- a/src/dlls/dbgshim/dbgshim.cpp +++ b/src/dlls/dbgshim/dbgshim.cpp @@ -194,11 +194,61 @@ GetContinueStartupEvent( // Functions that we'll look for in the loaded Mscordbi module. typedef HRESULT (STDAPICALLTYPE *FPCoreCLRCreateCordbObject)( - int iDebuggerVersion, - DWORD pid, - HMODULE hmodTargetCLR, + int iDebuggerVersion, + DWORD pid, + HMODULE hmodTargetCLR, + IUnknown **ppCordb); + +typedef HRESULT (STDAPICALLTYPE *FPCoreCLRCreateCordbObjectEx)( + int iDebuggerVersion, + DWORD pid, + LPCWSTR lpApplicationGroupId, + HMODULE hmodTargetCLR, IUnknown **ppCordb); +HRESULT CreateCoreDbgWithSandboxSupport( + HMODULE hCLRModule, HMODULE hDBIModule, DWORD processId, LPCWSTR lpApplicationGroupId, int iDebuggerVersion, IUnknown **ppCordb) +{ + FPCoreCLRCreateCordbObjectEx fpCreate = + (FPCoreCLRCreateCordbObjectEx)GetProcAddress(hDBIModule, "CoreCLRCreateCordbObjectEx"); + if (fpCreate == NULL) + { + return CORDBG_E_INCOMPATIBLE_PROTOCOL; + } + + return fpCreate(iDebuggerVersion, processId, lpApplicationGroupId, hCLRModule, ppCordb); +} + +HRESULT CreateCoreDbgWithoutSandboxSupport( + HMODULE hCLRModule, HMODULE hDBIModule, DWORD processId, int iDebuggerVersion, IUnknown **ppCordb) +{ + FPCoreCLRCreateCordbObject fpCreate = + (FPCoreCLRCreateCordbObject)GetProcAddress(hDBIModule, "CoreCLRCreateCordbObject"); + if (fpCreate == NULL) + { + return CORDBG_E_INCOMPATIBLE_PROTOCOL; + } + + return fpCreate(iDebuggerVersion, processId, hCLRModule, ppCordb); +} + +HRESULT CreateCoreDbg( + HMODULE hCLRModule, HMODULE hDBIModule, DWORD processId, LPCWSTR lpApplicationGroupId, int iDebuggerVersion, IUnknown **ppCordb) +{ + HRESULT hr = S_OK; + + if (lpApplicationGroupId != NULL) + { + hr = CreateCoreDbgWithSandboxSupport(hCLRModule, hDBIModule, processId, lpApplicationGroupId, iDebuggerVersion, ppCordb); + } + else + { + hr = CreateCoreDbgWithoutSandboxSupport(hCLRModule, hDBIModule, processId, iDebuggerVersion, ppCordb); + } + + return hr; +} + // // Helper class for RegisterForRuntimeStartup // @@ -210,6 +260,7 @@ class RuntimeStartupHelper PVOID m_parameter; #ifdef FEATURE_PAL PVOID m_unregisterToken; + LPWSTR m_applicationGroupId; #else bool m_canceled; HANDLE m_startupEvent; @@ -224,7 +275,8 @@ public: m_callback(pfnCallback), m_parameter(parameter), #ifdef FEATURE_PAL - m_unregisterToken(NULL) + m_unregisterToken(NULL), + m_applicationGroupId(NULL) #else m_canceled(false), m_startupEvent(NULL), @@ -236,7 +288,12 @@ public: ~RuntimeStartupHelper() { -#ifndef FEATURE_PAL +#ifdef FEATURE_PAL + if (m_applicationGroupId != NULL) + { + delete m_applicationGroupId; + } +#else // FEATURE_PAL if (m_startupEvent != NULL) { CloseHandle(m_startupEvent); @@ -266,9 +323,20 @@ public: #ifdef FEATURE_PAL - HRESULT Register() + HRESULT Register(LPCWSTR lpApplicationGroupId) { - DWORD pe = PAL_RegisterForRuntimeStartup(m_processId, RuntimeStartupHandler, this, &m_unregisterToken); + if (lpApplicationGroupId != NULL) + { + int size = wcslen(lpApplicationGroupId) + 1; + m_applicationGroupId = new (nothrow) WCHAR[size]; + if (m_applicationGroupId == NULL) + { + return E_OUTOFMEMORY; + } + wcscpy_s(m_applicationGroupId, size, lpApplicationGroupId); + } + + DWORD pe = PAL_RegisterForRuntimeStartup(m_processId, m_applicationGroupId, RuntimeStartupHandler, this, &m_unregisterToken); if (pe != NO_ERROR) { return HRESULT_FROM_WIN32(pe); @@ -297,7 +365,6 @@ public: PAL_CPP_TRY { - FPCoreCLRCreateCordbObject fpCreate = NULL; char dbiPath[MAX_LONGPATH]; char *pszLast = strrchr(pszModulePath, DIRECTORY_SEPARATOR_CHAR_A); @@ -318,14 +385,7 @@ public: goto exit; } - fpCreate = (FPCoreCLRCreateCordbObject)GetProcAddress(hMod, "CoreCLRCreateCordbObject"); - if (fpCreate == NULL) - { - hr = CORDBG_E_INCOMPATIBLE_PROTOCOL; - goto exit; - } - - HRESULT hr = fpCreate(CorDebugVersion_2_0, m_processId, hModule, &pCordb); + HRESULT hr = CreateCoreDbg(hModule, hMod, m_processId, m_applicationGroupId, CorDebugVersion_2_0, &pCordb); _ASSERTE((pCordb == NULL) == FAILED(hr)); if (FAILED(hr)) { @@ -358,7 +418,7 @@ public: #else // FEATURE_PAL - HRESULT Register() + HRESULT Register(LPCWSTR lpApplicationGroupId) { HRESULT hr = GetStartupNotificationEvent(m_processId, &m_startupEvent); if (FAILED(hr)) @@ -623,7 +683,28 @@ StartupHelperThread(LPVOID p) //----------------------------------------------------------------------------- // Public API. // -// RegisterForRuntimeStartup -- executes the callback when the coreclr runtime +// RegisterForRuntimeStartup -- Refer to RegisterForRuntimeStartupEx. +// This method calls RegisterForRuntimeStartupEx with null application group ID value +// +// dwProcessId -- process id of the target process +// pfnCallback -- invoked when coreclr runtime starts +// parameter -- data to pass to callback +// ppUnregisterToken -- pointer to put the UnregisterForRuntimeStartup token. +// +//----------------------------------------------------------------------------- +HRESULT +RegisterForRuntimeStartup( + __in DWORD dwProcessId, + __in PSTARTUP_CALLBACK pfnCallback, + __in PVOID parameter, + __out PVOID *ppUnregisterToken) +{ + return RegisterForRuntimeStartupEx(dwProcessId, NULL, pfnCallback, parameter, ppUnregisterToken); +} +//----------------------------------------------------------------------------- +// Public API. +// +// RegisterForRuntimeStartupEx -- executes the callback when the coreclr runtime // starts in the specified process. The callback is passed the proper ICorDebug // instance for the version of the runtime or an error if something fails. This // API works for launch and attach (and even the attach scenario if the runtime @@ -643,14 +724,18 @@ StartupHelperThread(LPVOID p) // supported. // // dwProcessId -- process id of the target process +// lpApplicationGroupId - A string representing the application group ID of a sandboxed +// process running in Mac. Pass NULL if the process is not +// running in a sandbox and other platforms. // pfnCallback -- invoked when coreclr runtime starts // parameter -- data to pass to callback // ppUnregisterToken -- pointer to put the UnregisterForRuntimeStartup token. // //----------------------------------------------------------------------------- HRESULT -RegisterForRuntimeStartup( +RegisterForRuntimeStartupEx( __in DWORD dwProcessId, + __in LPCWSTR lpApplicationGroupId, __in PSTARTUP_CALLBACK pfnCallback, __in PVOID parameter, __out PVOID *ppUnregisterToken) @@ -671,7 +756,7 @@ RegisterForRuntimeStartup( } else { - hr = helper->Register(); + hr = helper->Register(lpApplicationGroupId); if (FAILED(hr)) { helper->Release(); @@ -1628,6 +1713,35 @@ CreateDebuggingInterfaceFromVersionEx( __in LPCWSTR szDebuggeeVersion, __out IUnknown ** ppCordb) { + return CreateDebuggingInterfaceFromVersion2(iDebuggerVersion, szDebuggeeVersion, NULL, ppCordb); +} + +//----------------------------------------------------------------------------- +// Public API. +// Given a version string, create the matching mscordbi.dll for it. +// Create a managed debugging interface for the specified version. +// +// Parameters: +// iDebuggerVersion - the version of interface the debugger (eg, Cordbg) expects. +// szDebuggeeVersion - the version of the debuggee. This will map to a version of mscordbi.dll +// lpApplicationGroupId - A string representing the application group ID of a sandboxed +// process running in Mac. Pass NULL if the process is not +// running in a sandbox and other platforms. +// ppCordb - the outparameter used to return the debugging interface object. +// +// Return: +// S_OK on success. *ppCordb will be non-null. +// CORDBG_E_INCOMPATIBLE_PROTOCOL - if the proper DBI is not available. This can be a very common error if +// the right debug pack is not installed. +// else Error. (*ppCordb will be null) +//----------------------------------------------------------------------------- +HRESULT +CreateDebuggingInterfaceFromVersion2( + __in int iDebuggerVersion, + __in LPCWSTR szDebuggeeVersion, + __in LPCWSTR szApplicationGroupId, + __out IUnknown ** ppCordb) +{ PUBLIC_CONTRACT; HRESULT hrIgnore = S_OK; // ignored HResult @@ -1707,17 +1821,8 @@ CreateDebuggingInterfaceFromVersionEx( // // Step 3: Now that module is loaded, instantiate an ICorDebug. - // - fpCreate2 = (FPCoreCLRCreateCordbObject)GetProcAddress(hMod, "CoreCLRCreateCordbObject"); - if (fpCreate2 == NULL) - { - // New-style creation API didn't exist - this DBI must be the wrong version, for the Mix07 protocol - hr = CORDBG_E_INCOMPATIBLE_PROTOCOL; - goto Exit; - } - - // Invoke to instantiate an ICorDebug. This export was introduced after the Mix'07 release. - hr = fpCreate2(iDebuggerVersion, pidDebuggee, hmodTargetCLR, &pCordb); + // + hr = CreateCoreDbg(hmodTargetCLR, hMod, pidDebuggee, szApplicationGroupId, iDebuggerVersion, &pCordb); _ASSERTE((pCordb == NULL) == FAILED(hr)); Exit: diff --git a/src/dlls/dbgshim/dbgshim.h b/src/dlls/dbgshim/dbgshim.h index 777c706ade..018058bbe0 100644 --- a/src/dlls/dbgshim/dbgshim.h +++ b/src/dlls/dbgshim/dbgshim.h @@ -35,6 +35,14 @@ RegisterForRuntimeStartup( __out PVOID *ppUnregisterToken); EXTERN_C HRESULT +RegisterForRuntimeStartupEx( + __in DWORD dwProcessId, + __in LPCWSTR szApplicationGroupId, + __in PSTARTUP_CALLBACK pfnCallback, + __in PVOID parameter, + __out PVOID *ppUnregisterToken); + +EXTERN_C HRESULT UnregisterForRuntimeStartup( __in PVOID pUnregisterToken); @@ -70,6 +78,13 @@ CreateDebuggingInterfaceFromVersionEx( __out IUnknown ** ppCordb); EXTERN_C HRESULT +CreateDebuggingInterfaceFromVersion2( + __in int iDebuggerVersion, + __in LPCWSTR szDebuggeeVersion, + __in LPCWSTR szApplicationGroupId, + __out IUnknown ** ppCordb); + +EXTERN_C HRESULT CreateDebuggingInterfaceFromVersion( __in LPCWSTR szDebuggeeVersion, __out IUnknown ** ppCordb); diff --git a/src/dlls/dbgshim/dbgshim.ntdef b/src/dlls/dbgshim/dbgshim.ntdef index 6915beebf9..1376cbcfe8 100644 --- a/src/dlls/dbgshim/dbgshim.ntdef +++ b/src/dlls/dbgshim/dbgshim.ntdef @@ -7,6 +7,7 @@ EXPORTS ResumeProcess CloseResumeHandle RegisterForRuntimeStartup + RegisterForRuntimeStartupEx UnregisterForRuntimeStartup GetStartupNotificationEvent EnumerateCLRs @@ -14,4 +15,5 @@ EXPORTS CreateVersionStringFromModule CreateDebuggingInterfaceFromVersion CreateDebuggingInterfaceFromVersionEx + CreateDebuggingInterfaceFromVersion2 CLRCreateInstance diff --git a/src/dlls/dbgshim/dbgshim_unixexports.src b/src/dlls/dbgshim/dbgshim_unixexports.src index b1cc36bc2f..013b739e6a 100644 --- a/src/dlls/dbgshim/dbgshim_unixexports.src +++ b/src/dlls/dbgshim/dbgshim_unixexports.src @@ -6,6 +6,7 @@ CreateProcessForLaunch ResumeProcess CloseResumeHandle RegisterForRuntimeStartup +RegisterForRuntimeStartupEx UnregisterForRuntimeStartup GetStartupNotificationEvent EnumerateCLRs @@ -13,4 +14,5 @@ CloseCLREnumeration CreateVersionStringFromModule CreateDebuggingInterfaceFromVersion CreateDebuggingInterfaceFromVersionEx +CreateDebuggingInterfaceFromVersion2 CLRCreateInstance diff --git a/src/dlls/mscordbi/mscordbi.src b/src/dlls/mscordbi/mscordbi.src index 0e14701243..9c3f928310 100644 --- a/src/dlls/mscordbi/mscordbi.src +++ b/src/dlls/mscordbi/mscordbi.src @@ -20,6 +20,7 @@ EXPORTS OpenVirtualProcess2 CoreCLRCreateCordbObject private + CoreCLRCreateCordbObjectEx private #if defined(FEATURE_DBGIPC_TRANSPORT_DI) DllGetClassObject private diff --git a/src/dlls/mscordbi/mscordbi_unixexports.src b/src/dlls/mscordbi/mscordbi_unixexports.src index 88fa9cdab1..aeb92368a0 100644 --- a/src/dlls/mscordbi/mscordbi_unixexports.src +++ b/src/dlls/mscordbi/mscordbi_unixexports.src @@ -8,6 +8,7 @@ DllGetClassObject ; CoreClr API CoreCLRCreateCordbObject +CoreCLRCreateCordbObjectEx ; Out-of-proc creation path from the shim - ICLRDebugging OpenVirtualProcessImpl diff --git a/src/pal/inc/pal.h b/src/pal/inc/pal.h index db2ce0d4fa..bb5e05f359 100644 --- a/src/pal/inc/pal.h +++ b/src/pal/inc/pal.h @@ -433,6 +433,7 @@ DWORD PALAPI PAL_RegisterForRuntimeStartup( IN DWORD dwProcessId, + IN LPCWSTR lpApplicationGroupId, IN PPAL_STARTUP_CALLBACK pfnCallback, IN PVOID parameter, OUT PVOID *ppUnregisterToken); @@ -453,7 +454,7 @@ PALIMPORT LPCSTR PALAPI PAL_GetApplicationGroupId(); -#endif // __APPLE__ +#endif static const int MAX_DEBUGGER_TRANSPORT_PIPE_NAME_LENGTH = MAX_PATH; @@ -463,6 +464,7 @@ PALAPI PAL_GetTransportPipeName( OUT char *name, IN DWORD id, + IN const char *applicationGroupId, IN const char *suffix); PALIMPORT diff --git a/src/pal/src/include/pal/stackstring.hpp b/src/pal/src/include/pal/stackstring.hpp index 89fcd009b2..133ec4a05e 100644 --- a/src/pal/src/include/pal/stackstring.hpp +++ b/src/pal/src/include/pal/stackstring.hpp @@ -109,7 +109,7 @@ private: public: StackString() - : m_buffer(m_innerBuffer), m_size(0), m_count(0) + : m_buffer(m_innerBuffer), m_size(STACKCOUNT+1), m_count(0) { } @@ -237,6 +237,7 @@ public: m_count = 0; NullTerminate(); } + ~StackString() { DeleteBuffer(); @@ -247,8 +248,8 @@ public: typedef StackString<32, CHAR> PathCharString; typedef StackString<32, WCHAR> PathWCharString; #else -typedef StackString<260, CHAR> PathCharString; -typedef StackString<260, WCHAR> PathWCharString; +typedef StackString<MAX_PATH, CHAR> PathCharString; +typedef StackString<MAX_PATH, WCHAR> PathWCharString; #endif #endif diff --git a/src/pal/src/thread/process.cpp b/src/pal/src/thread/process.cpp index 4358ca7cec..a964ee8829 100644 --- a/src/pal/src/thread/process.cpp +++ b/src/pal/src/thread/process.cpp @@ -81,6 +81,7 @@ SET_DEFAULT_DEBUG_CHANNEL(PROCESS); // some headers have code with asserts, so d #ifdef __APPLE__ #include <sys/sysctl.h> +#include <sys/posix_sem.h> #endif #ifdef __NetBSD__ @@ -195,10 +196,14 @@ PathCharString* gSharedFilesPath = nullptr; // MacOSX 10.11: 31 -- Core 1.0 RC2 compatibility #if defined(__NetBSD__) #define CLR_SEM_MAX_NAMELEN 15 +#elif defined(__APPLE__) +#define CLR_SEM_MAX_NAMELEN PSEMNAMLEN #else #define CLR_SEM_MAX_NAMELEN (NAME_MAX - 4) #endif +static_assert_no_msg(CLR_SEM_MAX_NAMELEN <= MAX_PATH); + // Function to call during PAL/process shutdown/abort Volatile<PSHUTDOWN_CALLBACK> g_shutdownCallback = nullptr; @@ -224,6 +229,31 @@ enum FILETYPE FILE_DIR /*Directory*/ }; +#pragma pack(push,1) +// When creating the semaphore name on Mac running in a sandbox, We reference this structure as a byte array +// in order to encode its data into a string. Its important to make sure there is no padding between the fields +// and also at the end of the buffer. Hence, this structure is defined inside a pack(1) +struct UnambiguousProcessDescriptor +{ + UnambiguousProcessDescriptor() + { + } + + UnambiguousProcessDescriptor(DWORD processId, UINT64 disambiguationKey) + { + Init(processId, disambiguationKey); + } + + void Init(DWORD processId, UINT64 disambiguationKey) + { + m_processId = processId; + m_disambiguationKey = disambiguationKey; + } + UINT64 m_disambiguationKey; + DWORD m_processId; +}; +#pragma pack(pop) + static DWORD PALAPI @@ -243,6 +273,14 @@ PROCGetProcessStatus( PROCESS_STATE *pps, DWORD *pdwExitCode); +static +void +CreateSemaphoreName( + char semName[CLR_SEM_MAX_NAMELEN], + LPCSTR semaphoreName, + const UnambiguousProcessDescriptor& unambiguousProcessDescriptor, + LPCSTR applicationGroupId); + static BOOL getFileName(LPCWSTR lpApplicationName, LPWSTR lpCommandLine, PathCharString& lpFileName); static char ** buildArgv(LPCWSTR lpCommandLine, PathCharString& lpAppPath, UINT *pnArg, BOOL prependLoader); static BOOL getPath(PathCharString& lpFileName, PathCharString& lpPathFileName); @@ -1520,13 +1558,14 @@ static bool IsCoreClrModule(const char* pModulePath) // NetBSD limits semaphore names to 15 characters, including null (at least up to 7.99.25). // Keep 31 length for Core 1.0 RC2 compatibility #if defined(__NetBSD__) -static const char* RuntimeStartupSemaphoreName = "/clrst%08llx"; -static const char* RuntimeContinueSemaphoreName = "/clrco%08llx"; +static const char* RuntimeSemaphoreNameFormat = "/clr%s%08llx"; #else -static const char* RuntimeStartupSemaphoreName = "/clrst%08x%016llx"; -static const char* RuntimeContinueSemaphoreName = "/clrco%08x%016llx"; +static const char* RuntimeSemaphoreNameFormat = "/clr%s%08x%016llx"; #endif +static const char* RuntimeStartupSemaphoreName = "st"; +static const char* RuntimeContinueSemaphoreName = "co"; + #if defined(__NetBSD__) static uint64_t HashSemaphoreName(uint64_t a, uint64_t b) { @@ -1547,6 +1586,11 @@ class PAL_RuntimeStartupHelper DWORD m_threadId; HANDLE m_threadHandle; DWORD m_processId; +#ifdef __APPLE__ + char m_applicationGroupId[MAX_APPLICATION_GROUP_ID_LENGTH+1]; +#endif // __APPLE__ + char m_startupSemName[CLR_SEM_MAX_NAMELEN]; + char m_continueSemName[CLR_SEM_MAX_NAMELEN]; // A value that, used in conjunction with the process ID, uniquely identifies a process. // See the format we use for debugger semaphore names for why this is necessary. @@ -1559,6 +1603,15 @@ class PAL_RuntimeStartupHelper // registered (m_callback) returns. sem_t *m_continueSem; + LPCSTR GetApplicationGroupId() const + { +#ifdef __APPLE__ + return m_applicationGroupId[0] == '\0' ? nullptr : m_applicationGroupId; +#else // __APPLE__ + return nullptr; +#endif // __APPLE__ + } + public: PAL_RuntimeStartupHelper(DWORD dwProcessId, PPAL_STARTUP_CALLBACK pfnCallback, PVOID parameter) : m_ref(1), @@ -1577,28 +1630,14 @@ public: { if (m_startupSem != SEM_FAILED) { - char startupSemName[CLR_SEM_MAX_NAMELEN]; - sprintf_s(startupSemName, - sizeof(startupSemName), - RuntimeStartupSemaphoreName, - HashSemaphoreName(m_processId, - m_processIdDisambiguationKey)); - sem_close(m_startupSem); - sem_unlink(startupSemName); + sem_unlink(m_startupSemName); } if (m_continueSem != SEM_FAILED) { - char continueSemName[CLR_SEM_MAX_NAMELEN]; - sprintf_s(continueSemName, - sizeof(continueSemName), - RuntimeContinueSemaphoreName, - HashSemaphoreName(m_processId, - m_processIdDisambiguationKey)); - sem_close(m_continueSem); - sem_unlink(continueSemName); + sem_unlink(m_continueSemName); } if (m_threadHandle != NULL) @@ -1654,39 +1693,54 @@ public: return pe; } - PAL_ERROR Register() + PAL_ERROR Register(LPCWSTR lpApplicationGroupId) { CPalThread *pThread = InternalGetCurrentThread(); - char startupSemName[CLR_SEM_MAX_NAMELEN]; - char continueSemName[CLR_SEM_MAX_NAMELEN]; PAL_ERROR pe = NO_ERROR; + BOOL ret; + UnambiguousProcessDescriptor unambiguousProcessDescriptor; + +#ifdef __APPLE__ + if (lpApplicationGroupId != NULL) + { + /* Convert to ASCII */ + int applicationGroupIdLength = WideCharToMultiByte(CP_ACP, 0, lpApplicationGroupId, -1, m_applicationGroupId, sizeof(m_applicationGroupId), NULL, NULL); + if (applicationGroupIdLength == 0) + { + pe = GetLastError(); + TRACE("applicationGroupId: Failed to convert to multibyte string (%u)\n", pe); + if (pe == ERROR_INSUFFICIENT_BUFFER) + { + pe = ERROR_BAD_LENGTH; + } + goto exit; + } + } + else + { + // Indicate that group ID is not being used + m_applicationGroupId[0] = '\0'; + } +#endif // __APPLE__ // See semaphore name format for details about this value. We store it so that // it can be used by the cleanup code that removes the semaphore with sem_unlink. - BOOL ret = GetProcessIdDisambiguationKey(m_processId, &m_processIdDisambiguationKey); + ret = GetProcessIdDisambiguationKey(m_processId, &m_processIdDisambiguationKey); // If GetProcessIdDisambiguationKey failed for some reason, it should set the value // to 0. We expect that anyone else opening the semaphore name will also fail and thus // will also try to use 0 as the value. _ASSERTE(ret == TRUE || m_processIdDisambiguationKey == 0); - sprintf_s(startupSemName, - sizeof(startupSemName), - RuntimeStartupSemaphoreName, - HashSemaphoreName(m_processId, - m_processIdDisambiguationKey)); - - sprintf_s(continueSemName, - sizeof(continueSemName), - RuntimeContinueSemaphoreName, - HashSemaphoreName(m_processId, - m_processIdDisambiguationKey)); + unambiguousProcessDescriptor.Init(m_processId, m_processIdDisambiguationKey); + CreateSemaphoreName(m_startupSemName, RuntimeStartupSemaphoreName, unambiguousProcessDescriptor, GetApplicationGroupId()); + CreateSemaphoreName(m_continueSemName, RuntimeContinueSemaphoreName, unambiguousProcessDescriptor, GetApplicationGroupId()); - TRACE("PAL_RuntimeStartupHelper.Register creating startup '%s' continue '%s'\n", startupSemName, continueSemName); + TRACE("PAL_RuntimeStartupHelper.Register creating startup '%s' continue '%s'\n", m_startupSemName, m_continueSemName); // Create the continue semaphore first so we don't race with PAL_NotifyRuntimeStarted. This open will fail if another // debugger is trying to attach to this process because the name will already exist. - m_continueSem = sem_open(continueSemName, O_CREAT | O_EXCL, S_IRWXU, 0); + m_continueSem = sem_open(m_continueSemName, O_CREAT | O_EXCL, S_IRWXU, 0); if (m_continueSem == SEM_FAILED) { TRACE("sem_open(continue) failed: errno is %d (%s)\n", errno, strerror(errno)); @@ -1695,7 +1749,7 @@ public: } // Create the debuggee startup semaphore so the runtime (debuggee) knows to wait for a debugger connection. - m_startupSem = sem_open(startupSemName, O_CREAT | O_EXCL, S_IRWXU, 0); + m_startupSem = sem_open(m_startupSemName, O_CREAT | O_EXCL, S_IRWXU, 0); if (m_startupSem == SEM_FAILED) { TRACE("sem_open(startup) failed: errno is %d (%s)\n", errno, strerror(errno)); @@ -1771,7 +1825,7 @@ public: { char pipeName[MAX_DEBUGGER_TRANSPORT_PIPE_NAME_LENGTH]; - PAL_GetTransportPipeName(pipeName, m_processId, "in"); + PAL_GetTransportPipeName(pipeName, m_processId, GetApplicationGroupId(), "in"); struct stat buf; if (stat(pipeName, &buf) == 0) @@ -1886,6 +1940,9 @@ StartupHelperThread(LPVOID p) Parameters: dwProcessId - process id of runtime process + lpApplicationGroupId - A string representing the application group ID of a sandboxed + process running in Mac. Pass NULL if the process is not + running in a sandbox and other platforms. pfnCallback - function to callback for coreclr module found parameter - data to pass to callback ppUnregisterToken - pointer to put PAL_UnregisterForRuntimeStartup token. @@ -1906,6 +1963,7 @@ DWORD PALAPI PAL_RegisterForRuntimeStartup( IN DWORD dwProcessId, + IN LPCWSTR lpApplicationGroupId, IN PPAL_STARTUP_CALLBACK pfnCallback, IN PVOID parameter, OUT PVOID *ppUnregisterToken) @@ -1917,7 +1975,7 @@ PAL_RegisterForRuntimeStartup( // Create the debuggee startup semaphore so the runtime (debuggee) knows to wait for // a debugger connection. - PAL_ERROR pe = helper->Register(); + PAL_ERROR pe = helper->Register(lpApplicationGroupId); if (NO_ERROR != pe) { helper->Release(); @@ -1985,8 +2043,15 @@ PAL_NotifyRuntimeStarted() // will also try to use 0 as the value. _ASSERTE(ret == TRUE || processIdDisambiguationKey == 0); - sprintf_s(startupSemName, sizeof(startupSemName), RuntimeStartupSemaphoreName, HashSemaphoreName(gPID, processIdDisambiguationKey)); - sprintf_s(continueSemName, sizeof(continueSemName), RuntimeContinueSemaphoreName, HashSemaphoreName(gPID, processIdDisambiguationKey)); + UnambiguousProcessDescriptor unambiguousProcessDescriptor(gPID, processIdDisambiguationKey); + LPCSTR applicationGroupId = +#ifdef __APPLE__ + PAL_GetApplicationGroupId(); +#else + nullptr; +#endif + CreateSemaphoreName(startupSemName, RuntimeStartupSemaphoreName, unambiguousProcessDescriptor, applicationGroupId); + CreateSemaphoreName(continueSemName, RuntimeContinueSemaphoreName, unambiguousProcessDescriptor, applicationGroupId); TRACE("PAL_NotifyRuntimeStarted opening continue '%s' startup '%s'\n", continueSemName, startupSemName); @@ -2041,7 +2106,78 @@ PAL_GetApplicationGroupId() { return gApplicationGroupId; } -#endif // __APPLE__ + +// We use 7bits from each byte, so this computes the extra size we need to encode a given byte count +constexpr int GetExtraEncodedAreaSize(UINT rawByteCount) +{ + return (rawByteCount+6)/7; +} +const int SEMAPHORE_ENCODED_NAME_EXTRA_LENGTH = GetExtraEncodedAreaSize(sizeof(UnambiguousProcessDescriptor)); +const int SEMAPHORE_ENCODED_NAME_LENGTH = + sizeof(UnambiguousProcessDescriptor) + /* For process ID + disambiguationKey */ + SEMAPHORE_ENCODED_NAME_EXTRA_LENGTH; /* For base 255 extra encoding space */ + +static_assert_no_msg(MAX_APPLICATION_GROUP_ID_LENGTH + + 1 /* For / */ + + 2 /* For ST/CO name prefix */ + + SEMAPHORE_ENCODED_NAME_LENGTH /* For encoded name string */ + + 1 /* For null terminator */ + <= CLR_SEM_MAX_NAMELEN); + +// In Apple we are limited by the length of the semaphore name. However, the characters which can be used in the +// name can be anything between 1 and 255 (since 0 will terminate the string). Thus, we encode each byte b in +// unambiguousProcessDescriptor as b ? b : 1, and mark an additional bit indicating if b is 0 or not. We use 7 bits +// out of each extra byte so 1 bit will always be '1'. This will ensure that our extra bytes are never 0 which are +// invalid characters. Thus we need an extra byte for each 7 input bytes. Hence, only extra 2 bytes for the name string. +void EncodeSemaphoreName(char *encodedSemName, const UnambiguousProcessDescriptor& unambiguousProcessDescriptor) +{ + const unsigned char *buffer = (const unsigned char *)&unambiguousProcessDescriptor; + char *extraEncodingBits = encodedSemName + sizeof(UnambiguousProcessDescriptor); + + // Reset the extra encoding bit area + for (int i=0; i<SEMAPHORE_ENCODED_NAME_EXTRA_LENGTH; i++) + { + extraEncodingBits[i] = 0x80; + } + + // Encode each byte in unambiguousProcessDescriptor + for (int i=0; i<sizeof(UnambiguousProcessDescriptor); i++) + { + unsigned char b = buffer[i]; + encodedSemName[i] = b ? b : 1; + extraEncodingBits[i/7] |= (b ? 0 : 1) << (i%7); + } +} +#endif + +void CreateSemaphoreName(char semName[CLR_SEM_MAX_NAMELEN], LPCSTR semaphoreName, const UnambiguousProcessDescriptor& unambiguousProcessDescriptor, LPCSTR applicationGroupId) +{ + int length = 0; + +#ifdef __APPLE__ + if (applicationGroupId != nullptr) + { + // We assume here that applicationGroupId has been already tested for length and is less than MAX_APPLICATION_GROUP_ID_LENGTH + length = sprintf_s(semName, CLR_SEM_MAX_NAMELEN, "%s/%s", applicationGroupId, semaphoreName); + _ASSERTE(length > 0 && length < CLR_SEM_MAX_NAMELEN); + + EncodeSemaphoreName(semName+length, unambiguousProcessDescriptor); + length += SEMAPHORE_ENCODED_NAME_LENGTH; + semName[length] = 0; + } + else +#endif // __APPLE__ + { + length = sprintf_s( + semName, + CLR_SEM_MAX_NAMELEN, + RuntimeSemaphoreNameFormat, + semaphoreName, + HashSemaphoreName(unambiguousProcessDescriptor.m_processId, unambiguousProcessDescriptor.m_disambiguationKey)); + } + + _ASSERTE(length > 0 && length < CLR_SEM_MAX_NAMELEN ); +} /*++ Function: @@ -2188,33 +2324,70 @@ PALAPI PAL_GetTransportPipeName( OUT char *name, IN DWORD id, + IN const char *applicationGroupId, IN const char *suffix) { *name = '\0'; DWORD dwRetVal = 0; UINT64 disambiguationKey = 0; - char formatBuffer[MAX_DEBUGGER_TRANSPORT_PIPE_NAME_LENGTH]; + PathCharString formatBufferString; BOOL ret = GetProcessIdDisambiguationKey(id, &disambiguationKey); + char *formatBuffer = formatBufferString.OpenStringBuffer(MAX_DEBUGGER_TRANSPORT_PIPE_NAME_LENGTH-1); + if (formatBuffer == nullptr) + { + ERROR("Out Of Memory"); + return; + } // If GetProcessIdDisambiguationKey failed for some reason, it should set the value // to 0. We expect that anyone else making the pipe name will also fail and thus will // also try to use 0 as the value. _ASSERTE(ret == TRUE || disambiguationKey == 0); - - // Get a temp file location - dwRetVal = ::GetTempPathA(MAX_DEBUGGER_TRANSPORT_PIPE_NAME_LENGTH, formatBuffer); - if (dwRetVal == 0) +#ifdef __APPLE__ + if (nullptr != applicationGroupId) { - ERROR("GetTempPath failed (0x%08x)", ::GetLastError()); - return; + // Verify the length of the application group ID + int applicationGroupIdLength = strlen(applicationGroupId); + if (applicationGroupIdLength > MAX_APPLICATION_GROUP_ID_LENGTH) + { + ERROR("The length of applicationGroupId is larger than MAX_APPLICATION_GROUP_ID_LENGTH"); + return; + } + + // In sandbox, all IPC files (locks, pipes) should be written to the application group + // container. The path returned by GetTempPathA will be unique for each process and cannot + // be used for IPC between two different processes + if (!GetApplicationContainerFolder(formatBufferString, applicationGroupId, applicationGroupIdLength)) + { + ERROR("Out Of Memory"); + return; + } + + // Verify the size of the path won't exceed maximum allowed size + if (formatBufferString.GetCount() >= MAX_DEBUGGER_TRANSPORT_PIPE_NAME_LENGTH) + { + ERROR("GetApplicationContainerFolder returned a path that was larger than MAX_DEBUGGER_TRANSPORT_PIPE_NAME_LENGTH"); + return; + } } - if (dwRetVal > MAX_DEBUGGER_TRANSPORT_PIPE_NAME_LENGTH) + else +#endif // __APPLE__ { - ERROR("GetTempPath returned a path that was larger than MAX_DEBUGGER_TRANSPORT_PIPE_NAME_LENGTH"); - return; + // Get a temp file location + dwRetVal = ::GetTempPathA(MAX_DEBUGGER_TRANSPORT_PIPE_NAME_LENGTH, formatBuffer); + if (dwRetVal == 0) + { + ERROR("GetTempPath failed (0x%08x)", ::GetLastError()); + return; + } + if (dwRetVal > MAX_DEBUGGER_TRANSPORT_PIPE_NAME_LENGTH) + { + ERROR("GetTempPath returned a path that was larger than MAX_DEBUGGER_TRANSPORT_PIPE_NAME_LENGTH"); + return; + } } - if (strncat_s(formatBuffer, _countof(formatBuffer), PipeNameFormat, strlen(PipeNameFormat)) == STRUNCATE) + if (strncat_s(formatBuffer, MAX_DEBUGGER_TRANSPORT_PIPE_NAME_LENGTH, PipeNameFormat, strlen(PipeNameFormat)) == STRUNCATE) { ERROR("TransportPipeName was larger than MAX_DEBUGGER_TRANSPORT_PIPE_NAME_LENGTH"); return; |