summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/debug/di/dbgtransportmanager.cpp3
-rw-r--r--src/debug/ee/debugger.cpp282
-rw-r--r--src/debug/ee/debugger.h2
-rw-r--r--src/debug/inc/dbgtransportsession.h29
-rw-r--r--src/debug/inc/debug-pal.h16
-rw-r--r--src/debug/shared/dbgtransportsession.cpp69
-rw-r--r--src/dlls/dbgshim/dbgshim.cpp695
-rw-r--r--src/dlls/dbgshim/dbgshim.h72
-rw-r--r--src/dlls/dbgshim/dbgshim.ntdef2
-rw-r--r--src/inc/dacvars.h2
-rw-r--r--src/pal/inc/pal.h47
-rw-r--r--src/pal/inc/rt/palrt.h2
-rw-r--r--src/pal/src/debug/debug.cpp8
-rw-r--r--src/pal/src/exception/machexception.cpp24
-rw-r--r--src/pal/src/exception/signal.cpp68
-rw-r--r--src/pal/src/file/file.cpp25
-rw-r--r--src/pal/src/include/pal/dbgmsg.h2
-rw-r--r--src/pal/src/include/pal/init.h9
-rw-r--r--src/pal/src/include/pal/process.h22
-rw-r--r--src/pal/src/include/pal/procobj.hpp10
-rw-r--r--src/pal/src/init/pal.cpp34
-rw-r--r--src/pal/src/init/sxs.cpp3
-rw-r--r--src/pal/src/misc/dbgmsg.cpp1
-rw-r--r--src/pal/src/misc/miscpalapi.cpp3
-rw-r--r--src/pal/src/thread/process.cpp1029
25 files changed, 1734 insertions, 725 deletions
diff --git a/src/debug/di/dbgtransportmanager.cpp b/src/debug/di/dbgtransportmanager.cpp
index e9b619de18..c5f60f3a02 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 12a7473e8e..6279ed0499 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 5e4ff53562..d14c83c87a 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 3b38b87bb7..0c0b37e67d 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 90cf0c822e..0000000000
--- 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 01f4acbc66..5aa9f28e15 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 42a2d253f9..eeccfe8f2b 100644
--- a/src/dlls/dbgshim/dbgshim.cpp
+++ b/src/dlls/dbgshim/dbgshim.cpp
@@ -26,9 +26,7 @@
#include <getproductversionnumber.h>
#include <dbgenginemetrics.h>
-#if defined(FEATURE_PAL)
-#include "debug-pal.h"
-#else
+#ifndef FEATURE_PAL
#define PSAPI_VERSION 2
#include <psapi.h>
#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 4623e0d344..f706b89c9b 100644
--- a/src/dlls/dbgshim/dbgshim.h
+++ b/src/dlls/dbgshim/dbgshim.h
@@ -9,28 +9,52 @@
#include <windows.h>
-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 942bc220cf..cc62a40304 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 c45a65f491..6a0bcc2e11 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 b937917922..5324b3bdde 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 7426d5654f..9f2194ac2b 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 9a9fefcdb0..715ea7d641 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 4142e7c3e9..4cd19d7be9 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 ef7b569f69..aa70c22fff 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 352a0979b1..30a4c3bf34 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 bf16bc4b98..93172f7dc3 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 c482a0090b..6e58c0d08d 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 cd26349162..3f93df003c 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 8500fd1f09..866a3ae867 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 626f077112..ab00bfb76c 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 f568711344..f011047e67 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 4d345649b1..8d66254d82 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 15cb741684..c39fb7fbca 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 4e2a9c5571..3d9a9c4f25 100644
--- a/src/pal/src/thread/process.cpp
+++ b/src/pal/src/thread/process.cpp
@@ -49,36 +49,19 @@ Abstract:
#include <sys/time.h>
#include <sys/resource.h>
#include <debugmacrosext.h>
+#include <semaphore.h>
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<LONG> 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<PSHUTDOWN_CALLBACK> 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<void **>(&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<void **>(&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<void **>(&pLocalData)
- );
+ palError = pobjProcess->GetProcessLocalData(
+ pThread,
+ WriteLock,
+ &pDataLock,
+ reinterpret_cast<void **>(&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/<pid>/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/<pid>/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<void **>(&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<CProcProcessLocalData*>(pProcessLocalData);
- CProcSharedData *pProcSharedData =
- reinterpret_cast<CProcSharedData*>(pSharedData);
-
- pProcLocalData->dwProcessId = pProcSharedData->dwProcessId;
-
- return palError;
-}
-
#ifdef _DEBUG
void PROCDumpThreadList()
{