summaryrefslogtreecommitdiff
path: root/src/debug/di/rsthread.cpp
diff options
context:
space:
mode:
authorMike McLaughlin <mikem@microsoft.com>2018-02-26 15:19:00 -0800
committerGitHub <noreply@github.com>2018-02-26 15:19:00 -0800
commit759f0c84fcffa058e5d2ed9ba8a13e47fd28ee5c (patch)
treeb5b29228718c1145154fd7b60e8f674b522e7f80 /src/debug/di/rsthread.cpp
parent3cb7c002e1325a7614961fd10e9a9f989528370f (diff)
downloadcoreclr-759f0c84fcffa058e5d2ed9ba8a13e47fd28ee5c.tar.gz
coreclr-759f0c84fcffa058e5d2ed9ba8a13e47fd28ee5c.tar.bz2
coreclr-759f0c84fcffa058e5d2ed9ba8a13e47fd28ee5c.zip
Fixed mixed mode attach/JIT debugging. (#16552)
Fixed mixed mode attach/JIT debugging. The mixed mode debugging attach uses TLS slot to communicate between debugger break-in thread and the right side. Unfortunately, the __thread static variables cannot be used on debugger breakin thread because of it does not have storage allocated for them. The fix is to switch the storage for debugger word to classic TlsAlloc allocated slot that works fine on debugger break-in thread. There was also problem (that is also in 2.0) where the WINNT_OFFSETOF__TEB__ThreadLocalStoragePointer was using the define for 64/32 bit and ended up always the 32 bit Windows value. This caused the right side GetEEThreadValue, GetEETlsDataBlock unmanaged thread functions to always fail.
Diffstat (limited to 'src/debug/di/rsthread.cpp')
-rw-r--r--src/debug/di/rsthread.cpp322
1 files changed, 143 insertions, 179 deletions
diff --git a/src/debug/di/rsthread.cpp b/src/debug/di/rsthread.cpp
index cd5e62932a..fae679a55f 100644
--- a/src/debug/di/rsthread.cpp
+++ b/src/debug/di/rsthread.cpp
@@ -3031,36 +3031,126 @@ REMOTE_PTR CordbUnmanagedThread::GetPreDefTlsSlot(SIZE_T slot, bool * pRead)
return 0;
}
-// sets the value of gCurrentThreadInfo.m_pThread
-HRESULT CordbUnmanagedThread::SetEEThreadValue(REMOTE_PTR EETlsValue)
+// Read the contents from a LS threads's TLS slot.
+HRESULT CordbUnmanagedThread::GetTlsSlot(DWORD slot, REMOTE_PTR * pValue)
{
- FAIL_IF_NEUTERED(this);
+ // Compute the address of the necessary TLS value.
+ HRESULT hr = LoadTLSArrayPtr();
+ if (FAILED(hr))
+ {
+ return hr;
+ }
- HRESULT hr = S_OK;
- _ASSERTE(GetProcess()->ThreadHoldsProcessLock());
+ void * pBase = NULL;
+ SIZE_T slotAdjusted = slot;
- REMOTE_PTR EEThreadAddr = (BYTE*) GetClrModuleTlsDataAddress() + OFFSETOF__TLS__tls_CurrentThread;
- if(EEThreadAddr == NULL)
- return E_FAIL;
+ if (slot < TLS_MINIMUM_AVAILABLE)
+ {
+ pBase = m_pTLSArray;
+ }
+ else if (slot < TLS_MINIMUM_AVAILABLE + TLS_EXPANSION_SLOTS)
+ {
+ pBase = m_pTLSExtendedArray;
+ slotAdjusted -= TLS_MINIMUM_AVAILABLE;
- // Write the thread's TLS value.
- hr = GetProcess()->SafeWriteStruct(PTR_TO_CORDB_ADDRESS(EEThreadAddr), &EETlsValue);
+ // Expansion slot is lazily allocated. If we're trying to read from it, but hasn't been allocated,
+ // then the TLS slot is still the default value, which is 0 (NULL).
+ if (pBase == NULL)
+ {
+ *pValue = NULL;
+ return S_OK;
+ }
+ }
+ else
+ {
+ // Slot is out of range. Shouldn't happen unless debuggee is corrupted.
+ _ASSERTE(!"Invalid TLS slot");
+ return E_UNEXPECTED;
+ }
+ void *pEEThreadTLS = (BYTE*)pBase + (slotAdjusted * sizeof(void*));
+
+ // Read the thread's TLS value.
+ hr = GetProcess()->SafeReadStruct(PTR_TO_CORDB_ADDRESS(pEEThreadTLS), pValue);
if (FAILED(hr))
{
- LOG((LF_CORDB, LL_INFO1000, "CUT::SEETV: failed to set TLS value: "
- "computed addr=0x%p index=%d, err=%x\n",
- EEThreadAddr, GetProcess()->m_runtimeOffsets.m_TLSIndex, hr));
+ LOG((LF_CORDB, LL_INFO1000, "CUT::GTS: failed to read TLS value: computed addr=0x%p index=%d, err=%x\n",
+ pEEThreadTLS, slot, hr));
+ return hr;
+ }
+ LOG((LF_CORDB, LL_INFO1000000, "CUT::GTS: EE Thread TLS value is 0x%p for thread 0x%x, slot 0x%x\n", *pValue, m_id, slot));
+ return S_OK;
+}
+
+// This does a WriteProcessMemory to write to the debuggee's TLS slot
+//
+// Notes:
+// This is very brittle because the OS can lazily allocates storage for TLS slots.
+// In order to gaurantee the storage is available, it must have been written to by the debuggee.
+// For managed threads, that's easy because the Thread* is already written to the slot.
+// But for pure native threads where GetThread() == NULL, the storage may not yet be allocated.
+//
+// The saving grace is that the debuggee's hijack filters will force the TLS to be allocated before it
+// sends a flare.
+//
+// Therefore, this function can only be called:
+// 1) on a managed thread
+// 2) on a native thread after that thread has been hijacked and sent a flare.
+//
+// This is brittle reasoning, but so is the rest of interop-debugging.
+//
+HRESULT CordbUnmanagedThread::SetTlsSlot(DWORD slot, REMOTE_PTR value)
+{
+ FAIL_IF_NEUTERED(this);
+
+ // Compute the address of the necessary TLS value.
+ HRESULT hr = LoadTLSArrayPtr();
+ if (FAILED(hr))
+ {
return hr;
}
- LOG((LF_CORDB, LL_INFO1000000,
- "CUT::SEETV: EE Thread TLS value is now 0x%p for thread 0x%x\n",
- EETlsValue, m_id));
+ void * pBase = NULL;
+ SIZE_T slotAdjusted = slot;
+ if (slot < TLS_MINIMUM_AVAILABLE)
+ {
+ pBase = m_pTLSArray;
+ }
+ else if (slot < TLS_MINIMUM_AVAILABLE + TLS_EXPANSION_SLOTS)
+ {
+ pBase = m_pTLSExtendedArray;
+ slotAdjusted -= TLS_MINIMUM_AVAILABLE;
- return S_OK;
+ // Expansion slot is lazily allocated. If we're trying to read from it, but hasn't been allocated,
+ // then the TLS slot is still the default value, which is 0.
+ if (pBase == NULL)
+ {
+ // See reasoning in header for why this should succeed.
+ _ASSERTE(!"Can't set to expansion slots because they haven't been allocated");
+ return E_FAIL;
+ }
+ }
+ else
+ {
+ // Slot is out of range. Shouldn't happen unless debuggee is corrupted.
+ _ASSERTE(!"Invalid TLS slot");
+ return E_INVALIDARG;
+ }
+ void *pEEThreadTLS = (BYTE*)pBase + (slotAdjusted * sizeof(void*));
+
+ // Write the thread's TLS value.
+ hr = GetProcess()->SafeWriteStruct(PTR_TO_CORDB_ADDRESS(pEEThreadTLS), &value);
+
+ if (FAILED(hr))
+ {
+ LOG((LF_CORDB, LL_INFO1000, "CUT::SEETV: failed to set TLS value: computed addr=0x%p slot=%d, err=%x\n", pEEThreadTLS, slot, hr));
+ return hr;
+ }
+
+ LOG((LF_CORDB, LL_INFO1000000, "CUT::SEETV: EE Thread TLS value is now 0x%p for 0x%x\n", value, m_id));
+ return S_OK;
}
// gets the value of gCurrentThreadInfo.m_pThread
@@ -3068,45 +3158,44 @@ DWORD_PTR CordbUnmanagedThread::GetEEThreadValue()
{
DWORD_PTR ret = NULL;
- REMOTE_PTR EEThreadAddr = (BYTE*) GetClrModuleTlsDataAddress() + OFFSETOF__TLS__tls_CurrentThread;
- if(EEThreadAddr == NULL)
+ REMOTE_PTR tlsDataAddress;
+ HRESULT hr = GetClrModuleTlsDataAddress(&tlsDataAddress);
+ if (FAILED(hr))
+ {
+ LOG((LF_CORDB, LL_INFO1000, "CUT::GEETV: GetClrModuleTlsDataAddress FAILED %x for 0x%x\n", hr, m_id));
return NULL;
+ }
// Read the thread's TLS value.
- HRESULT hr = GetProcess()->SafeReadStruct(PTR_TO_CORDB_ADDRESS(EEThreadAddr), &ret);
-
+ REMOTE_PTR EEThreadAddr = (BYTE*)tlsDataAddress + OFFSETOF__TLS__tls_CurrentThread;
+ hr = GetProcess()->SafeReadStruct(PTR_TO_CORDB_ADDRESS(EEThreadAddr), &ret);
if (FAILED(hr))
{
- LOG((LF_CORDB, LL_INFO1000, "CUT::GEETV: failed to get TLS value: "
- "computed addr=0x%p index=%d, err=%x\n",
+ LOG((LF_CORDB, LL_INFO1000, "CUT::GEETV: failed to get TLS value: computed addr=0x%p index=%d, err=%x\n",
EEThreadAddr, GetProcess()->m_runtimeOffsets.m_TLSIndex, hr));
-
return NULL;
}
- LOG((LF_CORDB, LL_INFO1000000,
- "CUT::GEETV: EE Thread TLS value is 0x%p for thread 0x%x\n",
- ret, m_id));
-
+ LOG((LF_CORDB, LL_INFO1000000, "CUT::GEETV: EE Thread TLS value is 0x%p for 0x%x\n", ret, m_id));
return ret;
}
// returns the remote address of gCurrentThreadInfo
-REMOTE_PTR CordbUnmanagedThread::GetClrModuleTlsDataAddress()
+HRESULT CordbUnmanagedThread::GetClrModuleTlsDataAddress(REMOTE_PTR* pAddress)
{
- HRESULT hr = S_OK;
+ *pAddress = NULL;
REMOTE_PTR tlsArrayAddr;
- hr = GetProcess()->SafeReadStruct(PTR_TO_CORDB_ADDRESS((BYTE*)m_threadLocalBase + WINNT_OFFSETOF__TEB__ThreadLocalStoragePointer), &tlsArrayAddr);
+ HRESULT hr = GetProcess()->SafeReadStruct(PTR_TO_CORDB_ADDRESS((BYTE*)m_threadLocalBase + WINNT_OFFSETOF__TEB__ThreadLocalStoragePointer), &tlsArrayAddr);
if (FAILED(hr))
{
- return NULL;
+ return hr;
}
+ // This is the special break-in thread case: TEB.ThreadLocalStoragePointer == NULL
if (tlsArrayAddr == NULL)
{
- _ASSERTE(!"ThreadLocalStoragePointer is NULL");
- return NULL;
+ return E_FAIL;
}
DWORD slot = (DWORD)(GetProcess()->m_runtimeOffsets.m_TLSIndex);
@@ -3115,103 +3204,46 @@ REMOTE_PTR CordbUnmanagedThread::GetClrModuleTlsDataAddress()
hr = GetProcess()->SafeReadStruct(PTR_TO_CORDB_ADDRESS((BYTE*)tlsArrayAddr + (slot & 0xFFFF) * sizeof(void*)), &clrModuleTlsDataAddr);
if (FAILED(hr))
{
- return NULL;
+ return hr;
}
if (clrModuleTlsDataAddr == NULL)
{
_ASSERTE(!"No clr module data present at _tls_index for this thread");
- return NULL;
+ return E_FAIL;
}
- return (BYTE*) clrModuleTlsDataAddr + ((slot & 0x7FFF0000) >> 16);
+ *pAddress = (BYTE*) clrModuleTlsDataAddr + ((slot & 0x7FFF0000) >> 16);
+ return S_OK;
}
-// gets the value of gCurrentThreadInfo.m_EETlsData
+// Gets the value of gCurrentThreadInfo.m_EETlsData
REMOTE_PTR CordbUnmanagedThread::GetEETlsDataBlock()
{
REMOTE_PTR ret;
- REMOTE_PTR blockAddr = (BYTE*) GetClrModuleTlsDataAddress() + OFFSETOF__TLS__tls_EETlsData;
+ REMOTE_PTR tlsDataAddress;
+ HRESULT hr = GetClrModuleTlsDataAddress(&tlsDataAddress);
+ if (FAILED(hr))
+ {
+ LOG((LF_CORDB, LL_INFO1000, "CUT::GEETDB: GetClrModuleTlsDataAddress FAILED %x for 0x%x\n", hr, m_id));
+ return NULL;
+ }
-
- HRESULT hr = GetProcess()->SafeReadStruct(PTR_TO_CORDB_ADDRESS(blockAddr), &ret);
+ REMOTE_PTR blockAddr = (BYTE*)tlsDataAddress + OFFSETOF__TLS__tls_EETlsData;
+ hr = GetProcess()->SafeReadStruct(PTR_TO_CORDB_ADDRESS(blockAddr), &ret);
if (FAILED(hr))
{
LOG((LF_CORDB, LL_INFO1000, "CUT::GEETDB: failed to read EETlsData address: computed addr=0x%p offset=%d, err=%x\n",
blockAddr, OFFSETOF__TLS__tls_EETlsData, hr));
-
return NULL;
}
- LOG((LF_CORDB, LL_INFO1000000, "CUT::GEETDB: EETlsData address value is 0x%p for thread 0x%x\n", ret, m_id));
-
+ LOG((LF_CORDB, LL_INFO1000000, "CUT::GEETDB: EETlsData address value is 0x%p for 0x%x\n", ret, m_id));
return ret;
}
/*
- * CacheEEDebuggerWord
- *
- * NOTE: This routine is inappropriately named at this time because we dont
- * actually cache any values. This is because we dont have a way to invalidate
- * the cache between purely-native continues.
- *
- * This routine grabs two pieces of information from the target process via
- * ReadProcessMemory. First, if the runtime does not have a thread object for
- * this thread it grabs the debugger's value from the TLS slot. If there is a
- * runtime thread object, then it saves that away and grabs the debugger's value
- * from the thread object.
- *
- * Parameters:
- * None.
- *
- * Returns:
- * None. If it fails, then the Get/Set functions will fail.
- */
-void CordbUnmanagedThread::CacheEEDebuggerWord()
-{
- LOG((LF_CORDB, LL_INFO1000, "CacheEEDW: Entered\n"));
-
- REMOTE_PTR value = (REMOTE_PTR)GetEEThreadValue();
-
- if ((((DWORD)value) & 0x1) == 1)
- {
- m_pEEThread = NULL;
- m_pdwTlsValue = (REMOTE_PTR)((BYTE*)value - 0x1);
- m_fValidTlsData = TRUE;
- }
- else if (value != NULL)
- {
- m_pEEThread = value;
-
- // Compute the address of the debugger word #2.
- void *pEEDebuggerWord = (BYTE*)m_pEEThread + GetProcess()->m_runtimeOffsets.m_EEThreadDebuggerWordOffset;
-
- // Update the word.
- HRESULT hr = GetProcess()->SafeReadStruct(PTR_TO_CORDB_ADDRESS(pEEDebuggerWord), &m_pdwTlsValue);
- m_fValidTlsData = SUCCEEDED(hr);
-
- if (!m_fValidTlsData)
- {
- LOG((LF_CORDB, LL_INFO1000, "EEDW: failed to read debugger word: 0x%08x + 0x%x = 0x%p, err=%d\n",
- m_pEEThread, GetProcess()->m_runtimeOffsets.m_EEThreadDebuggerWordOffset, pEEDebuggerWord, GetLastError()));
- }
- else
- {
- LOG((LF_CORDB, LL_INFO1000, "CacheEEDW: Debugger word is 0x%p\n", m_pdwTlsValue));
- }
- }
- else
- {
- m_fValidTlsData = TRUE;
- m_pEEThread = NULL;
- m_pdwTlsValue = NULL;
- }
-
- LOG((LF_CORDB, LL_INFO1000, "CacheEEDW: Exited\n"));
-}
-
-/*
* GetEEDebuggerWord
*
* This routine returns the value read from the thread
@@ -3224,22 +3256,12 @@ void CordbUnmanagedThread::CacheEEDebuggerWord()
*/
HRESULT CordbUnmanagedThread::GetEEDebuggerWord(REMOTE_PTR *pValue)
{
+ LOG((LF_CORDB, LL_INFO1000, "CUT::GEEDW: Entered\n"));
if (pValue == NULL)
{
return E_INVALIDARG;
}
-
- CacheEEDebuggerWord();
-
- if (!m_fValidTlsData)
- {
- *pValue = NULL;
- return E_FAIL;
- }
-
- *pValue = m_pdwTlsValue;
-
- return S_OK;
+ return GetTlsSlot(GetProcess()->m_runtimeOffsets.m_debuggerWordTLSIndex, pValue);
}
// SetEEDebuggerWord
@@ -3257,53 +3279,7 @@ HRESULT CordbUnmanagedThread::GetEEDebuggerWord(REMOTE_PTR *pValue)
HRESULT CordbUnmanagedThread::SetEEDebuggerWord(REMOTE_PTR value)
{
LOG((LF_CORDB, LL_INFO1000, "CUT::SEEDW: Entered - value is 0x%p\n", value));
-
- CacheEEDebuggerWord();
-
- if (!m_fValidTlsData)
- {
- return E_FAIL;
- }
-
- m_pdwTlsValue = value;
-
- //
- // If the thread is NULL, bit-or on a 1 and store that.
- //
- if (m_pEEThread == NULL)
- {
- REMOTE_PTR pdwTemp = m_pdwTlsValue;
-
- if (pdwTemp != 0)
- {
- // actually we add 1, but we only use it for pointers which are
- // 8 byte aligned so it is the same thing
- _ASSERTE( ((UINT_PTR)pdwTemp & 0x1) == 0);
- pdwTemp = (REMOTE_PTR) ((BYTE*)pdwTemp + 0x01);
- }
- // This will write to the TLS slot. It's only safe to do this after a Flare has been sent from the
- // LS (since that's what guarantees the slot is allocated).
- return SetEEThreadValue(pdwTemp);
- }
- else
- {
- // Compute the address of the debugger word #2.
- void *pEEDebuggerWord = (BYTE*)m_pEEThread + GetProcess()->m_runtimeOffsets.m_EEThreadDebuggerWordOffset;
-
- // Update the word.
- HRESULT hr = GetProcess()->SafeWriteStruct(PTR_TO_CORDB_ADDRESS(pEEDebuggerWord), &m_pdwTlsValue);
-
- if (FAILED(hr))
- {
- LOG((LF_CORDB, LL_INFO1000, "CUT::SEETDW: failed to write debugger word: 0x%08x + 0x%x = 0x%08x, err=%x\n",
- m_pEEThread, GetProcess()->m_runtimeOffsets.m_EEThreadDebuggerWordOffset, pEEDebuggerWord, hr));
-
- return hr;
- }
- }
-
- LOG((LF_CORDB, LL_INFO1000, "CUT::SEEDW: Exited\n"));
- return S_OK;
+ return SetTlsSlot(GetProcess()->m_runtimeOffsets.m_debuggerWordTLSIndex, value);
}
/*
@@ -3326,21 +3302,12 @@ HRESULT CordbUnmanagedThread::GetEEThreadPtr(REMOTE_PTR *ppEEThread)
return E_INVALIDARG;
}
- CacheEEDebuggerWord();
-
- if (!m_fValidTlsData)
- {
- *ppEEThread = NULL;
- return E_FAIL;
- }
-
- *ppEEThread = m_pEEThread;
+ *ppEEThread = (REMOTE_PTR)GetEEThreadValue();
return S_OK;
}
-
void CordbUnmanagedThread::GetEEState(bool *threadStepping, bool *specialManagedException)
{
REMOTE_PTR pEEThread;
@@ -3360,12 +3327,10 @@ void CordbUnmanagedThread::GetEEState(bool *threadStepping, bool *specialManaged
// Grab the thread state out of the EE Thread.
DWORD EEThreadStateNC;
hr = GetProcess()->SafeReadStruct(PTR_TO_CORDB_ADDRESS(pEEThreadStateNC), &EEThreadStateNC);
-
if (FAILED(hr))
{
LOG((LF_CORDB, LL_INFO1000, "CUT::GEETS: failed to read thread state NC: 0x%p + 0x%x = 0x%p, err=%d\n",
pEEThread, pRO->m_EEThreadStateNCOffset, pEEThreadStateNC, GetLastError()));
-
return;
}
@@ -3448,14 +3413,13 @@ bool CordbUnmanagedThread::IsCantStop()
}
_ASSERTE(GetProcess()->ThreadHoldsProcessLock());
- if(IsRaiseExceptionHijacked())
+ if (IsRaiseExceptionHijacked())
{
return true;
}
REMOTE_PTR pEEThread;
HRESULT hr = this->GetEEThreadPtr(&pEEThread);
-
if (FAILED(hr))
{
_ASSERTE(!"Failed to EEThreadPtr in IsCantStop");