From dac4a490f66ef501fb8d7b246ed3c21a8bcfa485 Mon Sep 17 00:00:00 2001 From: Mike McLaughlin Date: Tue, 15 Dec 2015 16:42:20 -0800 Subject: [PATCH] Add debugger launch to dbgshim for xplat. Add the RegisterForRuntimeStartup/UnregisterForRuntimeStartup to dbghim. 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 hasn't been loaded yet) equally on both xplat and Windows. The callback is always called on a separate thread. This API returns immediately. The callback is invoke when the coreclr runtime module is loaded during early initialization. The runtime is blocked during initialization until the callback returns. HRESULT RegisterForRuntimeStartup( __in DWORD dwProcessId, __in PSTARTUP_CALLBACK pfnCallback, __in PVOID parameter, __out PVOID *ppUnregisterToken) HRESULT UnregisterForRuntimeStartup( __in PVOID pUnregisterToken) Most of the work is done for xplat in the PAL_RegisterForRuntimeStartup and PAL_UnregisterForRuntimeStartup. On Windows, the APIs are implemented on top of the old dbgshim ones. Added reference counting to DbgTransportSession so the cleanup can be done after the transport worker and the main code is finished. Fix a hang in OSX initializing multiple PALs in the debugging test dbg, dbgshim and mscordaccore by not calling FILEInitStdHandles() from PAL_InitializeDLL. Fixed a minor EnumerateCLRs bug in an error path. A ThrowHR instead of returning the HRESULT. Better pipe file/dbg transport cleanup. Now also call the dbg transport connection abort for an unhandled native exception. Added PROCAbort to replace most calls to abort(). The shutdown handler is called in PROCAbort(). Cleanup debugger transport pipes on CTRL-C termination. Cleanup process code; remove now useless CProcSharedData. Added "PROCESS" PAL trace type. --- src/debug/di/dbgtransportmanager.cpp | 3 +- src/debug/ee/debugger.cpp | 282 ++++---- src/debug/ee/debugger.h | 2 + src/debug/inc/dbgtransportsession.h | 29 +- src/debug/inc/debug-pal.h | 16 - src/debug/shared/dbgtransportsession.cpp | 69 +- src/dlls/dbgshim/dbgshim.cpp | 695 +++++++++++++++++--- src/dlls/dbgshim/dbgshim.h | 72 ++- src/dlls/dbgshim/dbgshim.ntdef | 2 + src/inc/dacvars.h | 2 + src/pal/inc/pal.h | 47 +- src/pal/inc/rt/palrt.h | 2 + src/pal/src/debug/debug.cpp | 8 +- src/pal/src/exception/machexception.cpp | 24 - src/pal/src/exception/signal.cpp | 68 +- src/pal/src/file/file.cpp | 25 +- src/pal/src/include/pal/dbgmsg.h | 2 + src/pal/src/include/pal/init.h | 9 - src/pal/src/include/pal/process.h | 22 +- src/pal/src/include/pal/procobj.hpp | 10 +- src/pal/src/init/pal.cpp | 34 +- src/pal/src/init/sxs.cpp | 3 +- src/pal/src/misc/dbgmsg.cpp | 1 + src/pal/src/misc/miscpalapi.cpp | 3 +- src/pal/src/thread/process.cpp | 1029 ++++++++++++++++++++---------- 25 files changed, 1734 insertions(+), 725 deletions(-) delete mode 100644 src/debug/inc/debug-pal.h diff --git a/src/debug/di/dbgtransportmanager.cpp b/src/debug/di/dbgtransportmanager.cpp index e9b619d..c5f60f3 100644 --- a/src/debug/di/dbgtransportmanager.cpp +++ b/src/debug/di/dbgtransportmanager.cpp @@ -74,6 +74,7 @@ HRESULT DbgTransportTarget::GetTransportForProcess(DWORD dwPID HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPID); if (hProcess == NULL) { + transport->Shutdown(); return HRESULT_FROM_GetLastError(); } @@ -160,7 +161,6 @@ void DbgTransportTarget::ReleaseTransport(DbgTransportSession *pTransport) _ASSERTE(!"Trying to release transport that doesn't belong to this DbgTransportTarget"); pTransport->Shutdown(); - delete pTransport; } HRESULT DbgTransportTarget::CreateProcess(LPCWSTR lpApplicationName, @@ -211,7 +211,6 @@ DbgTransportTarget::ProcessEntry::~ProcessEntry() m_hProcess = NULL; m_transport->Shutdown(); - delete m_transport; m_transport = NULL; } diff --git a/src/debug/ee/debugger.cpp b/src/debug/ee/debugger.cpp index 12a7473..6279ed0 100644 --- a/src/debug/ee/debugger.cpp +++ b/src/debug/ee/debugger.cpp @@ -904,7 +904,6 @@ HRESULT ValidateObject(Object *objPtr) #ifdef FEATURE_DBGIPC_TRANSPORT_VM -__attribute__((destructor)) void ShutdownTransport() { @@ -914,6 +913,15 @@ ShutdownTransport() g_pDbgTransport = NULL; } } + +void +AbortTransport() +{ + if (g_pDbgTransport != NULL) + { + g_pDbgTransport->AbortConnection(); + } +} #endif // FEATURE_DBGIPC_TRANSPORT_VM @@ -1742,21 +1750,16 @@ void Debugger::RaiseStartupNotification() // that it's flushed from any CPU cache into memory. InterlockedIncrement(&m_fLeftSideInitialized); +#ifndef FEATURE_DBGIPC_TRANSPORT_VM // If we are remote debugging, don't send the event now if a debugger is not attached. No one will be // listening, and we will fail. However, we still want to initialize the variable above. - BOOL fRaiseStartupNotification = TRUE; -#if defined(FEATURE_DBGIPC_TRANSPORT_VM) - fRaiseStartupNotification = (CORDebuggerAttached() ? TRUE : FALSE); -#endif - if (fRaiseStartupNotification) - { - DebuggerIPCEvent startupEvent; - InitIPCEvent(&startupEvent, DB_IPCE_LEFTSIDE_STARTUP, NULL, VMPTR_AppDomain::NullPtr()); - - SendRawEvent(&startupEvent); + DebuggerIPCEvent startupEvent; + InitIPCEvent(&startupEvent, DB_IPCE_LEFTSIDE_STARTUP, NULL, VMPTR_AppDomain::NullPtr()); + + SendRawEvent(&startupEvent); - // RS will set flags from OOP while we're stopped at the event if it wants to attach. - } + // RS will set flags from OOP while we're stopped at the event if it wants to attach. +#endif // FEATURE_DBGIPC_TRANSPORT_VM } @@ -1884,7 +1887,8 @@ void Debugger::SendCreateProcess(DebuggerLockHolder * pDbgLockHolder) pDbgLockHolder->Acquire(); } -#ifdef FEATURE_CORECLR +#if defined(FEATURE_CORECLR) && !defined(FEATURE_PAL) + HANDLE g_hContinueStartupEvent = NULL; CLR_ENGINE_METRICS g_CLREngineMetrics = { @@ -1895,9 +1899,6 @@ CLR_ENGINE_METRICS g_CLREngineMetrics = { bool IsTelestoDebugPackInstalled() { -#ifdef FEATURE_PAL - return false; -#else RegKeyHolder hKey; if (ERROR_SUCCESS != WszRegOpenKeyEx(HKEY_LOCAL_MACHINE, FRAMEWORK_REGISTRY_KEY_W, 0, KEY_READ, &hKey)) return false; @@ -1916,10 +1917,8 @@ bool IsTelestoDebugPackInstalled() // RegCloseKey called by holder return debugPackInstalled; -#endif // FEATURE_PAL } - #define StartupNotifyEventNamePrefix W("TelestoStartupEvent_") const int cchEventNameBufferSize = sizeof(StartupNotifyEventNamePrefix)/sizeof(WCHAR) + 8; // + hex DWORD (8). NULL terminator is included in sizeof(StartupNotifyEventNamePrefix) HANDLE OpenStartupNotificationEvent() @@ -1957,7 +1956,8 @@ void NotifyDebuggerOfTelestoStartup() CloseHandle(g_hContinueStartupEvent); g_hContinueStartupEvent = NULL; } -#endif // FEATURE_CORECLR + +#endif // FEATURE_CORECLR && !FEATURE_PAL //--------------------------------------------------------------------------------------- // @@ -1996,10 +1996,8 @@ HRESULT Debugger::Startup(void) // Iff the debug pack is installed, then go through the telesto debugging pipeline. LOG((LF_CORDB, LL_INFO10, "Debugging service is enabled because debug pack is installed or Watson support is enabled)\n")); -#if !defined(FEATURE_DBGIPC_TRANSPORT_VM) // This may block while an attach occurs. NotifyDebuggerOfTelestoStartup(); -#endif } else { @@ -2013,164 +2011,162 @@ HRESULT Debugger::Startup(void) } #endif // FEATURE_CORECLR && !FEATURE_PAL - DebuggerLockHolder dbgLockHolder(this); + { + DebuggerLockHolder dbgLockHolder(this); - // Stubs in Stacktraces are always enabled. - g_EnableSIS = true; + // Stubs in Stacktraces are always enabled. + g_EnableSIS = true; - // We can get extra Interop-debugging test coverage by having some auxillary unmanaged - // threads running and throwing debug events. Keep these stress procs separate so that - // we can focus on certain problem areas. -#ifdef _DEBUG + // We can get extra Interop-debugging test coverage by having some auxillary unmanaged + // threads running and throwing debug events. Keep these stress procs separate so that + // we can focus on certain problem areas. + #ifdef _DEBUG - g_DbgShouldntUseDebugger = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_DbgNoDebugger) != 0; + g_DbgShouldntUseDebugger = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_DbgNoDebugger) != 0; - // Creates random thread procs. - DWORD dwRegVal = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_DbgExtraThreads); - DWORD dwId; - DWORD i; + // Creates random thread procs. + DWORD dwRegVal = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_DbgExtraThreads); + DWORD dwId; + DWORD i; - if (dwRegVal > 0) - { - for(i = 0; i < dwRegVal; i++) + if (dwRegVal > 0) { - int iProc = GetRandomInt(NumItems(g_pStressProcs)); - LPTHREAD_START_ROUTINE pStartRoutine = g_pStressProcs[iProc]; - ::CreateThread(NULL, 0, pStartRoutine, NULL, 0, &dwId); - LOG((LF_CORDB, LL_INFO1000, "Created random thread (%d) with tid=0x%x\n", i, dwId)); + for (i = 0; i < dwRegVal; i++) + { + int iProc = GetRandomInt(NumItems(g_pStressProcs)); + LPTHREAD_START_ROUTINE pStartRoutine = g_pStressProcs[iProc]; + ::CreateThread(NULL, 0, pStartRoutine, NULL, 0, &dwId); + LOG((LF_CORDB, LL_INFO1000, "Created random thread (%d) with tid=0x%x\n", i, dwId)); + } } - } - dwRegVal = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_DbgExtraThreadsIB); - if (dwRegVal > 0) - { - for(i = 0; i < dwRegVal; i++) + dwRegVal = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_DbgExtraThreadsIB); + if (dwRegVal > 0) { - ::CreateThread(NULL, 0, DbgInteropStressProc, NULL, 0, &dwId); - LOG((LF_CORDB, LL_INFO1000, "Created extra thread (%d) with tid=0x%x\n", i, dwId)); + for (i = 0; i < dwRegVal; i++) + { + ::CreateThread(NULL, 0, DbgInteropStressProc, NULL, 0, &dwId); + LOG((LF_CORDB, LL_INFO1000, "Created extra thread (%d) with tid=0x%x\n", i, dwId)); + } } - } - dwRegVal = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_DbgExtraThreadsCantStop); - if (dwRegVal > 0) - { - for(i = 0; i < dwRegVal; i++) + dwRegVal = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_DbgExtraThreadsCantStop); + if (dwRegVal > 0) { - ::CreateThread(NULL, 0, DbgInteropCantStopStressProc, NULL, 0, &dwId); - LOG((LF_CORDB, LL_INFO1000, "Created extra thread 'can't-stop' (%d) with tid=0x%x\n", i, dwId)); + for (i = 0; i < dwRegVal; i++) + { + ::CreateThread(NULL, 0, DbgInteropCantStopStressProc, NULL, 0, &dwId); + LOG((LF_CORDB, LL_INFO1000, "Created extra thread 'can't-stop' (%d) with tid=0x%x\n", i, dwId)); + } } - } - dwRegVal = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_DbgExtraThreadsOOB); - if (dwRegVal > 0) - { - for(i = 0; i < dwRegVal; i++) + dwRegVal = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_DbgExtraThreadsOOB); + if (dwRegVal > 0) { - ::CreateThread(NULL, 0, DbgInteropOOBStressProc, NULL, 0, &dwId); - LOG((LF_CORDB, LL_INFO1000, "Created extra thread OOB (%d) with tid=0x%x\n", i, dwId)); + for (i = 0; i < dwRegVal; i++) + { + ::CreateThread(NULL, 0, DbgInteropOOBStressProc, NULL, 0, &dwId); + LOG((LF_CORDB, LL_INFO1000, "Created extra thread OOB (%d) with tid=0x%x\n", i, dwId)); + } } - } -#endif + #endif -#ifdef FEATURE_PAL - PAL_InitializeDebug(); -#endif // FEATURE_PAL + #ifdef FEATURE_PAL + PAL_InitializeDebug(); + #endif // FEATURE_PAL - // Lazily initialize the interop-safe heap + // Lazily initialize the interop-safe heap - // Must be done before the RC thread is initialized. - // @dbgtodo - In V2, LS was lazily initialized; but was eagerly pre-initialized if launched by debugger. - // (This was for perf reasons). But we don't want Launch vs. Attach checks in the LS, so we now always - // init. As we move more to OOP, this init will become cheaper. - { - LazyInit(); - DebuggerController::Initialize(); - } + // Must be done before the RC thread is initialized. + // @dbgtodo - In V2, LS was lazily initialized; but was eagerly pre-initialized if launched by debugger. + // (This was for perf reasons). But we don't want Launch vs. Attach checks in the LS, so we now always + // init. As we move more to OOP, this init will become cheaper. + { + LazyInit(); + DebuggerController::Initialize(); + } - InitializeHijackFunctionAddress(); + InitializeHijackFunctionAddress(); - // Create the runtime controller thread, a.k.a, the debug helper thread. - // Don't use the interop-safe heap b/c we don't want to lazily create it. - m_pRCThread = new DebuggerRCThread(this); - - _ASSERTE(m_pRCThread != NULL); // throws on oom - TRACE_ALLOC(m_pRCThread); + // Create the runtime controller thread, a.k.a, the debug helper thread. + // Don't use the interop-safe heap b/c we don't want to lazily create it. + m_pRCThread = new DebuggerRCThread(this); + _ASSERTE(m_pRCThread != NULL); // throws on oom + TRACE_ALLOC(m_pRCThread); - hr = m_pRCThread->Init(); - - _ASSERTE(SUCCEEDED(hr)); // throws on error + hr = m_pRCThread->Init(); + _ASSERTE(SUCCEEDED(hr)); // throws on error -#if defined(FEATURE_DBGIPC_TRANSPORT_VM) - // Create transport session and initialize it. - g_pDbgTransport = new DbgTransportSession(); - hr = g_pDbgTransport->Init(m_pRCThread->GetDCB(), m_pAppDomainCB); - if (FAILED(hr)) - ThrowHR(hr); + #if defined(FEATURE_DBGIPC_TRANSPORT_VM) + // Create transport session and initialize it. + g_pDbgTransport = new DbgTransportSession(); + hr = g_pDbgTransport->Init(m_pRCThread->GetDCB(), m_pAppDomainCB); + if (FAILED(hr)) + { + ShutdownTransport(); + ThrowHR(hr); + } -#ifdef FEATURE_PAL - PAL_SetShutdownCallback(ShutdownTransport); -#endif // FEATURE_PAL + #ifdef FEATURE_PAL + PAL_SetShutdownCallback(AbortTransport); + #endif // FEATURE_PAL + #endif // FEATURE_DBGIPC_TRANSPORT_VM - bool waitForAttach = CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_DbgWaitForDebuggerAttach) != 0; - if (waitForAttach) - { - // Mark this process as launched by the debugger and the debugger as attached. - g_CORDebuggerControlFlags |= DBCF_GENERATE_DEBUG_CODE; - MarkDebuggerAttachedInternal(); + RaiseStartupNotification(); - LazyInit(); - DebuggerController::Initialize(); - } -#endif // FEATURE_DBGIPC_TRANSPORT_VM + // Also initialize the AppDomainEnumerationIPCBlock + #if !defined(FEATURE_IPCMAN) || defined(FEATURE_DBGIPC_TRANSPORT_VM) + m_pAppDomainCB = new (nothrow) AppDomainEnumerationIPCBlock(); + #else + m_pAppDomainCB = g_pIPCManagerInterface->GetAppDomainBlock(); + #endif - RaiseStartupNotification(); - - // Also initialize the AppDomainEnumerationIPCBlock -#if !defined(FEATURE_IPCMAN) || defined(FEATURE_DBGIPC_TRANSPORT_VM) - m_pAppDomainCB = new (nothrow) AppDomainEnumerationIPCBlock(); -#else - m_pAppDomainCB = g_pIPCManagerInterface->GetAppDomainBlock(); -#endif + if (m_pAppDomainCB == NULL) + { + LOG((LF_CORDB, LL_INFO100, "D::S: Failed to get AppDomain IPC block from IPCManager.\n")); + ThrowHR(E_FAIL); + } - if (m_pAppDomainCB == NULL) - { - LOG((LF_CORDB, LL_INFO100, "D::S: Failed to get AppDomain IPC block from IPCManager.\n")); - ThrowHR(E_FAIL); - } + hr = InitAppDomainIPC(); + _ASSERTE(SUCCEEDED(hr)); // throws on error. - hr = InitAppDomainIPC(); - _ASSERTE(SUCCEEDED(hr)); // throws on error. + // See if we need to spin up the helper thread now, rather than later. + DebuggerIPCControlBlock* pIPCControlBlock = m_pRCThread->GetDCB(); + (void)pIPCControlBlock; //prevent "unused variable" error from GCC - // See if we need to spin up the helper thread now, rather than later. - DebuggerIPCControlBlock* pIPCControlBlock = m_pRCThread->GetDCB(); - (void)pIPCControlBlock; //prevent "unused variable" error from GCC + _ASSERTE(pIPCControlBlock != NULL); + _ASSERTE(!pIPCControlBlock->m_rightSideShouldCreateHelperThread); + { + // Create the win32 thread for the helper and let it run free. + hr = m_pRCThread->Start(); - _ASSERTE(pIPCControlBlock != NULL); + // convert failure to exception as with old contract + if (FAILED(hr)) + { + ThrowHR(hr); + } - _ASSERTE(!pIPCControlBlock->m_rightSideShouldCreateHelperThread); - { - // Create the win32 thread for the helper and let it run free. - hr = m_pRCThread->Start(); + LOG((LF_CORDB, LL_EVERYTHING, "Start was successful\n")); + } - // convert failure to exception as with old contract - if (FAILED(hr)) + #ifdef TEST_DATA_CONSISTENCY + // if we have set the environment variable TestDataConsistency, run the data consistency test. + // See code:DataTest::TestDataSafety for more information + if ((g_pConfig != NULL) && (g_pConfig->TestDataConsistency() == true)) { - ThrowHR(hr); + DataTest dt; + dt.TestDataSafety(); } - - LOG((LF_CORDB, LL_EVERYTHING, "Start was successful\n")); + #endif } -#ifdef TEST_DATA_CONSISTENCY - // if we have set the environment variable TestDataConsistency, run the data consistency test. - // See code:DataTest::TestDataSafety for more information - if((g_pConfig != NULL) && (g_pConfig->TestDataConsistency() == true)) - { - DataTest dt; - dt.TestDataSafety(); - } -#endif +#ifdef FEATURE_PAL + // Signal the debugger (via dbgshim) and wait until it is ready for us to + // continue. This needs to be outside the lock and after the transport is + // initialized. + PAL_NotifyRuntimeStarted(); +#endif // FEATURE_PAL // We don't bother changing this process's permission. // A managed debugger will have the SE_DEBUG permission which will allow it to open our process handle, @@ -2584,11 +2580,9 @@ void Debugger::StopDebugger(void) m_pRCThread->AsyncStop(); } - // Also clean up the AppDomain stuff since this is cross-process. TerminateAppDomainIPC (); - // // Tell the VM to clear out all references to the debugger before we start cleaning up, // so that nothing will reference (accidentally) through the partially cleaned up debugger. diff --git a/src/debug/ee/debugger.h b/src/debug/ee/debugger.h index 5e4ff53..d14c83c 100644 --- a/src/debug/ee/debugger.h +++ b/src/debug/ee/debugger.h @@ -111,7 +111,9 @@ typedef DPTR(struct DebuggerIPCControlBlock) PTR_DebuggerIPCControlBlock; GPTR_DECL(Debugger, g_pDebugger); GPTR_DECL(EEDebugInterface, g_pEEInterface); +#ifndef FEATURE_PAL GVAL_DECL(HANDLE, g_hContinueStartupEvent); +#endif extern DebuggerRCThread *g_pRCThread; //--------------------------------------------------------------------------------------- diff --git a/src/debug/inc/dbgtransportsession.h b/src/debug/inc/dbgtransportsession.h index 3b38b87..0c0b37e 100644 --- a/src/debug/inc/dbgtransportsession.h +++ b/src/debug/inc/dbgtransportsession.h @@ -306,10 +306,12 @@ enum IPCEventType class DbgTransportSession { public: - // No real work done in the constructor. Use Init() instead. DbgTransportSession(); + // Cleanup what is allocated/created in Init() + ~DbgTransportSession(); + // Allocates initial resources (including starting the transport thread). The session will start in the // SS_Opening state. That is, the RS will immediately start trying to Connect() a connection while the // LS will perform an Accept() to wait for a connection request. The RS needs an IP address and port @@ -330,6 +332,28 @@ public: // Init() may be called again to start over from the beginning). void Shutdown(); + // Cleans up the named pipe connection so no tmp files are left behind. Does only + // the minimum and must be safe to call at any time. Called during PAL ExitProcess, + // TerminateProcess and for unhandled native exceptions and asserts. + void AbortConnection(); + + LONG AddRef() + { + LONG ref = InterlockedIncrement(&m_ref); + return ref; + } + + LONG Release() + { + _ASSERTE(m_ref > 0); + LONG ref = InterlockedDecrement(&m_ref); + if (ref == 0) + { + delete this; + } + return ref; + } + #ifndef RIGHT_SIDE_COMPILE // API used only by the LS to drive the transport into a state where it won't accept connections. This is // used when no proxy is detected at startup but it's too late to shutdown all of the debugging system @@ -614,6 +638,9 @@ private: #endif // _DEBUG + // Reference count + LONG m_ref; + // Some flags used to record how far we got in Init() (used for cleanup in Shutdown()). bool m_fInitStateLock; #ifndef RIGHT_SIDE_COMPILE diff --git a/src/debug/inc/debug-pal.h b/src/debug/inc/debug-pal.h deleted file mode 100644 index 90cf0c8..0000000 --- a/src/debug/inc/debug-pal.h +++ /dev/null @@ -1,16 +0,0 @@ -// -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. -// - - -#ifndef Debug_PAL_H -#define Debug_PAL_H - -#if defined(FEATURE_PAL) -// This function looks for a dynamic module (libraryName) loaded into the process specified (pId) -// and returns its load address. NULL is module is not loaded. -void *GetDynamicLibraryAddressInProcess(DWORD pid, const char *libraryName); -#endif - -#endif //Debug_PAL_H \ No newline at end of file diff --git a/src/debug/shared/dbgtransportsession.cpp b/src/debug/shared/dbgtransportsession.cpp index 01f4acb..5aa9f28 100644 --- a/src/debug/shared/dbgtransportsession.cpp +++ b/src/debug/shared/dbgtransportsession.cpp @@ -36,9 +36,37 @@ DbgTransportSession *g_pDbgTransport = NULL; // No real work done in the constructor. Use Init() instead. DbgTransportSession::DbgTransportSession() { + m_ref = 1; m_eState = SS_Closed; } +DbgTransportSession::~DbgTransportSession() +{ + DbgTransportLog(LC_Proxy, "DbgTransportSession::~DbgTransportSession() called"); + + // No other threads are now using session resources. We're free to deallocate them as we wish (if they + // were allocated in the first place). + if (m_hTransportThread) + CloseHandle(m_hTransportThread); + if (m_rghEventReadyEvent[IPCET_OldStyle]) + CloseHandle(m_rghEventReadyEvent[IPCET_OldStyle]); + if (m_rghEventReadyEvent[IPCET_DebugEvent]) + CloseHandle(m_rghEventReadyEvent[IPCET_DebugEvent]); + if (m_pEventBuffers) + delete [] m_pEventBuffers; + +#ifdef RIGHT_SIDE_COMPILE + if (m_hSessionOpenEvent) + CloseHandle(m_hSessionOpenEvent); + + if (m_hProcessExited) + CloseHandle(m_hProcessExited); +#endif // RIGHT_SIDE_COMPILE + + if (m_fInitStateLock) + m_sStateLock.Destroy(); +} + // Allocates initial resources (including starting the transport thread). The session will start in the // SS_Opening state. That is, the RS will immediately start trying to Connect() a connection while the LS will // perform an accept()/Accept() to wait for a connection request. The RS needs an IP address and port number @@ -59,6 +87,7 @@ HRESULT DbgTransportSession::Init(DebuggerIPCControlBlock *pDCB, AppDomainEnumer // Because of the above memset the embeded classes/structs need to be reinitialized especially // the two way pipe; it expects the in/out handles to be -1 instead of 0. + m_ref = 1; m_pipe = TwoWayPipe(); m_sStateLock = DbgTransportLock(); @@ -124,9 +153,13 @@ HRESULT DbgTransportSession::Init(DebuggerIPCControlBlock *pDCB, AppDomainEnumer // Start the transport thread which handles forming and re-forming connections, driving the session // state to SS_Open and receiving and initially processing all incoming traffic. + AddRef(); m_hTransportThread = CreateThread(NULL, 0, TransportWorkerStatic, this, 0, NULL); if (m_hTransportThread == NULL) + { + Release(); return E_OUTOFMEMORY; + } return S_OK; } @@ -178,30 +211,16 @@ void DbgTransportSession::Shutdown() #endif // RIGHT_SIDE_COMPILE } - // No other threads are now using session resources. We're free to deallocate them as we wish (if they - // were allocated in the first place). - if (m_hTransportThread) - CloseHandle(m_hTransportThread); - if (m_rghEventReadyEvent[IPCET_OldStyle]) - CloseHandle(m_rghEventReadyEvent[IPCET_OldStyle]); - if (m_rghEventReadyEvent[IPCET_DebugEvent]) - CloseHandle(m_rghEventReadyEvent[IPCET_DebugEvent]); - if (m_pEventBuffers) - delete [] m_pEventBuffers; - -#ifdef RIGHT_SIDE_COMPILE - if (m_hSessionOpenEvent) - CloseHandle(m_hSessionOpenEvent); - - if (m_hProcessExited) - { - CloseHandle(m_hProcessExited); - } -#endif // RIGHT_SIDE_COMPILE - - if (m_fInitStateLock) - m_sStateLock.Destroy(); + // The transport instance is no longer valid + Release(); +} +// Cleans up the named pipe connection so no tmp files are left behind. Does only +// the minimum and must be safe to call at any time. Called during PAL ExitProcess, +// TerminateProcess and for unhandled native exceptions and asserts. +void DbgTransportSession::AbortConnection() +{ + m_pipe.Disconnect(); } #ifndef RIGHT_SIDE_COMPILE @@ -2125,6 +2144,10 @@ void DbgTransportSession::TransportWorker() } } } // Leave m_sStateLock + + // Now release all the resources allocated for the transport now that the + // worker thread isn't using them anymore. + Release(); } // Given a fully initialized debugger event structure, return the size of the structure in bytes (this is not diff --git a/src/dlls/dbgshim/dbgshim.cpp b/src/dlls/dbgshim/dbgshim.cpp index 42a2d25..eeccfe8 100644 --- a/src/dlls/dbgshim/dbgshim.cpp +++ b/src/dlls/dbgshim/dbgshim.cpp @@ -26,9 +26,7 @@ #include #include -#if defined(FEATURE_PAL) -#include "debug-pal.h" -#else +#ifndef FEATURE_PAL #define PSAPI_VERSION 2 #include #endif @@ -40,27 +38,23 @@ // Here's a High-level overview of the API usage From the debugger: -A debugger calls GetStartupNotificationEvent(pid of debuggee) to get an event, which is signalled when that -process loads a Telesto. The debugger thus waits on that event, and when it's signalled, it can call +A debugger calls GetStartupNotificationEvent(pid of debuggee) to get an event, which is signalled when +that process loads a Telesto. The debugger thus waits on that event, and when it's signalled, it can call EnumerateCLRs / CloseCLREnumeration to get an array of Telestos in the target process (including the one -that was just loaded). -It can then call CreateVersionStringFromModule, CreateDebuggingInterfaceFromVersion to attach to -any or all Telestos of interest. - +that was just loaded). It can then call CreateVersionStringFromModule, CreateDebuggingInterfaceFromVersion +to attach to any or all Telestos of interest. From the debuggee: -When a new Telesto spins up, it checks for the startup event (created via GetStartupNotificationEvent), and if it -exists, it will: +When a new Telesto spins up, it checks for the startup event (created via GetStartupNotificationEvent), and +if it exists, it will: - signal it - wait on the "Continue" event, thus giving a debugger a chance to attach to the telesto - Notes: - There is no CreateProcess (Launch) case. All Launching is really an "Early-attach case". */ - // Contract for public APIs. These must be NOTHROW. #define PUBLIC_CONTRACT \ CONTRACTL \ @@ -69,6 +63,504 @@ Notes: } \ CONTRACTL_END; \ +#ifdef FEATURE_PAL + +static +void +RuntimeStartupHandler( + char *pszModulePath, + HMODULE hModule, + PVOID parameter); + +#else // FEATURE_PAL + +static +DWORD +StartupHelperThread( + LPVOID p); + +static +HRESULT +GetContinueStartupEvent( + DWORD debuggeePID, + LPCWSTR szTelestoFullPath, + __out HANDLE *phContinueStartupEvent); + +#endif // FEATURE_PAL + +// Functions that we'll look for in the loaded Mscordbi module. +typedef HRESULT (STDAPICALLTYPE *FPCoreCLRCreateCordbObject)( + int iDebuggerVersion, + DWORD pid, + HMODULE hmodTargetCLR, + IUnknown **ppCordb); + +// +// Helper class for RegisterForRuntimeStartup +// +class RuntimeStartupHelper +{ + LONG m_ref; + DWORD m_processId; + PSTARTUP_CALLBACK m_callback; + PVOID m_parameter; +#ifdef FEATURE_PAL + PVOID m_unregisterToken; +#else + bool m_canceled; + HANDLE m_startupEvent; + DWORD m_threadId; + HANDLE m_threadHandle; +#endif // FEATURE_PAL + +public: + RuntimeStartupHelper(DWORD dwProcessId, PSTARTUP_CALLBACK pfnCallback, PVOID parameter) : + m_ref(1), + m_processId(dwProcessId), + m_callback(pfnCallback), + m_parameter(parameter), +#ifdef FEATURE_PAL + m_unregisterToken(NULL) +#else + m_canceled(false), + m_startupEvent(NULL), + m_threadId(0), + m_threadHandle(NULL) +#endif // FEATURE_PAL + { + } + + ~RuntimeStartupHelper() + { +#ifndef FEATURE_PAL + if (m_startupEvent != NULL) + { + CloseHandle(m_startupEvent); + } + if (m_threadHandle != NULL) + { + CloseHandle(m_threadHandle); + } +#endif // FEATURE_PAL + } + + LONG AddRef() + { + LONG ref = InterlockedIncrement(&m_ref); + return ref; + } + + LONG Release() + { + LONG ref = InterlockedDecrement(&m_ref); + if (ref == 0) + { + delete this; + } + return ref; + } + +#ifdef FEATURE_PAL + + HRESULT Register() + { + if (PAL_RegisterForRuntimeStartup(m_processId, RuntimeStartupHandler, this, &m_unregisterToken) != NO_ERROR) + { + return E_INVALIDARG; + } + return S_OK; + } + + void Unregister() + { + PAL_UnregisterForRuntimeStartup(m_unregisterToken); + } + + void InvokeStartupCallback(char *pszModulePath, HMODULE hModule) + { + IUnknown *pCordb = NULL; + HMODULE hMod = NULL; + HRESULT hr = S_OK; + + // If either of these are NULL, there was an error from the PAL + // callback. GetLastError returns the error code from the PAL. + if (pszModulePath == NULL || hModule == NULL) + { + hr = HRESULT_FROM_WIN32(GetLastError()); + goto exit; + } + + PAL_CPP_TRY + { + FPCoreCLRCreateCordbObject fpCreate = NULL; + char dbiPath[MAX_LONGPATH]; + + char *pszLast = strrchr(pszModulePath, DIRECTORY_SEPARATOR_CHAR_A); + if (pszLast == NULL) + { + _ASSERT(!"InvokeStartupCallback: can find separator in coreclr path\n"); + hr = E_INVALIDARG; + goto exit; + } + + strncpy_s(dbiPath, _countof(dbiPath), pszModulePath, pszLast - pszModulePath); + strcat_s(dbiPath, _countof(dbiPath), DIRECTORY_SEPARATOR_STR_A MAKEDLLNAME_A("mscordbi")); + + hMod = LoadLibraryA(dbiPath); + if (hMod == NULL) + { + hr = CORDBG_E_DEBUG_COMPONENT_MISSING; + 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); + _ASSERTE((pCordb == NULL) == FAILED(hr)); + if (FAILED(hr)) + { + goto exit; + } + + m_callback(pCordb, m_parameter, S_OK); + } + PAL_CPP_CATCH_ALL + { + hr = E_FAIL; + goto exit; + } + PAL_CPP_ENDTRY + + exit: + if (FAILED(hr)) + { + _ASSERTE(pCordb == NULL); + + if (hMod != NULL) + { + FreeLibrary(hMod); + } + + // Invoke the callback on error + m_callback(NULL, m_parameter, hr); + } + } + +#else // FEATURE_PAL + + HRESULT Register() + { + HRESULT hr = GetStartupNotificationEvent(m_processId, &m_startupEvent); + if (FAILED(hr)) + { + goto exit; + } + + // Add a reference for the thread handler + AddRef(); + + m_threadHandle = CreateThread( + NULL, + 0, + ::StartupHelperThread, + this, + 0, + &m_threadId); + + if (m_threadHandle == NULL) + { + hr = E_OUTOFMEMORY; + Release(); + goto exit; + } + + exit: + return hr; + } + + HRESULT InternalEnumerateCLRs(HANDLE** ppHandleArray, LPWSTR** ppStringArray, DWORD* pdwArrayLength) + { + int numTries = 0; + HRESULT hr; + + while (numTries < 10) + { + // EnumerateCLRs uses the OS API CreateToolhelp32Snapshot which can return ERROR_BAD_LENGTH or + // ERROR_PARTIAL_COPY. If we get either of those, we try wait 1/10th of a second try again (that + // is the recommendation of the OS API owners) + hr = EnumerateCLRs(m_processId, ppHandleArray, ppStringArray, pdwArrayLength); + if ((hr != HRESULT_FROM_WIN32(ERROR_PARTIAL_COPY)) && (hr != HRESULT_FROM_WIN32(ERROR_BAD_LENGTH))) + { + break; + } + Sleep(100); + numTries++; + } + + return hr; + } + + void WakeRuntimes(HANDLE *handleArray, DWORD arrayLength) + { + if (handleArray != NULL) + { + for (int i = 0; i < (int)arrayLength; i++) + { + HANDLE h = handleArray[i]; + if (h != NULL && h != INVALID_HANDLE_VALUE) + { + SetEvent(h); + } + } + } + } + + void Unregister() + { + m_canceled = true; + + HANDLE *handleArray = NULL; + LPWSTR *stringArray = NULL; + DWORD arrayLength = 0; + + // Wake up runtime(s) + HRESULT hr = InternalEnumerateCLRs(&handleArray, &stringArray, &arrayLength); + if (SUCCEEDED(hr)) + { + WakeRuntimes(handleArray, arrayLength); + CloseCLREnumeration(handleArray, stringArray, arrayLength); + } + + // Wake up worker thread + SetEvent(m_startupEvent); + + // Don't need to wake up and wait for the worker thread if called on it + if (m_threadId != GetCurrentThreadId()) + { + // Wait for work thread to exit + WaitForSingleObject(m_threadHandle, INFINITE); + } + } + + HRESULT InvokeStartupCallback(bool *pCoreClrExists) + { + HANDLE *handleArray = NULL; + LPWSTR *stringArray = NULL; + DWORD arrayLength = 0; + HRESULT hr = S_OK; + + PAL_CPP_TRY + { + IUnknown *pCordb = NULL; + WCHAR verStr[MAX_LONGPATH]; + DWORD verLen; + + *pCoreClrExists = FALSE; + + hr = InternalEnumerateCLRs(&handleArray, &stringArray, &arrayLength); + if (FAILED(hr)) + { + goto exit; + } + + for (int i = 0; i < (int)arrayLength; i++) + { + *pCoreClrExists = TRUE; + + hr = CreateVersionStringFromModule(m_processId, stringArray[i], verStr, _countof(verStr), &verLen); + if (FAILED(hr)) + { + goto exit; + } + + hr = CreateDebuggingInterfaceFromVersion(verStr, &pCordb); + if (FAILED(hr)) + { + goto exit; + } + + m_callback(pCordb, m_parameter, S_OK); + + // Currently only the first coreclr module in a process is supported + break; + } + } + PAL_CPP_CATCH_ALL + { + hr = E_FAIL; + goto exit; + } + PAL_CPP_ENDTRY + + exit: + if (*pCoreClrExists) + { + // Wake up all the runtimes + WakeRuntimes(handleArray, arrayLength); + } + CloseCLREnumeration(handleArray, stringArray, arrayLength); + + return hr; + } + + void StartupHelperThread() + { + bool coreclrExists = false; + + HRESULT hr = InvokeStartupCallback(&coreclrExists); + if (SUCCEEDED(hr)) + { + if (!coreclrExists && !m_canceled) + { + // Wait until the coreclr runtime (debuggee) starts up + if (WaitForSingleObject(m_startupEvent, INFINITE) == WAIT_OBJECT_0) + { + if (!m_canceled) + { + hr = InvokeStartupCallback(&coreclrExists); + if (SUCCEEDED(hr)) + { + // We should always find a coreclr module + _ASSERTE(coreclrExists); + } + } + } + else + { + hr = HRESULT_FROM_WIN32(GetLastError()); + } + } + } + + if (FAILED(hr)) + { + m_callback(NULL, m_parameter, hr); + } + } + +#endif // FEATURE_PAL +}; + +#ifdef FEATURE_PAL + +static +void +RuntimeStartupHandler(char *pszModulePath, HMODULE hModule, PVOID parameter) +{ + RuntimeStartupHelper *helper = (RuntimeStartupHelper *)parameter; + helper->InvokeStartupCallback(pszModulePath, hModule); +} + +#else // FEATURE_PAL + +static +DWORD +StartupHelperThread(LPVOID p) +{ + RuntimeStartupHelper *helper = (RuntimeStartupHelper *)p; + helper->StartupHelperThread(); + helper->Release(); + return 0; +} + +#endif // FEATURE_PAL + +//----------------------------------------------------------------------------- +// Public API. +// +// RegisterForRuntimeStartup -- 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 +// hasn't been loaded yet) equally on both xplat and Windows. The callback is +// always called on a separate thread. This API returns immediately. +// +// The callback is invoked when the coreclr runtime module is loaded during early +// initialization. The runtime is blocked during initialization until the callback +// returns. +// +// If the runtime is already loaded in the process (as in the normal attach case), +// the callback is executed and the runtime is not blocked. +// +// The callback is always invoked on a separate thread and this API returns immediately. +// +// Only the first coreclr module instance found in the target process is currently +// supported. +// +// 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) +{ + PUBLIC_CONTRACT; + + if (pfnCallback == NULL || ppUnregisterToken == NULL) + { + return E_INVALIDARG; + } + + HRESULT hr = S_OK; + + RuntimeStartupHelper *helper = new (nothrow) RuntimeStartupHelper(dwProcessId, pfnCallback, parameter); + if (helper == NULL) + { + hr = E_OUTOFMEMORY; + } + else + { + hr = helper->Register(); + if (FAILED(hr)) + { + helper->Release(); + helper = NULL; + } + } + + *ppUnregisterToken = helper; + return hr; +} + +//----------------------------------------------------------------------------- +// Public API. +// +// UnregisterForRuntimeStartup -- stops/cancels runtime startup notification. Needs +// to be called during the debugger's shutdown to cleanup the internal data. +// +// This API can be called in the startup callback. Otherwise, it will block until +// the callback thread finishes and no more callbacks will be initiated after this +// API returns. +// +// pUnregisterToken -- unregister token from RegisterForRuntimeStartup or NULL. +//----------------------------------------------------------------------------- +HRESULT +UnregisterForRuntimeStartup( + __in PVOID pUnregisterToken) +{ + PUBLIC_CONTRACT; + + if (pUnregisterToken != NULL) + { + RuntimeStartupHelper *helper = (RuntimeStartupHelper *)pUnregisterToken; + helper->Unregister(); + helper->Release(); + } + + return S_OK; +} + //----------------------------------------------------------------------------- // Public API. // @@ -89,9 +581,10 @@ const int cchEventNameBufferSize = (sizeof(StartupNotifyEventNamePrefix) + sizeo + 10 // + decimal session id DWORD + 1; // '\' after session id - -HRESULT GetStartupNotificationEvent(DWORD debuggeePID, - __out HANDLE* phStartupEvent) +HRESULT +GetStartupNotificationEvent( + __in DWORD debuggeePID, + __out HANDLE* phStartupEvent) { PUBLIC_CONTRACT; @@ -162,17 +655,12 @@ HRESULT GetStartupNotificationEvent(DWORD debuggeePID, } *phStartupEvent = startupEvent; + return S_OK; #else *phStartupEvent = NULL; + return E_NOTIMPL; #endif // FEATURE_PAL - - return S_OK; } - -HRESULT GetContinueStartupEvent(DWORD debuggeePID, - LPCWSTR szTelestoFullPath, - __out HANDLE* phContinueStartupEvent); - // Refer to clr\src\mscoree\mscorwks_ntdef.src. const WORD kOrdinalForMetrics = 2; @@ -196,9 +684,12 @@ const WORD kOrdinalForMetrics = 2; // coreclr.dll is ours. A malicious user can be running a process with a bogus coreclr.dll loaded. // That's why we need to be extra careful reading coreclr.dll in this function. //----------------------------------------------------------------------------- -void GetTargetCLRMetrics(LPCWSTR szTelestoFullPath, - CLR_ENGINE_METRICS * pEngineMetricsOut, - DWORD * pdwRVAContinueStartupEvent = NULL) +static +void +GetTargetCLRMetrics( + LPCWSTR szTelestoFullPath, + CLR_ENGINE_METRICS *pEngineMetricsOut, + DWORD *pdwRVAContinueStartupEvent = NULL) { CONTRACTL { @@ -343,13 +834,22 @@ void GetTargetCLRMetrics(LPCWSTR szTelestoFullPath, #else //TODO: So far on POSIX systems we only support one version of debugging interface // in future we might want to detect it the same way we do it on Windows. + pEngineMetricsOut->cbSize = sizeof(*pEngineMetricsOut); pEngineMetricsOut->dwDbiVersion = CorDebugLatestVersion; + pEngineMetricsOut->phContinueStartupEvent = NULL; + + if (pdwRVAContinueStartupEvent != NULL) + { + *pdwRVAContinueStartupEvent = NULL; + } #endif // FEATURE_PAL } - // Returns true iff the module represents CoreClr. -bool IsCoreClr(const WCHAR* pModulePath) +static +bool +IsCoreClr( + const WCHAR* pModulePath) { _ASSERTE(pModulePath != NULL); @@ -366,7 +866,11 @@ bool IsCoreClr(const WCHAR* pModulePath) } // Returns true iff the module sent is named CoreClr.dll and has the metrics expected in it's PE header. -bool IsCoreClrWithGoodHeader(HANDLE hProcess, HMODULE hModule) +static +bool +IsCoreClrWithGoodHeader( + HANDLE hProcess, + HMODULE hModule) { HRESULT hr = S_OK; @@ -419,10 +923,12 @@ bool IsCoreClrWithGoodHeader(HANDLE hProcess, HMODULE hModule) // Notes: // Callers use code:CloseCLREnumeration to free the returned arrays. //----------------------------------------------------------------------------- -HRESULT EnumerateCLRs(DWORD debuggeePID, - __out HANDLE** ppHandleArrayOut, - __out LPWSTR** ppStringArrayOut, - __out DWORD* pdwArrayLengthOut) +HRESULT +EnumerateCLRs( + DWORD debuggeePID, + __out HANDLE** ppHandleArrayOut, + __out LPWSTR** ppStringArrayOut, + __out DWORD* pdwArrayLengthOut) { PUBLIC_CONTRACT; @@ -432,7 +938,7 @@ HRESULT EnumerateCLRs(DWORD debuggeePID, HandleHolder hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, debuggeePID); if (NULL == hProcess) - ThrowHR(E_FAIL); + return E_FAIL; // These shouldn't be freed HMODULE modules[1000]; @@ -556,7 +1062,11 @@ HRESULT EnumerateCLRs(DWORD debuggeePID, // dwArrayLength -- array length originally returned by EnumerateCLRs // //----------------------------------------------------------------------------- -HRESULT CloseCLREnumeration(HANDLE* pHandleArray, LPWSTR* pStringArray, DWORD dwArrayLength) +HRESULT +CloseCLREnumeration( + __in HANDLE* pHandleArray, + __in LPWSTR* pStringArray, + __in DWORD dwArrayLength) { PUBLIC_CONTRACT; @@ -593,7 +1103,11 @@ HRESULT CloseCLREnumeration(HANDLE* pHandleArray, LPWSTR* pStringArray, DWORD dw // - NULL if the module is not loaded. // - else Throws. *ppBaseAddress = NULL //----------------------------------------------------------------------------- -BYTE* GetRemoteModuleBaseAddress(DWORD dwPID, LPCWSTR szFullModulePath) +static +BYTE* +GetRemoteModuleBaseAddress( + DWORD dwPID, + LPCWSTR szFullModulePath) { CONTRACTL { @@ -674,11 +1188,13 @@ const WCHAR *c_versionStrFormat = W("%08x;%08x;%p"); // The version string is an opaque string that can only be passed back to other // DbgShim APIs. //----------------------------------------------------------------------------- -HRESULT CreateVersionStringFromModule(DWORD pidDebuggee, - LPCWSTR szModuleName, - __out_ecount_part(cchBuffer, *pdwLength) LPWSTR pBuffer, - DWORD cchBuffer, - __out DWORD* pdwLength) +HRESULT +CreateVersionStringFromModule( + __in DWORD pidDebuggee, + __in LPCWSTR szModuleName, + __out_ecount_part(cchBuffer, *pdwLength) LPWSTR pBuffer, + __in DWORD cchBuffer, + __out DWORD* pdwLength) { PUBLIC_CONTRACT; @@ -735,13 +1251,6 @@ HRESULT CreateVersionStringFromModule(DWORD pidDebuggee, return S_OK; } -// Functions that we'll look for in the loaded Mscordbi module. -typedef HRESULT (STDAPICALLTYPE *FPCoreCLRCreateCordbObject)( - int iDebuggerVersion, - DWORD pid, - HMODULE hmodTargetCLR, - IUnknown ** ppCordb); - //----------------------------------------------------------------------------- // Parse a version string into useful data. // @@ -758,8 +1267,13 @@ typedef HRESULT (STDAPICALLTYPE *FPCoreCLRCreateCordbObject)( // The version string is coming from the target CoreClr and in the case of a corrupted target, could be // an arbitrary string. It should be treated as untrusted public input. //----------------------------------------------------------------------------- -HRESULT ParseVersionString(LPCWSTR szDebuggeeVersion, CorDebugInterfaceVersion * piDebuggerVersion, DWORD * pdwPidDebuggee, - HMODULE * phmodTargetCLR) +static +HRESULT +ParseVersionString( + LPCWSTR szDebuggeeVersion, + CorDebugInterfaceVersion *piDebuggerVersion, + DWORD *pdwPidDebuggee, + HMODULE *phmodTargetCLR) { if ((piDebuggerVersion == NULL) || (pdwPidDebuggee == NULL) || @@ -772,7 +1286,6 @@ HRESULT ParseVersionString(LPCWSTR szDebuggeeVersion, CorDebugInterfaceVersion * } int numFieldsAssigned = swscanf_s(szDebuggeeVersion, c_versionStrFormat, piDebuggerVersion, pdwPidDebuggee, phmodTargetCLR); - if (numFieldsAssigned != 3) { return E_FAIL; @@ -787,13 +1300,14 @@ HRESULT ParseVersionString(LPCWSTR szDebuggeeVersion, CorDebugInterfaceVersion * // Arguments: // szFullDbiPath - (in/out): on input, the directory containing dbi. On output, the full path to dbi.dll. //----------------------------------------------------------------------------- -void AppendDbiDllName(SString & szFullDbiPath) +static +void +AppendDbiDllName(SString & szFullDbiPath) { const WCHAR * pDbiDllName = DIRECTORY_SEPARATOR_STR_W MAKEDLLNAME_W(W("mscordbi")); szFullDbiPath.Append(pDbiDllName); } - //----------------------------------------------------------------------------- // Return a path to the dbi next to the runtime, if present. // @@ -805,8 +1319,13 @@ void AppendDbiDllName(SString & szFullDbiPath) // Notes: // This just calculates a filename and does not determine if the file actually exists. //----------------------------------------------------------------------------- -void GetDbiFilenameNextToRuntime(DWORD pidDebuggee, HMODULE hmodTargetCLR, SString & szFullDbiPath, - SString & szFullCoreClrPath) +static +void +GetDbiFilenameNextToRuntime( + DWORD pidDebuggee, + HMODULE hmodTargetCLR, + SString & szFullDbiPath, + SString & szFullCoreClrPath) { szFullDbiPath.Clear(); @@ -830,8 +1349,6 @@ void GetDbiFilenameNextToRuntime(DWORD pidDebuggee, HMODULE hmodTargetCLR, SStri ThrowHR(E_FAIL); } - - // Change: // c:\abc\coreclr.dll // 01234567890 // c:\abc\mscordbi.dll @@ -859,8 +1376,11 @@ void GetDbiFilenameNextToRuntime(DWORD pidDebuggee, HMODULE hmodTargetCLR, SStri // Return Value: // true if the versions match // - -bool CheckDbiAndRuntimeVersion(SString & szFullDbiPath, SString & szFullCoreClrPath) +static +bool +CheckDbiAndRuntimeVersion( + SString & szFullDbiPath, + SString & szFullCoreClrPath) { #ifndef FEATURE_PAL DWORD dwDbiVersionMS = 0; @@ -886,7 +1406,6 @@ bool CheckDbiAndRuntimeVersion(SString & szFullDbiPath, SString & szFullCoreClrP #endif // FEATURE_PAL } - //----------------------------------------------------------------------------- // Public API. // Given a version string, create the matching mscordbi.dll for it. @@ -903,11 +1422,11 @@ bool CheckDbiAndRuntimeVersion(SString & szFullDbiPath, SString & szFullCoreClrP // the right debug pack is not installed. // else Error. (*ppCordb will be null) //----------------------------------------------------------------------------- -HRESULT CreateDebuggingInterfaceFromVersionEx( - int iDebuggerVersion, - LPCWSTR szDebuggeeVersion, - IUnknown ** ppCordb - ) +HRESULT +CreateDebuggingInterfaceFromVersionEx( + __in int iDebuggerVersion, + __in LPCWSTR szDebuggeeVersion, + __out IUnknown ** ppCordb) { PUBLIC_CONTRACT; @@ -925,8 +1444,6 @@ HRESULT CreateDebuggingInterfaceFromVersionEx( goto Exit; } - *ppCordb = NULL; - // // Step 1: Parse version information into internal data structures // @@ -940,7 +1457,7 @@ HRESULT CreateDebuggingInterfaceFromVersionEx( goto Exit; // - // Step 2: Find the proper Dbi module (mscordbi.dll) and load it. + // Step 2: Find the proper dbi module (mscordbi) and load it. // // Check for dbi next to target CLR. @@ -1020,10 +1537,7 @@ Exit: } // Set our outparam. - if (ppCordb != NULL) - { - *ppCordb = pCordb; - } + *ppCordb = pCordb; // On success case, mscordbi.dll is leaked. // - We never give the caller back the module handle, so our caller can't do FreeLibrary(). @@ -1032,7 +1546,6 @@ Exit: return hr; } - //----------------------------------------------------------------------------- // Public API. // Superceded by CreateDebuggingInterfaceFromVersionEx in SLv4. @@ -1049,16 +1562,15 @@ Exit: // the right debug pack is not installed. // else Error. (*ppCordb will be null) //----------------------------------------------------------------------------- -HRESULT CreateDebuggingInterfaceFromVersion( - LPCWSTR szDebuggeeVersion, - IUnknown ** ppCordb +HRESULT +CreateDebuggingInterfaceFromVersion( + __in LPCWSTR szDebuggeeVersion, + __out IUnknown ** ppCordb ) { PUBLIC_CONTRACT; - return CreateDebuggingInterfaceFromVersionEx(CorDebugVersion_2_0, - szDebuggeeVersion, - ppCordb); + return CreateDebuggingInterfaceFromVersionEx(CorDebugVersion_2_0, szDebuggeeVersion, ppCordb); } #ifndef FEATURE_PAL @@ -1075,9 +1587,11 @@ HRESULT CreateDebuggingInterfaceFromVersion( // Returns: // S_OK on success. //------------------------------------------------------------------------------ -HRESULT GetContinueStartupEvent(DWORD debuggeePID, - LPCWSTR szTelestoFullPath, - __out HANDLE* phContinueStartupEvent) +HRESULT +GetContinueStartupEvent( + DWORD debuggeePID, + LPCWSTR szTelestoFullPath, + __out HANDLE* phContinueStartupEvent) { if ((phContinueStartupEvent == NULL) || (szTelestoFullPath == NULL)) return E_INVALIDARG; @@ -1130,7 +1644,22 @@ HRESULT GetContinueStartupEvent(DWORD debuggeePID, #include "debugshim.h" #endif -HRESULT CLRCreateInstance(REFCLSID clsid, REFIID riid, LPVOID *ppInterface) +//----------------------------------------------------------------------------- +// Public API. +// +// Parameters: +// clsid +// riid +// ppInterface +// +// Return: +// S_OK on success. +//----------------------------------------------------------------------------- +HRESULT +CLRCreateInstance( + REFCLSID clsid, + REFIID riid, + LPVOID *ppInterface) { #if defined(FEATURE_CORESYSTEM) diff --git a/src/dlls/dbgshim/dbgshim.h b/src/dlls/dbgshim/dbgshim.h index 4623e0d..f706b89 100644 --- a/src/dlls/dbgshim/dbgshim.h +++ b/src/dlls/dbgshim/dbgshim.h @@ -9,28 +9,52 @@ #include -EXTERN_C HRESULT GetStartupNotificationEvent(DWORD debuggeePID, - __out HANDLE* phStartupEvent); - -EXTERN_C HRESULT CloseCLREnumeration(HANDLE* pHandleArray, LPWSTR* pStringArray, DWORD dwArrayLength); - -EXTERN_C HRESULT EnumerateCLRs(DWORD debuggeePID, - __out HANDLE** ppHandleArrayOut, - __out LPWSTR** ppStringArrayOut, - __out DWORD* pdwArrayLengthOut); - -EXTERN_C HRESULT CreateVersionStringFromModule(DWORD pidDebuggee, - LPCWSTR szModuleName, - __out_ecount_part(cchBuffer, *pdwLength) LPWSTR pBuffer, - DWORD cchBuffer, - __out DWORD* pdwLength); - -EXTERN_C HRESULT CreateDebuggingInterfaceFromVersionEx( - int iDebuggerVersion, - LPCWSTR szDebuggeeVersion, - IUnknown ** ppCordb); - -EXTERN_C HRESULT CreateDebuggingInterfaceFromVersion( - LPCWSTR szDebuggeeVersion, - IUnknown ** ppCordb); +typedef VOID (*PSTARTUP_CALLBACK)(IUnknown *pCordb, PVOID parameter, HRESULT hr); + +EXTERN_C HRESULT +RegisterForRuntimeStartup( + __in DWORD dwProcessId, + __in PSTARTUP_CALLBACK pfnCallback, + __in PVOID parameter, + __out PVOID *ppUnregisterToken); + +EXTERN_C HRESULT +UnregisterForRuntimeStartup( + __in PVOID pUnregisterToken); + +EXTERN_C HRESULT +GetStartupNotificationEvent( + __in DWORD debuggeePID, + __out HANDLE* phStartupEvent); + +EXTERN_C HRESULT +EnumerateCLRs(DWORD debuggeePID, + __out HANDLE** ppHandleArrayOut, + __out LPWSTR** ppStringArrayOut, + __out DWORD* pdwArrayLengthOut); + +EXTERN_C HRESULT +CloseCLREnumeration( + __in HANDLE* pHandleArray, + __in LPWSTR* pStringArray, + __in DWORD dwArrayLength); + +EXTERN_C HRESULT +CreateVersionStringFromModule( + __in DWORD pidDebuggee, + __in LPCWSTR szModuleName, + __out_ecount_part(cchBuffer, *pdwLength) LPWSTR pBuffer, + __in DWORD cchBuffer, + __out DWORD* pdwLength); + +EXTERN_C HRESULT +CreateDebuggingInterfaceFromVersionEx( + __in int iDebuggerVersion, + __in LPCWSTR szDebuggeeVersion, + __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 942bc22..cc62a40 100644 --- a/src/dlls/dbgshim/dbgshim.ntdef +++ b/src/dlls/dbgshim/dbgshim.ntdef @@ -4,6 +4,8 @@ ; ; ==--== EXPORTS + RegisterForRuntimeStartup + UnregisterForRuntimeStartup GetStartupNotificationEvent EnumerateCLRs CloseCLREnumeration diff --git a/src/inc/dacvars.h b/src/inc/dacvars.h index c45a65f..6a0bcc2 100644 --- a/src/inc/dacvars.h +++ b/src/inc/dacvars.h @@ -309,7 +309,9 @@ DEFINE_DACVAR(ULONG, PTR_BYTE, WKS__gc_heap__background_saved_lowest_address, WK DEFINE_DACVAR(ULONG, PTR_BYTE, WKS__gc_heap__background_saved_highest_address, WKS::gc_heap::background_saved_highest_address) #ifdef FEATURE_CORECLR +#ifndef FEATURE_PAL DEFINE_DACVAR(ULONG, HANDLE, dac__g_hContinueStartupEvent, ::g_hContinueStartupEvent) +#endif // !FEATURE_PAL DEFINE_DACVAR(ULONG, DWORD, CorHost2__m_dwStartupFlags, CorHost2::m_dwStartupFlags) #endif // FEATURE_CORECLR diff --git a/src/pal/inc/pal.h b/src/pal/inc/pal.h index b937917..5324b3b 100644 --- a/src/pal/inc/pal.h +++ b/src/pal/inc/pal.h @@ -481,9 +481,10 @@ typedef long time_t; #define PAL_INITIALIZE_NONE 0x00 #define PAL_INITIALIZE_SYNC_THREAD 0x01 #define PAL_INITIALIZE_EXEC_ALLOCATOR 0x02 +#define PAL_INITIALIZE_STD_HANDLES 0x04 // PAL_Initialize() flags -#define PAL_INITIALIZE PAL_INITIALIZE_SYNC_THREAD +#define PAL_INITIALIZE (PAL_INITIALIZE_SYNC_THREAD | PAL_INITIALIZE_STD_HANDLES) // PAL_InitializeDLL() flags - don't start any of the helper threads #define PAL_INITIALIZE_DLL PAL_INITIALIZE_NONE @@ -569,16 +570,6 @@ PALAPI PAL_TerminateEx( int exitCode); -/*++ -Function: - PAL_SetShutdownCallback - -Abstract: - Sets a callback that is executed when the PAL is shut down because of - ExitProcess, TerminateProcess or PAL_Shutdown but not PAL_Terminate/Ex. - - NOTE: Currently only one callback can be set at a time. ---*/ typedef VOID (*PSHUTDOWN_CALLBACK)(void); PALIMPORT @@ -587,6 +578,31 @@ PALAPI PAL_SetShutdownCallback( IN PSHUTDOWN_CALLBACK callback); +typedef VOID (*PPAL_STARTUP_CALLBACK)( + char *modulePath, + HMODULE hModule, + PVOID parameter); + +PALIMPORT +DWORD +PALAPI +PAL_RegisterForRuntimeStartup( + IN DWORD dwProcessId, + IN PPAL_STARTUP_CALLBACK pfnCallback, + IN PVOID parameter, + OUT PVOID *ppUnregisterToken); + +PALIMPORT +DWORD +PALAPI +PAL_UnregisterForRuntimeStartup( + IN PVOID pUnregisterToken); + +PALIMPORT +BOOL +PALAPI +PAL_NotifyRuntimeStarted(); + PALIMPORT void PALAPI @@ -633,15 +649,6 @@ PAL_Random( IN OUT LPVOID lpBuffer, IN DWORD dwLength); -// This helper will be used *only* by the CoreCLR to determine -// if an address lies inside CoreCLR or not. -// -// This shouldnt be used by any other component that links into the PAL. -PALIMPORT -BOOL -PALAPI -PAL_IsIPInCoreCLR(IN PVOID address); - #ifdef PLATFORM_UNIX PALIMPORT diff --git a/src/pal/inc/rt/palrt.h b/src/pal/inc/rt/palrt.h index 7426d56..9f2194a 100644 --- a/src/pal/inc/rt/palrt.h +++ b/src/pal/inc/rt/palrt.h @@ -1363,6 +1363,7 @@ typedef VOID (__stdcall *WAITORTIMERCALLBACK)(PVOID, BOOLEAN); #ifdef PLATFORM_UNIX #define DIRECTORY_SEPARATOR_CHAR_A '/' #define DIRECTORY_SEPARATOR_CHAR_W W('/') +#define DIRECTORY_SEPARATOR_STR_A "/" #define DIRECTORY_SEPARATOR_STR_W W("/") #define PATH_SEPARATOR_CHAR_W W(':') #define PATH_SEPARATOR_STR_W W(":") @@ -1370,6 +1371,7 @@ typedef VOID (__stdcall *WAITORTIMERCALLBACK)(PVOID, BOOLEAN); #else // PLATFORM_UNIX #define DIRECTORY_SEPARATOR_CHAR_A '\\' #define DIRECTORY_SEPARATOR_CHAR_W W('\\') +#define DIRECTORY_SEPARATOR_STR_A "\\" #define DIRECTORY_SEPARATOR_STR_W W("\\") #define PATH_SEPARATOR_CHAR_W W(';') #define PATH_SEPARATOR_STR_W W(";") diff --git a/src/pal/src/debug/debug.cpp b/src/pal/src/debug/debug.cpp index 9a9fefc..715ea7d 100644 --- a/src/pal/src/debug/debug.cpp +++ b/src/pal/src/debug/debug.cpp @@ -672,7 +672,7 @@ DBGAttachProcess( int attchmentCount; int savedErrno; #if HAVE_PROCFS_CTL - int fd; + int fd = -1; char ctlPath[1024]; #endif // HAVE_PROCFS_CTL @@ -822,7 +822,7 @@ DBGDetachProcess( { int nbAttachLeft; #if HAVE_PROCFS_CTL - int fd; + int fd = -1; char ctlPath[1024]; #endif // HAVE_PROCFS_CTL @@ -1253,7 +1253,7 @@ ReadProcessMemory( vm_map_t task; LONG_PTR bytesToRead; #elif HAVE_PROCFS_CTL - int fd; + int fd = -1; char memPath[64]; off_t offset; #elif !HAVE_TTRACE @@ -1549,7 +1549,7 @@ WriteProcessMemory( kern_return_t result; vm_map_t task; #elif HAVE_PROCFS_CTL - int fd; + int fd = -1; char memPath[64]; LONG_PTR bytesWritten; off_t offset; diff --git a/src/pal/src/exception/machexception.cpp b/src/pal/src/exception/machexception.cpp index 4142e7c..4cd19d7 100644 --- a/src/pal/src/exception/machexception.cpp +++ b/src/pal/src/exception/machexception.cpp @@ -1123,30 +1123,6 @@ bool IsWithinCoreCLR(void *pAddr) return (pAddr >= s_pLowerBound) && (pAddr < s_pUpperBound); } -#if !defined(_MSC_VER) && defined(_WIN64) -BOOL PALAPI PAL_IsIPInCoreCLR(IN PVOID address) -{ - BOOL fIsAddressWithinCoreCLR = FALSE; - - PERF_ENTRY(PAL_IsIPInCoreCLR); - ENTRY("PAL_IsIPInCoreCLR (address=%p)\n", address); - - if (address != NULL) - { - if (IsWithinCoreCLR(address)) - { - fIsAddressWithinCoreCLR = TRUE; - } - } - - LOGEXIT("PAL_IsIPInCoreCLR returns %d\n", fIsAddressWithinCoreCLR); - PERF_EXIT(PAL_IsIPInCoreCLR); - - return fIsAddressWithinCoreCLR; -} - -#endif //!defined(_MSC_VER) && defined(_WIN64) - extern malloc_zone_t *s_pExecutableHeap; // from heap.cpp in #ifdef CACHE_HEAP_ZONE #pragma mark - diff --git a/src/pal/src/exception/signal.cpp b/src/pal/src/exception/signal.cpp index ef7b569..aa70c22 100644 --- a/src/pal/src/exception/signal.cpp +++ b/src/pal/src/exception/signal.cpp @@ -65,6 +65,8 @@ static void sigfpe_handler(int code, siginfo_t *siginfo, void *context); static void sigsegv_handler(int code, siginfo_t *siginfo, void *context); static void sigtrap_handler(int code, siginfo_t *siginfo, void *context); static void sigbus_handler(int code, siginfo_t *siginfo, void *context); +static void sigint_handler(int code, siginfo_t *siginfo, void *context); +static void sigquit_handler(int code, siginfo_t *siginfo, void *context); static void common_signal_handler(PEXCEPTION_POINTERS pointers, int code, native_context_t *ucontext); @@ -81,6 +83,8 @@ struct sigaction g_previous_sigtrap; struct sigaction g_previous_sigfpe; struct sigaction g_previous_sigbus; struct sigaction g_previous_sigsegv; +struct sigaction g_previous_sigint; +struct sigaction g_previous_sigquit; /* public function definitions ************************************************/ @@ -119,6 +123,8 @@ BOOL SEHInitializeSignals() handle_signal(SIGFPE, sigfpe_handler, &g_previous_sigfpe); handle_signal(SIGBUS, sigbus_handler, &g_previous_sigbus); handle_signal(SIGSEGV, sigsegv_handler, &g_previous_sigsegv); + handle_signal(SIGINT, sigint_handler, &g_previous_sigint); + handle_signal(SIGQUIT, sigquit_handler, &g_previous_sigquit); handle_signal(INJECT_ACTIVATION_SIGNAL, inject_activation_handler, NULL); @@ -162,6 +168,8 @@ void SEHCleanupSignals() restore_signal(SIGFPE, &g_previous_sigfpe); restore_signal(SIGBUS, &g_previous_sigbus); restore_signal(SIGSEGV, &g_previous_sigsegv); + restore_signal(SIGINT, &g_previous_sigint); + restore_signal(SIGQUIT, &g_previous_sigquit); } /* internal function definitions **********************************************/ @@ -209,6 +217,8 @@ static void sigill_handler(int code, siginfo_t *siginfo, void *context) // Restore the original or default handler and restart h/w exception restore_signal(code, &g_previous_sigill); } + + PROCShutdownProcess(); } /*++ @@ -254,6 +264,8 @@ static void sigfpe_handler(int code, siginfo_t *siginfo, void *context) // Restore the original or default handler and restart h/w exception restore_signal(code, &g_previous_sigfpe); } + + PROCShutdownProcess(); } /*++ @@ -322,6 +334,8 @@ static void sigsegv_handler(int code, siginfo_t *siginfo, void *context) // Restore the original or default handler and restart h/w exception restore_signal(code, &g_previous_sigsegv); } + + PROCShutdownProcess(); } /*++ @@ -366,8 +380,10 @@ static void sigtrap_handler(int code, siginfo_t *siginfo, void *context) { // We abort instead of restore the original or default handler and returning // because returning from a SIGTRAP handler continues execution past the trap. - abort(); + PROCAbort(); } + + PROCShutdownProcess(); } /*++ @@ -421,6 +437,52 @@ static void sigbus_handler(int code, siginfo_t *siginfo, void *context) // Restore the original or default handler and restart h/w exception restore_signal(code, &g_previous_sigbus); } + + PROCShutdownProcess(); +} + +/*++ +Function : + sigint_handler + + handle SIGINT signal + +Parameters : + POSIX signal handler parameter list ("man sigaction" for details) + + (no return value) +--*/ +static void sigint_handler(int code, siginfo_t *siginfo, void *context) +{ + TRACE("SIGINT signal; chaining to previous sigaction\n"); + + PROCShutdownProcess(); + + // Restore the original or default handler and resend signal + restore_signal(code, &g_previous_sigint); + kill(gPID, code); +} + +/*++ +Function : + sigquit_handler + + handle SIGQUIT signal + +Parameters : + POSIX signal handler parameter list ("man sigaction" for details) + + (no return value) +--*/ +static void sigquit_handler(int code, siginfo_t *siginfo, void *context) +{ + TRACE("SIGQUIT signal; chaining to previous sigaction\n"); + + PROCShutdownProcess(); + + // Restore the original or default handler and resend signal + restore_signal(code, &g_previous_sigquit); + kill(gPID, code); } /*++ @@ -480,10 +542,12 @@ PAL_ERROR InjectActivationInternal(CorUnix::CPalThread* pThread) int status = pthread_kill(pThread->GetPThreadSelf(), INJECT_ACTIVATION_SIGNAL); if (status != 0) { + PROCShutdownProcess(); + // Failure to send the signal is fatal. There are only two cases when sending // the signal can fail. First, if the signal ID is invalid and second, // if the thread doesn't exist anymore. - abort(); + PROCAbort(); } return NO_ERROR; diff --git a/src/pal/src/file/file.cpp b/src/pal/src/file/file.cpp index 352a097..30a4c3b 100644 --- a/src/pal/src/file/file.cpp +++ b/src/pal/src/file/file.cpp @@ -141,9 +141,9 @@ typedef enum /* Static global. The init function must be called before any other functions and if it is not successful, no other functions should be done. */ -static HANDLE pStdIn; -static HANDLE pStdOut; -static HANDLE pStdErr; +static HANDLE pStdIn = INVALID_HANDLE_VALUE; +static HANDLE pStdOut = INVALID_HANDLE_VALUE; +static HANDLE pStdErr = INVALID_HANDLE_VALUE; /*++ Function : @@ -4942,12 +4942,25 @@ void FILECleanupStdHandles(void) stdin_handle = pStdIn; stdout_handle = pStdOut; stderr_handle = pStdErr; + pStdIn = INVALID_HANDLE_VALUE; pStdOut = INVALID_HANDLE_VALUE; pStdErr = INVALID_HANDLE_VALUE; - CloseHandle(stdin_handle); - CloseHandle(stdout_handle); - CloseHandle(stderr_handle); + + if (stdin_handle != INVALID_HANDLE_VALUE) + { + CloseHandle(stdin_handle); + } + + if (stdout_handle != INVALID_HANDLE_VALUE) + { + CloseHandle(stdout_handle); + } + + if (stderr_handle != INVALID_HANDLE_VALUE) + { + CloseHandle(stderr_handle); + } } diff --git a/src/pal/src/include/pal/dbgmsg.h b/src/pal/src/include/pal/dbgmsg.h index bf16bc4..93172f7 100644 --- a/src/pal/src/include/pal/dbgmsg.h +++ b/src/pal/src/include/pal/dbgmsg.h @@ -54,6 +54,7 @@ Available channels : LOADER : Loading API (LoadLibrary, etc); loader application HANDLE : Handle manager (CloseHandle, etc.) SHMEM : Shared Memory functions (for IPC) + PROCESS : Process related APIs THREAD : Threading mechanism EXCEPT : Structured Exception Handling functions CRT : PAL implementation of the C Runtime Library functions @@ -172,6 +173,7 @@ typedef enum DCI_LOADER, DCI_HANDLE, DCI_SHMEM, + DCI_PROCESS, DCI_THREAD, DCI_EXCEPT, DCI_CRT, diff --git a/src/pal/src/include/pal/init.h b/src/pal/src/include/pal/init.h index c482a00..6e58c0d 100644 --- a/src/pal/src/include/pal/init.h +++ b/src/pal/src/include/pal/init.h @@ -31,15 +31,6 @@ extern "C" /*++ Function: - PALShutdown - -Utility function to force PAL to shutdown state - ---*/ -void PALShutdown( void ); - -/*++ -Function: PALCommonCleanup Utility function to prepare for shutdown. diff --git a/src/pal/src/include/pal/process.h b/src/pal/src/include/pal/process.h index cd26349..3f93df0 100644 --- a/src/pal/src/include/pal/process.h +++ b/src/pal/src/include/pal/process.h @@ -69,7 +69,7 @@ Return Notes : This function takes ownership of lpwstrCmdLine, but not of lpwstrFullPath --*/ -BOOL PROCCreateInitialProcess(LPWSTR lpwstrCmdLine, LPWSTR lpwstrFullPath); +BOOL PROCCreateInitialProcess(LPWSTR lpwstrCmdLine, LPWSTR lpwstrFullPath); /*++ Function: @@ -121,14 +121,26 @@ VOID PROCProcessUnlock(VOID); /*++ Function: - PROCCleanupProcess + PROCAbort() + + Aborts the process after calling the shutdown cleanup handler. This function + should be called instead of calling abort() directly. + + Does not return +--*/ +PAL_NORETURN +void PROCAbort(); + +/*++ +Function: + PROCShutdownProcess - Do all cleanup work for TerminateProcess, but don't terminate the process. - If bTerminateUnconditionally is TRUE, we exit as quickly as possible. + Calls the abort handler to do any shutdown cleanup. Call be + called from the unhandled native exception handler. (no return value) --*/ -void PROCCleanupProcess(BOOL bTerminateUnconditionally); +void PROCShutdownProcess(); /*++ Function: diff --git a/src/pal/src/include/pal/procobj.hpp b/src/pal/src/include/pal/procobj.hpp index 8500fd1..866a3ae 100644 --- a/src/pal/src/include/pal/procobj.hpp +++ b/src/pal/src/include/pal/procobj.hpp @@ -73,7 +73,8 @@ namespace CorUnix ps(PS_IDLE), dwExitCode(0), lAttachCount(0), - pProcessModules(NULL) + pProcessModules(NULL), + cProcessModules(0) { }; @@ -84,12 +85,7 @@ namespace CorUnix DWORD dwExitCode; LONG lAttachCount; ProcessModules *pProcessModules; - }; - - class CProcSharedData - { - public: - DWORD dwProcessId; + DWORD cProcessModules; }; PAL_ERROR diff --git a/src/pal/src/init/pal.cpp b/src/pal/src/init/pal.cpp index 626f077..ab00bfb 100644 --- a/src/pal/src/init/pal.cpp +++ b/src/pal/src/init/pal.cpp @@ -506,11 +506,14 @@ Initialize( goto CLEANUP10; } - /* create file objects for standard handles */ - if(!FILEInitStdHandles()) + if (flags & PAL_INITIALIZE_STD_HANDLES) { - ERROR("Unable to initialize standard file handles\n"); - goto CLEANUP13; + /* create file objects for standard handles */ + if (!FILEInitStdHandles()) + { + ERROR("Unable to initialize standard file handles\n"); + goto CLEANUP13; + } } if (FALSE == CRTInitStdStreams()) @@ -794,6 +797,7 @@ PAL_TerminateEx( LOGEXIT("PAL_Terminate returns.\n"); } + // Declare the beginning of shutdown PALSetShutdownIntent(); LOGEXIT("PAL_TerminateEx is exiting the current process.\n"); @@ -836,7 +840,7 @@ BOOL PALIsThreadDataInitialized() Function: PALCommonCleanup -Utility function to prepare for shutdown. + Utility function to prepare for shutdown. --*/ void @@ -844,36 +848,24 @@ PALCommonCleanup() { static bool cleanupDone = false; + // Declare the beginning of shutdown + PALSetShutdownIntent(); + if (!cleanupDone) { cleanupDone = true; - PALSetShutdownIntent(); - // // Let the synchronization manager know we're about to shutdown // - CPalSynchMgrController::PrepareForShutdown(); #ifdef _DEBUG PROCDumpThreadList(); #endif } -} - -/*++ -Function: - PALShutdown - sets the PAL's initialization count to zero, so that PALIsInitialized will - return FALSE. called by PROCCleanupProcess to tell some functions that the - PAL isn't fully functional, and that they should use an alternate code path - -(no parameters, no retun vale) ---*/ -void PALShutdown() -{ + // Mark that the PAL is uninitialized init_count = 0; } diff --git a/src/pal/src/init/sxs.cpp b/src/pal/src/init/sxs.cpp index f568711..f011047 100644 --- a/src/pal/src/init/sxs.cpp +++ b/src/pal/src/init/sxs.cpp @@ -15,6 +15,7 @@ #include "pal/thread.hpp" #include "../thread/procprivate.hpp" #include "pal/module.h" +#include "pal/process.h" #include "pal/seh.hpp" using namespace CorUnix; @@ -95,7 +96,7 @@ CreateCurrentThreadData() if (NO_ERROR != palError) { ASSERT("Unable to allocate pal thread: error %d - aborting\n", palError); - abort(); + PROCAbort(); } } diff --git a/src/pal/src/misc/dbgmsg.cpp b/src/pal/src/misc/dbgmsg.cpp index 4d34564..8d66254 100644 --- a/src/pal/src/misc/dbgmsg.cpp +++ b/src/pal/src/misc/dbgmsg.cpp @@ -84,6 +84,7 @@ static const char *dbg_channel_names[]= "LOADER", "HANDLE", "SHMEM", + "PROCESS", "THREAD", "EXCEPT", "CRT", diff --git a/src/pal/src/misc/miscpalapi.cpp b/src/pal/src/misc/miscpalapi.cpp index 15cb741..c39fb7f 100644 --- a/src/pal/src/misc/miscpalapi.cpp +++ b/src/pal/src/misc/miscpalapi.cpp @@ -24,6 +24,7 @@ Revision History: #include "pal/palinternal.h" #include "pal/dbgmsg.h" #include "pal/file.h" +#include "pal/process.h" #include "pal/module.h" #include "pal/malloc.hpp" @@ -367,7 +368,7 @@ CoCreateGuid(OUT GUID * pguid) if (status != uuid_s_ok) { ASSERT("Unexpected uuid_create failure (status=%u)\n", status); - abort(); + PROCAbort(); } // Encode the uuid with little endian. diff --git a/src/pal/src/thread/process.cpp b/src/pal/src/thread/process.cpp index 4e2a9c5..3d9a9c4 100644 --- a/src/pal/src/thread/process.cpp +++ b/src/pal/src/thread/process.cpp @@ -49,36 +49,19 @@ Abstract: #include #include #include +#include using namespace CorUnix; -SET_DEFAULT_DEBUG_CHANNEL(THREAD); - - -void -ProcessCleanupRoutine( - CPalThread *pThread, - IPalObject *pObjectToCleanup, - bool fShutdown, - bool fCleanupSharedState - ); - -PAL_ERROR -ProcessInitializationRoutine( - CPalThread *pThread, - CObjectType *pObjectType, - void *pImmutableData, - void *pSharedData, - void *pProcessLocalData - ); +SET_DEFAULT_DEBUG_CHANNEL(PROCESS); CObjectType CorUnix::otProcess( otiProcess, - ProcessCleanupRoutine, - ProcessInitializationRoutine, + NULL, + NULL, 0, sizeof(CProcProcessLocalData), - sizeof(CProcSharedData), + 0, PROCESS_ALL_ACCESS, CObjectType::SecuritySupported, CObjectType::SecurityInfoNotPersisted, @@ -90,6 +73,12 @@ CObjectType CorUnix::otProcess( CObjectType::NoOwner ); +static +DWORD +PALAPI +StartupHelperThread( + LPVOID p); + // // Helper memory page used by the FlushProcessWriteBuffers // @@ -131,8 +120,8 @@ Volatile terminator = 0; // Process ID of this process. DWORD gPID = (DWORD) -1; -// Function to call during PAL/process shutdown -PSHUTDOWN_CALLBACK g_shutdownCallback = nullptr; +// Function to call during PAL/process shutdown/abort +Volatile g_shutdownCallback = nullptr; // // Key used for associating CPalThread's with the underlying pthread @@ -170,7 +159,8 @@ static int checkFileType(char *lpFileName); static BOOL PROCEndProcess(HANDLE hProcess, UINT uExitCode, BOOL bTerminateUnconditionally); -ProcessModules *CreateProcessModules(IN HANDLE hProcess, OUT LPDWORD lpCount); +ProcessModules *GetProcessModulesFromHandle(IN HANDLE hProcess, OUT LPDWORD lpCount); +ProcessModules *CreateProcessModules(IN DWORD dwProcessId, OUT LPDWORD lpCount); void DestroyProcessModules(IN ProcessModules *listHead); /*++ @@ -522,7 +512,6 @@ PrepareStandardHandleExit: return palError; } - PAL_ERROR CorUnix::InternalCreateProcess( CPalThread *pThread, @@ -544,7 +533,6 @@ CorUnix::InternalCreateProcess( IDataLock *pLocalDataLock = NULL; CProcProcessLocalData *pLocalData; IDataLock *pSharedDataLock = NULL; - CProcSharedData *pSharedData; CPalThread *pDummyThread = NULL; HANDLE hDummyThread = NULL; HANDLE hProcess = NULL; @@ -844,23 +832,6 @@ CorUnix::InternalCreateProcess( child_blocking_pipe = pipe_descs[0]; } - // - // Get the data pointers for the new process object - // - - palError = pobjProcessRegistered->GetSharedData( - pThread, - WriteLock, - &pSharedDataLock, - reinterpret_cast(&pSharedData) - ); - - if (NO_ERROR != palError) - { - ASSERT("Unable to obtain shared data for new process object\n"); - goto InternalCreateProcessExit; - } - palError = pobjProcessRegistered->GetProcessLocalData( pThread, WriteLock, @@ -1019,10 +990,6 @@ CorUnix::InternalCreateProcess( pLocalDataLock->ReleaseLock(pThread, TRUE); pLocalDataLock = NULL; - pSharedData->dwProcessId = processId; - pSharedDataLock->ReleaseLock(pThread, TRUE); - pSharedDataLock = NULL; - // // Release file handle info; we don't need them anymore. Note that // this must happen after we've released the data locks, as @@ -1376,16 +1343,12 @@ static BOOL PROCEndProcess(HANDLE hProcess, UINT uExitCode, BOOL bTerminateUncon // (1) it doesn't run atexit handlers // (2) can invoke CrashReporter or produce a coredump, // which is appropriate for TerminateProcess calls - - // If this turns out to be inappropriate for some case, where we - // call TerminateProcess vs. ExitProcess, then there needs to be - // a CLR wrapper for TerminateProcess and some exposure for PAL_abort() - // to selectively use that in all but those cases. - abort(); } - else + else + { exit(uExitCode); + } ASSERT(FALSE); // we shouldn't get here } @@ -1413,29 +1376,428 @@ PAL_SetShutdownCallback( g_shutdownCallback = callback; } +#define RuntimeStartupSemaphoreName "/RuntimeStartupEvent%08x" +#define RuntimeContinueSemaphoreName "/RuntimeContinueEvent%08x" + +static bool IsCoreClrModule(const char* pModulePath) +{ + // Strip off everything up to and including the last slash in the path to get name + const char* pModuleName = pModulePath; + while (strchr(pModuleName, '/') != NULL) + { + pModuleName = strchr(pModuleName, '/'); + pModuleName++; // pass the slash + } + + return _stricmp(pModuleName, MAKEDLLNAME_A("coreclr")) == 0; +} + +class PAL_RuntimeStartupHelper +{ + LONG m_ref; + bool m_canceled; + DWORD m_processId; + PPAL_STARTUP_CALLBACK m_callback; + PVOID m_parameter; + DWORD m_threadId; + HANDLE m_threadHandle; + + // Debugger waits on this semaphore and the runtime signals it on startup. + sem_t *m_startupSem; + + // Debuggee waits on this semaphore and the debugger signals it after the callback returns. + sem_t *m_continueSem; + +public: + PAL_RuntimeStartupHelper(DWORD dwProcessId, PPAL_STARTUP_CALLBACK pfnCallback, PVOID parameter) : + m_ref(1), + m_canceled(false), + m_processId(dwProcessId), + m_callback(pfnCallback), + m_parameter(parameter), + m_threadId(0), + m_threadHandle(NULL), + m_startupSem(SEM_FAILED), + m_continueSem(SEM_FAILED) + { + } + + ~PAL_RuntimeStartupHelper() + { + if (m_startupSem != SEM_FAILED) + { + char startupSemName[NAME_MAX - 4]; + sprintf_s(startupSemName, sizeof(startupSemName), RuntimeStartupSemaphoreName, m_processId); + + sem_close(m_startupSem); + sem_unlink(startupSemName); + } + if (m_continueSem != SEM_FAILED) + { + char continueSemName[NAME_MAX - 4]; + sprintf_s(continueSemName, sizeof(continueSemName), RuntimeContinueSemaphoreName, m_processId); + + sem_close(m_continueSem); + sem_unlink(continueSemName); + } + if (m_threadHandle != NULL) + { + CloseHandle(m_threadHandle); + } + } + + LONG AddRef() + { + LONG ref = InterlockedIncrement(&m_ref); + return ref; + } + + LONG Release() + { + LONG ref = InterlockedDecrement(&m_ref); + if (ref == 0) + { + delete this; + } + return ref; + } + + PAL_ERROR Register() + { + CPalThread *pThread = InternalGetCurrentThread(); + char startupSemName[NAME_MAX - 4]; + char continueSemName[NAME_MAX - 4]; + PAL_ERROR pe = NO_ERROR; + + sprintf_s(startupSemName, sizeof(startupSemName), RuntimeStartupSemaphoreName, m_processId); + sprintf_s(continueSemName, sizeof(continueSemName), RuntimeContinueSemaphoreName, m_processId); + + TRACE("PAL_RuntimeStartupHelper.Register startup '%s' continue '%s'\n", startupSemName, 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 | O_RDWR, S_IRWXU | S_IRWXG | S_IRWXO, 0); + if (m_continueSem == SEM_FAILED) + { + TRACE("sem_open(continue) failed: errno is %d (%s)\n", errno, strerror(errno)); + pe = ERROR_INVALID_PARAMETER; + goto exit; + } + + // 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 | O_RDWR, S_IRWXU | S_IRWXG | S_IRWXO, 0); + if (m_startupSem == SEM_FAILED) + { + TRACE("sem_open(startup) failed: errno is %d (%s)\n", errno, strerror(errno)); + pe = ERROR_INVALID_PARAMETER; + goto exit; + } + + // Add a reference for the thread handler + AddRef(); + + pe = InternalCreateThread( + pThread, + NULL, + 0, + ::StartupHelperThread, + this, + 0, + UserCreatedThread, + &m_threadId, + &m_threadHandle); + + if (NO_ERROR != pe) + { + TRACE("InternalCreateThread failed %d\n", pe); + Release(); + goto exit; + } + + exit: + return pe; + } + + void Unregister() + { + m_canceled = true; + + // Tell the runtime to continue + if (sem_post(m_continueSem) != 0) + { + ASSERT("sem_post(continueSem) failed: errno is %d (%s)\n", errno, strerror(errno)); + } + + // Tell the worker thread to continue + if (sem_post(m_startupSem) != 0) + { + ASSERT("sem_post(startupSem) failed: errno is %d (%s)\n", errno, strerror(errno)); + } + + // Don't need to wait for the worker thread if unregister called on it + if (m_threadId != (DWORD)THREADSilentGetCurrentThreadId()) + { + // Wait for work thread to exit + if (WaitForSingleObject(m_threadHandle, INFINITE) != WAIT_OBJECT_0) + { + ASSERT("WaitForSingleObject\n"); + } + } + } + + PAL_ERROR InvokeStartupCallback(bool *pCoreClrExists) + { + PAL_ERROR pe = NO_ERROR; + + *pCoreClrExists = false; + + // Enumerate all the modules in the process and invoke the callback + // for the coreclr module if found. + DWORD count; + ProcessModules *listHead = CreateProcessModules(m_processId, &count); + if (listHead == NULL) + { + TRACE("CreateProcessModules failed for pid %d\n", m_processId); + pe = ERROR_INVALID_PARAMETER; + goto exit; + } + + for (ProcessModules *entry = listHead; entry != NULL; entry = entry->Next) + { + if (IsCoreClrModule(entry->Name)) + { + *pCoreClrExists = true; + + PAL_CPP_TRY + { + TRACE("InvokeStartupCallback executing callback %s\n", entry->Name); + m_callback(entry->Name, entry->BaseAddress, m_parameter); + } + PAL_CPP_CATCH_ALL + { + } + PAL_CPP_ENDTRY + + // Currently only the first coreclr module in a process is supported + break; + } + } + + exit: + if (*pCoreClrExists) + { + // Wake up all the runtimes + if (sem_post(m_continueSem) != 0) + { + ASSERT("sem_post(continueSem) failed: errno is %d (%s)\n", errno, strerror(errno)); + } + } + + if (listHead != NULL) + { + DestroyProcessModules(listHead); + } + return pe; + } + + void StartupHelperThread() + { + bool coreclrExists = false; + + PAL_ERROR pe = InvokeStartupCallback(&coreclrExists); + if (pe == NO_ERROR) + { + if (!coreclrExists && !m_canceled) + { + // Wait until the coreclr runtime (debuggee) starts up + if (sem_wait(m_startupSem) == 0) + { + if (!m_canceled) + { + pe = InvokeStartupCallback(&coreclrExists); + if (pe == NO_ERROR) + { + // We should always find a coreclr module + _ASSERTE(coreclrExists); + } + } + } + else + { + TRACE("sem_wait(startup) failed: errno is %d (%s)\n", errno, strerror(errno)); + pe = ERROR_INVALID_HANDLE; + } + } + } + + if (pe != NO_ERROR) + { + SetLastError(pe); + m_callback(NULL, NULL, m_parameter); + } + } +}; + +static +DWORD +PALAPI +StartupHelperThread(LPVOID p) +{ + TRACE("PAL's StartupHelperThread starting\n"); + + PAL_RuntimeStartupHelper *helper = (PAL_RuntimeStartupHelper *)p; + helper->StartupHelperThread(); + helper->Release(); + return 0; +} + /*++ -Function: - PROCCleanupProcess - - Do all cleanup work for TerminateProcess, but don't terminate the process. - If bTerminateUnconditionally is TRUE, we exit as quickly as possible. + PAL_RegisterForRuntimeStartup -(no return value) +Parameters: + dwProcessId - process id of runtime process + pfnCallback - function to callback for coreclr module found + parameter - data to pass to callback + ppUnregisterToken - pointer to put PAL_UnregisterForRuntimeStartup token. + +Return value: + PAL_ERROR + +Note: + If the modulePath or hModule is NULL when the callback is invoked, an error occured + and GetLastError() will return the Win32 error code. + + The callback is always invoked on a separate thread and this API returns immediately. + + Only the first coreclr module is currently supported. + +--*/ +DWORD +PALAPI +PAL_RegisterForRuntimeStartup( + IN DWORD dwProcessId, + IN PPAL_STARTUP_CALLBACK pfnCallback, + IN PVOID parameter, + OUT PVOID *ppUnregisterToken) +{ + _ASSERTE(pfnCallback != NULL); + _ASSERTE(ppUnregisterToken != NULL); + + PAL_RuntimeStartupHelper *helper = new PAL_RuntimeStartupHelper(dwProcessId, pfnCallback, parameter); + + // Create the debuggee startup semaphore so the runtime (debuggee) knows to wait for + // a debugger connection. + PAL_ERROR pe = helper->Register(); + if (NO_ERROR != pe) + { + helper->Release(); + helper = NULL; + } + + *ppUnregisterToken = helper; + return pe; +} + +/*++ + PAL_UnregisterForRuntimeStartup + + Stops/cancels startup notification. This API can be called in the startup callback. Otherwise, + it will block until the callback thread finishes and no more callbacks will be initiated after + this API returns. + +Parameters: + dwUnregisterToken - token from PAL_RegisterForRuntimeStartup or NULL. + +Return value: + PAL_ERROR +--*/ +DWORD +PALAPI +PAL_UnregisterForRuntimeStartup( + IN PVOID pUnregisterToken) +{ + if (pUnregisterToken != NULL) + { + PAL_RuntimeStartupHelper *helper = (PAL_RuntimeStartupHelper *)pUnregisterToken; + helper->Unregister(); + helper->Release(); + } + return NO_ERROR; +} + +/*++ + PAL_NotifyRuntimeStarted + + Signals the debugger waiting for runtime startup notification to continue and + waits until the debugger signals us to continue. + +Parameters: + None + +Return value: + TRUE - succeeded, FALSE - failed --*/ -void PROCCleanupProcess(BOOL bTerminateUnconditionally) +BOOL +PALAPI +PAL_NotifyRuntimeStarted() { - if (g_shutdownCallback != nullptr) + char szStartupSemName[NAME_MAX - 4]; + char szContinueSemName[NAME_MAX - 4]; + sem_t *startupSem = SEM_FAILED; + sem_t *continueSem = SEM_FAILED; + BOOL result = TRUE; + + sprintf_s(szStartupSemName, sizeof(szStartupSemName), RuntimeStartupSemaphoreName, gPID); + sprintf_s(szContinueSemName, sizeof(szContinueSemName), RuntimeContinueSemaphoreName, gPID); + + TRACE("PAL_NotifyRuntimeStarted opening startup '%s' continue '%s'\n", szStartupSemName, szContinueSemName); + + // Open the debugger startup semaphore. If it doesn't exists, then we do nothing and + // the function is successful. + startupSem = sem_open(szStartupSemName, O_RDWR); + if (startupSem == SEM_FAILED) { - g_shutdownCallback(); + TRACE("sem_open(%s) failed: %d (%s)\n", szStartupSemName, errno, strerror(errno)); + goto exit; } - /* Declare the beginning of shutdown */ - PALSetShutdownIntent(); + // Open the debuggee continue semaphore. If we can open the startup sem and not this one + // something is seriously wrong. + continueSem = sem_open(szContinueSemName, O_RDWR); + if (continueSem == SEM_FAILED) + { + ASSERT("sem_open(%s) failed: %d (%s)\n", szContinueSemName, errno, strerror(errno)); + result = FALSE; + goto exit; + } - PALCommonCleanup(); + // Wake up the debugger waiting for startup + if (sem_post(startupSem) != 0) + { + ASSERT("sem_post(startupSem) failed: errno is %d (%s)\n", errno, strerror(errno)); + result = FALSE; + goto exit; + } + + // Now wait until the debugger notification is finished + if (sem_wait(continueSem) != 0) + { + ASSERT("sem_wait(continueSem) failed: errno is %d (%s)\n", errno, strerror(errno)); + result = FALSE; + goto exit; + } - /* This must be called after PALCommonCleanup */ - PALShutdown(); +exit: + if (startupSem != SEM_FAILED) + { + sem_close(startupSem); + } + if (continueSem != SEM_FAILED) + { + sem_close(continueSem); + } + return result; } /*++ @@ -1688,7 +2050,6 @@ OpenProcess( IPalObject *pobjProcessRegistered = NULL; IDataLock *pDataLock; CProcProcessLocalData *pLocalData; - CProcSharedData *pSharedData; CObjectAttributes oa; HANDLE hProcess = NULL; @@ -1732,21 +2093,6 @@ OpenProcess( pLocalData->dwProcessId = dwProcessId; pDataLock->ReleaseLock(pThread, TRUE); - palError = pobjProcess->GetSharedData( - pThread, - WriteLock, - &pDataLock, - reinterpret_cast(&pSharedData) - ); - - if (NO_ERROR != palError) - { - goto OpenProcessExit; - } - - pSharedData->dwProcessId = dwProcessId; - pDataLock->ReleaseLock(pThread, TRUE); - palError = g_pObjectManager->RegisterObject( pThread, pobjProcess, @@ -1818,8 +2164,7 @@ EnumProcessModules( BOOL result = TRUE; DWORD count = 0; - - ProcessModules *listHead = CreateProcessModules(hProcess, &count); + ProcessModules *listHead = GetProcessModulesFromHandle(hProcess, &count); if (listHead != NULL) { for (ProcessModules *entry = listHead; entry != NULL; entry = entry->Next) @@ -1870,7 +2215,7 @@ GetModuleFileNameExW( DWORD result = 0; DWORD count = 0; - ProcessModules *listHead = CreateProcessModules(hProcess, &count); + ProcessModules *listHead = GetProcessModulesFromHandle(hProcess, &count); if (listHead != NULL) { for (ProcessModules *entry = listHead; entry != NULL; entry = entry->Next) @@ -1889,7 +2234,7 @@ GetModuleFileNameExW( /*++ Function: - CreateProcessModules + GetProcessModulesFromHandle Abstract Returns a process's module list @@ -1899,7 +2244,7 @@ Return --*/ ProcessModules * -CreateProcessModules( +GetProcessModulesFromHandle( IN HANDLE hProcess, OUT LPDWORD lpCount) { @@ -1910,10 +2255,13 @@ CreateProcessModules( IDataLock *pDataLock = NULL; PAL_ERROR palError = NO_ERROR; DWORD dwProcessId = 0; + DWORD count = 0; + + _ASSERTE(lpCount != NULL); if (hPseudoCurrentProcess == hProcess) { - dwProcessId = gPID; + pobjProcess = g_pobjProcess; } else { @@ -1924,83 +2272,205 @@ CreateProcessModules( hProcess, &aotProcess, 0, - &pobjProcess - ); + &pobjProcess); if (NO_ERROR != palError) { + pThread->SetLastError(ERROR_INVALID_HANDLE); goto exit; } + } - palError = pobjProcess->GetProcessLocalData( - pThread, - WriteLock, - &pDataLock, - reinterpret_cast(&pLocalData) - ); + palError = pobjProcess->GetProcessLocalData( + pThread, + WriteLock, + &pDataLock, + reinterpret_cast(&pLocalData)); - if (NO_ERROR != palError) + _ASSERTE(NO_ERROR == palError); + + dwProcessId = pLocalData->dwProcessId; + listHead = pLocalData->pProcessModules; + count = pLocalData->cProcessModules; + + // If the module list hasn't been created yet, create it now + if (listHead == NULL) + { + listHead = CreateProcessModules(dwProcessId, &count); + if (listHead == NULL) { + pThread->SetLastError(ERROR_INVALID_PARAMETER); goto exit; } - dwProcessId = pLocalData->dwProcessId; - listHead = pLocalData->pProcessModules; + if (pLocalData != NULL) + { + pLocalData->pProcessModules = listHead; + pLocalData->cProcessModules = count; + } } - // If the module list hasn't been created yet, create it now - if (listHead == NULL) +exit: + if (NULL != pDataLock) { + pDataLock->ReleaseLock(pThread, TRUE); + } + if (NULL != pobjProcess) + { + pobjProcess->ReleaseReference(pThread); + } + + *lpCount = count; + return listHead; +} + +/*++ +Function: + CreateProcessModules + +Abstract + Returns a process's module list + +Return + ProcessModules * list + +--*/ +ProcessModules * +CreateProcessModules( + IN DWORD dwProcessId, + OUT LPDWORD lpCount) +{ + ProcessModules *listHead = NULL; + _ASSERTE(lpCount != NULL); + #if defined(__APPLE__) - // For OSx, the "vmmap" command outputs something similar to the /proc/*/maps file so popen the - // command and read the relevant lines: - // - // ... - // ==== regions for process 347 (non-writable and writable regions are interleaved) - // REGION TYPE START - END [ VSIZE] PRT/MAX SHRMOD REGION DETAIL - // __TEXT 000000010446d000-0000000104475000 [ 32K] r-x/rwx SM=COW /Users/mikem/coreclr/bin/Product/OSx.x64.Debug/corerun - // __DATA 0000000104475000-0000000104476000 [ 4K] rw-/rwx SM=PRV /Users/mikem/coreclr/bin/Product/OSx.x64.Debug/corerun - // __LINKEDIT 0000000104476000-000000010447a000 [ 16K] r--/rwx SM=COW /Users/mikem/coreclr/bin/Product/OSx.x64.Debug/corerun - // Kernel Alloc Once 000000010447a000-000000010447b000 [ 4K] rw-/rwx SM=PRV - // MALLOC (admin) 000000010447b000-000000010447c000 [ 4K] r--/rwx SM=ZER - // ... - // MALLOC (admin) 00000001044ab000-00000001044ac000 [ 4K] r--/rwx SM=PRV - // __TEXT 00000001044ac000-0000000104c84000 [ 8032K] r-x/rwx SM=COW /Users/mikem/coreclr/bin/Product/OSx.x64.Debug/libcoreclr.dylib - // __TEXT 0000000104c84000-0000000104c85000 [ 4K] rwx/rwx SM=PRV /Users/mikem/coreclr/bin/Product/OSx.x64.Debug/libcoreclr.dylib - // __TEXT 0000000104c85000-000000010513b000 [ 4824K] r-x/rwx SM=COW /Users/mikem/coreclr/bin/Product/OSx.x64.Debug/libcoreclr.dylib - // __TEXT 000000010513b000-000000010513c000 [ 4K] rwx/rwx SM=PRV /Users/mikem/coreclr/bin/Product/OSx.x64.Debug/libcoreclr.dylib - // __TEXT 000000010513c000-000000010516f000 [ 204K] r-x/rwx SM=COW /Users/mikem/coreclr/bin/Product/OSx.x64.Debug/libcoreclr.dylib - // __DATA 000000010516f000-00000001051ce000 [ 380K] rw-/rwx SM=COW /Users/mikem/coreclr/bin/Product/OSx.x64.Debug/libcoreclr.dylib - // __DATA 00000001051ce000-00000001051fa000 [ 176K] rw-/rwx SM=PRV /Users/mikem/coreclr/bin/Product/OSx.x64.Debug/libcoreclr.dylib - // __LINKEDIT 00000001051fa000-0000000105bac000 [ 9928K] r--/rwx SM=COW /Users/mikem/coreclr/bin/Product/OSx.x64.Debug/libcoreclr.dylib - // VM_ALLOCATE 0000000105bac000-0000000105bad000 [ 4K] r--/rw- SM=SHM - // MALLOC (admin) 0000000105bad000-0000000105bae000 [ 4K] r--/rwx SM=ZER - // MALLOC 0000000105bae000-0000000105baf000 [ 4K] rw-/rwx SM=ZER - char *line = NULL; - size_t lineLen = 0; - int count = 0; - ssize_t read; - - char vmmapCommand[100]; - int chars = snprintf(vmmapCommand, sizeof(vmmapCommand), "/usr/bin/vmmap -interleaved %d", dwProcessId); - _ASSERTE(chars > 0 && chars <= sizeof(vmmapCommand)); - - FILE *vmmapFile = popen(vmmapCommand, "r"); - if (vmmapFile == NULL) - { - SetLastError(ERROR_INVALID_HANDLE); - return NULL; + // For OSx, the "vmmap" command outputs something similar to the /proc/*/maps file so popen the + // command and read the relevant lines: + // + // ... + // ==== regions for process 347 (non-writable and writable regions are interleaved) + // REGION TYPE START - END [ VSIZE] PRT/MAX SHRMOD REGION DETAIL + // __TEXT 000000010446d000-0000000104475000 [ 32K] r-x/rwx SM=COW /Users/mikem/coreclr/bin/Product/OSx.x64.Debug/corerun + // __DATA 0000000104475000-0000000104476000 [ 4K] rw-/rwx SM=PRV /Users/mikem/coreclr/bin/Product/OSx.x64.Debug/corerun + // __LINKEDIT 0000000104476000-000000010447a000 [ 16K] r--/rwx SM=COW /Users/mikem/coreclr/bin/Product/OSx.x64.Debug/corerun + // Kernel Alloc Once 000000010447a000-000000010447b000 [ 4K] rw-/rwx SM=PRV + // MALLOC (admin) 000000010447b000-000000010447c000 [ 4K] r--/rwx SM=ZER + // ... + // MALLOC (admin) 00000001044ab000-00000001044ac000 [ 4K] r--/rwx SM=PRV + // __TEXT 00000001044ac000-0000000104c84000 [ 8032K] r-x/rwx SM=COW /Users/mikem/coreclr/bin/Product/OSx.x64.Debug/libcoreclr.dylib + // __TEXT 0000000104c84000-0000000104c85000 [ 4K] rwx/rwx SM=PRV /Users/mikem/coreclr/bin/Product/OSx.x64.Debug/libcoreclr.dylib + // __TEXT 0000000104c85000-000000010513b000 [ 4824K] r-x/rwx SM=COW /Users/mikem/coreclr/bin/Product/OSx.x64.Debug/libcoreclr.dylib + // __TEXT 000000010513b000-000000010513c000 [ 4K] rwx/rwx SM=PRV /Users/mikem/coreclr/bin/Product/OSx.x64.Debug/libcoreclr.dylib + // __TEXT 000000010513c000-000000010516f000 [ 204K] r-x/rwx SM=COW /Users/mikem/coreclr/bin/Product/OSx.x64.Debug/libcoreclr.dylib + // __DATA 000000010516f000-00000001051ce000 [ 380K] rw-/rwx SM=COW /Users/mikem/coreclr/bin/Product/OSx.x64.Debug/libcoreclr.dylib + // __DATA 00000001051ce000-00000001051fa000 [ 176K] rw-/rwx SM=PRV /Users/mikem/coreclr/bin/Product/OSx.x64.Debug/libcoreclr.dylib + // __LINKEDIT 00000001051fa000-0000000105bac000 [ 9928K] r--/rwx SM=COW /Users/mikem/coreclr/bin/Product/OSx.x64.Debug/libcoreclr.dylib + // VM_ALLOCATE 0000000105bac000-0000000105bad000 [ 4K] r--/rw- SM=SHM + // MALLOC (admin) 0000000105bad000-0000000105bae000 [ 4K] r--/rwx SM=ZER + // MALLOC 0000000105bae000-0000000105baf000 [ 4K] rw-/rwx SM=ZER + char *line = NULL; + size_t lineLen = 0; + int count = 0; + ssize_t read; + + char vmmapCommand[100]; + int chars = snprintf(vmmapCommand, sizeof(vmmapCommand), "/usr/bin/vmmap -interleaved %d", dwProcessId); + _ASSERTE(chars > 0 && chars <= sizeof(vmmapCommand)); + + FILE *vmmapFile = popen(vmmapCommand, "r"); + if (vmmapFile == NULL) + { + goto exit; + } + + // Reading maps file line by line + while ((read = getline(&line, &lineLen, vmmapFile)) != -1) + { + void *startAddress, *endAddress; + char moduleName[PATH_MAX]; + int size; + + if (sscanf(line, "__TEXT %p-%p [ %dK] %*[-/rwxsp] SM=%*[A-Z] %s\n", &startAddress, &endAddress, &size, moduleName) == 4) + { + bool dup = false; + for (ProcessModules *entry = listHead; entry != NULL; entry = entry->Next) + { + if (strcmp(moduleName, entry->Name) == 0) + { + dup = true; + break; + } + } + + if (!dup) + { + int cbModuleName = strlen(moduleName) + 1; + ProcessModules *entry = (ProcessModules *)InternalMalloc(sizeof(ProcessModules) + cbModuleName); + if (entry == NULL) + { + DestroyProcessModules(listHead); + listHead = NULL; + count = 0; + break; + } + strcpy_s(entry->Name, cbModuleName, moduleName); + entry->BaseAddress = startAddress; + entry->Next = listHead; + listHead = entry; + count++; + } } + } - // Reading maps file line by line - while ((read = getline(&line, &lineLen, vmmapFile)) != -1) - { - void *startAddress, *endAddress; - char moduleName[PATH_MAX]; - int size; + *lpCount = count; + + free(line); // We didn't allocate line, but as per contract of getline we should free it + pclose(vmmapFile); + +#elif defined(HAVE_PROCFS_CTL) - if (sscanf(line, "__TEXT %p-%p [ %dK] %*[-/rwxsp] SM=%*[A-Z] %s\n", &startAddress, &endAddress, &size, moduleName) == 4) + // Here we read /proc//maps file in order to parse it and figure out what it says + // about a library we are looking for. This file looks something like this: + // + // [address] [perms] [offset] [dev] [inode] [pathname] - HEADER is not preset in an actual file + // + // 35b1800000-35b1820000 r-xp 00000000 08:02 135522 /usr/lib64/ld-2.15.so + // 35b1a1f000-35b1a20000 r--p 0001f000 08:02 135522 /usr/lib64/ld-2.15.so + // 35b1a20000-35b1a21000 rw-p 00020000 08:02 135522 /usr/lib64/ld-2.15.so + // 35b1a21000-35b1a22000 rw-p 00000000 00:00 0 [heap] + // 35b1c00000-35b1dac000 r-xp 00000000 08:02 135870 /usr/lib64/libc-2.15.so + // 35b1dac000-35b1fac000 ---p 001ac000 08:02 135870 /usr/lib64/libc-2.15.so + // 35b1fac000-35b1fb0000 r--p 001ac000 08:02 135870 /usr/lib64/libc-2.15.so + // 35b1fb0000-35b1fb2000 rw-p 001b0000 08:02 135870 /usr/lib64/libc-2.15.so + + // Making something like: /proc/123/maps + char mapFileName[100]; + char *line = NULL; + size_t lineLen = 0; + int count = 0; + ssize_t read; + + INDEBUG(int chars = ) + snprintf(mapFileName, sizeof(mapFileName), "/proc/%d/maps", dwProcessId); + _ASSERTE(chars > 0 && chars <= sizeof(mapFileName)); + + FILE *mapsFile = fopen(mapFileName, "r"); + if (mapsFile == NULL) + { + goto exit; + } + + // Reading maps file line by line + while ((read = getline(&line, &lineLen, mapsFile)) != -1) + { + void *startAddress, *endAddress, *offset; + int devHi, devLo, inode; + char moduleName[PATH_MAX]; + + if (sscanf(line, "%p-%p %*[-rwxsp] %p %x:%x %d %s\n", &startAddress, &endAddress, &offset, &devHi, &devLo, &inode, moduleName) == 7) + { + if (inode != 0) { bool dup = false; for (ProcessModules *entry = listHead; entry != NULL; entry = entry->Next) @@ -2018,7 +2488,6 @@ CreateProcessModules( ProcessModules *entry = (ProcessModules *)InternalMalloc(sizeof(ProcessModules) + cbModuleName); if (entry == NULL) { - SetLastError(ERROR_NOT_ENOUGH_MEMORY); DestroyProcessModules(listHead); listHead = NULL; count = 0; @@ -2032,112 +2501,16 @@ CreateProcessModules( } } } + } - *lpCount = count; - - free(line); // We didn't allocate line, but as per contract of getline we should free it - pclose(vmmapFile); - -#elif defined(HAVE_PROCFS_CTL) - - // Here we read /proc//maps file in order to parse it and figure out what it says - // about a library we are looking for. This file looks something like this: - // - // [address] [perms] [offset] [dev] [inode] [pathname] - HEADER is not preset in an actual file - // - // 35b1800000-35b1820000 r-xp 00000000 08:02 135522 /usr/lib64/ld-2.15.so - // 35b1a1f000-35b1a20000 r--p 0001f000 08:02 135522 /usr/lib64/ld-2.15.so - // 35b1a20000-35b1a21000 rw-p 00020000 08:02 135522 /usr/lib64/ld-2.15.so - // 35b1a21000-35b1a22000 rw-p 00000000 00:00 0 [heap] - // 35b1c00000-35b1dac000 r-xp 00000000 08:02 135870 /usr/lib64/libc-2.15.so - // 35b1dac000-35b1fac000 ---p 001ac000 08:02 135870 /usr/lib64/libc-2.15.so - // 35b1fac000-35b1fb0000 r--p 001ac000 08:02 135870 /usr/lib64/libc-2.15.so - // 35b1fb0000-35b1fb2000 rw-p 001b0000 08:02 135870 /usr/lib64/libc-2.15.so - - // Making something like: /proc/123/maps - char mapFileName[100]; - - INDEBUG(int chars = ) - snprintf(mapFileName, sizeof(mapFileName), "/proc/%d/maps", dwProcessId); - _ASSERTE(chars > 0 && chars <= sizeof(mapFileName)); - - FILE *mapsFile = fopen(mapFileName, "r"); - if (mapsFile == NULL) - { - SetLastError(ERROR_INVALID_HANDLE); - return NULL; - } - - char *line = NULL; - size_t lineLen = 0; - int count = 0; - ssize_t read; - - // Reading maps file line by line - while ((read = getline(&line, &lineLen, mapsFile)) != -1) - { - void *startAddress, *endAddress, *offset; - int devHi, devLo, inode; - char moduleName[PATH_MAX]; - - if (sscanf(line, "%p-%p %*[-rwxsp] %p %x:%x %d %s\n", &startAddress, &endAddress, &offset, &devHi, &devLo, &inode, moduleName) == 7) - { - if (inode != 0) - { - bool dup = false; - for (ProcessModules *entry = listHead; entry != NULL; entry = entry->Next) - { - if (strcmp(moduleName, entry->Name) == 0) - { - dup = true; - break; - } - } - - if (!dup) - { - int cbModuleName = strlen(moduleName) + 1; - ProcessModules *entry = (ProcessModules *)InternalMalloc(sizeof(ProcessModules) + cbModuleName); - if (entry == NULL) - { - SetLastError(ERROR_NOT_ENOUGH_MEMORY); - DestroyProcessModules(listHead); - listHead = NULL; - count = 0; - break; - } - strcpy_s(entry->Name, cbModuleName, moduleName); - entry->BaseAddress = startAddress; - entry->Next = listHead; - listHead = entry; - count++; - } - } - } - } - - *lpCount = count; + *lpCount = count; - free(line); // We didn't allocate line, but as per contract of getline we should free it - fclose(mapsFile); + free(line); // We didn't allocate line, but as per contract of getline we should free it + fclose(mapsFile); #else - _ASSERTE(!"Not implemented on this platform"); + _ASSERTE(!"Not implemented on this platform"); #endif - if (pLocalData != NULL) - { - pLocalData->pProcessModules = listHead; - } - } exit: - if (NULL != pDataLock) - { - pDataLock->ReleaseLock(pThread, TRUE); - } - - if (NULL != pobjProcess) - { - pobjProcess->ReleaseReference(pThread); - } return listHead; } @@ -2147,8 +2520,10 @@ Function: Abstract Cleans up the process module table. + Return - TRUE if it succeeded, FALSE otherwise + None + --*/ void DestroyProcessModules(IN ProcessModules *listHead) @@ -2163,6 +2538,44 @@ DestroyProcessModules(IN ProcessModules *listHead) /*++ Function: + PROCShutdownProcess + + Calls the abort handler to do any shutdown cleanup. Call be called + from the unhandled native exception handler. + +(no return value) +--*/ +__attribute__((destructor)) +void PROCShutdownProcess() +{ + TRACE("PROCShutdownProcess %p\n", g_shutdownCallback.RawValue()); + + PSHUTDOWN_CALLBACK callback = InterlockedExchangePointer(&g_shutdownCallback, NULL); + if (callback != NULL) + { + callback(); + } +} + +/*++ +Function: + PROCAbort() + + Aborts the process after calling the shutdown cleanup handler. This function + should be called instead of calling abort() directly. + + Does not return +--*/ +PAL_NORETURN +void +PROCAbort() +{ + PROCShutdownProcess(); + abort(); +} + +/*++ +Function: InitializeFlushProcessWriteBuffers Abstract @@ -2200,7 +2613,7 @@ BOOL InitializeFlushProcessWriteBuffers() if (!(e)) \ { \ fprintf(stderr, "FATAL ERROR: " msg); \ - abort(); \ + PROCAbort(); \ } \ } \ while(0) @@ -2417,7 +2830,6 @@ CorUnix::CreateInitialProcessAndThreadObjects( IPalObject *pobjProcess = NULL; IDataLock *pDataLock; CProcProcessLocalData *pLocalData; - CProcSharedData *pSharedData; CObjectAttributes oa; HANDLE hProcess; @@ -2471,22 +2883,6 @@ CorUnix::CreateInitialProcessAndThreadObjects( pLocalData->ps = PS_RUNNING; pDataLock->ReleaseLock(pThread, TRUE); - palError = pobjProcess->GetSharedData( - pThread, - WriteLock, - &pDataLock, - reinterpret_cast(&pSharedData) - ); - - if (NO_ERROR != palError) - { - ASSERT("Unable to access shared data"); - goto CreateInitialProcessAndThreadObjectsExit; - } - - pSharedData->dwProcessId = gPID; - pDataLock->ReleaseLock(pThread, TRUE); - palError = g_pObjectManager->RegisterObject( pThread, pobjProcess, @@ -2790,36 +3186,38 @@ CorUnix::TerminateCurrentProcessNoExit(BOOL bTerminateUnconditionally) if (0 != old_terminator && GetCurrentThreadId() != old_terminator) { - /* another thread has already initiated the termination process. we - could just block on the PALInitLock critical section, but then - PROCSuspendOtherThreads would hang... so sleep forever here, we're - terminating anyway + /* another thread has already initiated the termination process. we + could just block on the PALInitLock critical section, but then + PROCSuspendOtherThreads would hang... so sleep forever here, we're + terminating anyway - Update: [TODO] PROCSuspendOtherThreads has been removed. Can this - code be changed? */ - - /* note that if *this* thread has already started the termination - process, we want to proceed. the only way this can happen is if a - call to DllMain (from ExitProcess) brought us here (because DllMain - called ExitProcess, or TerminateProcess, or ExitThread); - TerminateProcess won't call DllMain, so there's no danger to get - caught in an infinite loop */ - WARN("termination already started from another thread; blocking.\n"); - poll(NULL, 0, INFTIM); - } - - /* Try to lock the initialization count to prevent multiple threads from - terminating/initializing the PAL simultaneously */ - - /* note : it's also important to take this lock before the process lock, - because Init/Shutdown take the init lock, and the functions they call - may take the process lock. We must do it in the same order to avoid - deadlocks */ - locked = PALInitLock(); - if(locked && PALIsInitialized()) - { - PROCCleanupProcess(bTerminateUnconditionally); - } + Update: [TODO] PROCSuspendOtherThreads has been removed. Can this + code be changed? */ + + /* note that if *this* thread has already started the termination + process, we want to proceed. the only way this can happen is if a + call to DllMain (from ExitProcess) brought us here (because DllMain + called ExitProcess, or TerminateProcess, or ExitThread); + TerminateProcess won't call DllMain, so there's no danger to get + caught in an infinite loop */ + WARN("termination already started from another thread; blocking.\n"); + poll(NULL, 0, INFTIM); + } + + /* Try to lock the initialization count to prevent multiple threads from + terminating/initializing the PAL simultaneously */ + + /* note : it's also important to take this lock before the process lock, + because Init/Shutdown take the init lock, and the functions they call + may take the process lock. We must do it in the same order to avoid + deadlocks */ + + locked = PALInitLock(); + if(locked && PALIsInitialized()) + { + PROCShutdownProcess(); + PALCommonCleanup(); + } } /*++ @@ -3007,39 +3405,6 @@ PROCGetProcessStatusExit: return palError; } -void -ProcessCleanupRoutine( - CPalThread *pThread, - IPalObject *pObjectToCleanup, - bool fShutdown, - bool fCleanupSharedState - ) -{ - // - // Nothing to do -- no allocated data - // -} - -PAL_ERROR -ProcessInitializationRoutine( - CPalThread *pThread, - CObjectType *pObjectType, - void *pImmutableData, - void *pSharedData, - void *pProcessLocalData - ) -{ - PAL_ERROR palError = NO_ERROR; - CProcProcessLocalData *pProcLocalData = - reinterpret_cast(pProcessLocalData); - CProcSharedData *pProcSharedData = - reinterpret_cast(pSharedData); - - pProcLocalData->dwProcessId = pProcSharedData->dwProcessId; - - return palError; -} - #ifdef _DEBUG void PROCDumpThreadList() { -- 2.7.4