Skip to content

Commit 1a5af14

Browse files
Debugger fixes for the Windows X86 EH Funclets model (#115630)
Notable areas improved - The funclets model uses the vectored exception handler to funnel debug events to the debugger - FramePointer details for the debugger are inscrutable and weird. I've made it work, but enough of this is driven by subtleties in the stackwalker that I would not be surprised if there are additional issues here - RtlpGetFunctionEndAddress needs to use a DAC pointer to read the unwind info. - Funclet prologs for CoreCLR X86 Funclets need to have at least 1 instruction in them so that the Native->IL mapping is correct. Otherwise it gets shadowed by a mapping which says it is a PROLOG instruction. This is probably fixable by allowing an Native -> IL mapping for both the PROLOG as well as the IL offset in the funclet, but a nop here is both easy to generate an unlikely to be a major cost. - Likewise, EnC frame generation is no longer able to rely on the shadow stack pointer logic injected by EH handling, and needed 1 bit of info not in the current emitted stack information. Notably, that there is synchronized codegen in the method. Instead of modifying the gcinfo to include that data, we just put in fake offsets for where the synchronized region is, and use the existence of any data as a flag in the EnC layout. NOTE: I also removed the general purpose logic to read the synchronized range from the data. The fix for this also needs to fix the runtime behavior around stackwalking, and in another case in the presence of a locallloc instruction in the method. Contributes to #113985
1 parent 0e411ee commit 1a5af14

File tree

13 files changed

+98
-11
lines changed

13 files changed

+98
-11
lines changed

src/coreclr/debug/ee/debugger.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7569,7 +7569,8 @@ HRESULT Debugger::SendException(Thread *pThread,
75697569
}
75707570
CONTRACTL_END;
75717571

7572-
LOG((LF_CORDB, LL_INFO10000, "D::SendException\n"));
7572+
LOG((LF_CORDB, LL_INFO10000, "D::SendException pThread=0x%p fFirstChance=%s currentIP=0x%p currentSP= 0x%p fContinuable=%s fAttaching=%s fForceNonInterceptable=%s\n",
7573+
pThread, fFirstChance ? "true" : "false", (void*)currentIP, (void*)currentSP, fContinuable ? "true" : "false", fAttaching ? "true" : "false", fForceNonInterceptable ? "true" : "false"));
75737574

75747575
if (CORDBUnrecoverableError(this))
75757576
{

src/coreclr/debug/ee/frameinfo.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1265,7 +1265,7 @@ FramePointer GetFramePointerForDebugger(DebuggerFrameData* pData, CrawlFrame* pC
12651265

12661266
FramePointer fpResult;
12671267

1268-
#if defined(FEATURE_EH_FUNCLETS)
1268+
#if !defined(TARGET_X86)
12691269
if (pData->info.frame == NULL)
12701270
{
12711271
// This is a managed method frame.
@@ -1277,7 +1277,7 @@ FramePointer GetFramePointerForDebugger(DebuggerFrameData* pData, CrawlFrame* pC
12771277
fpResult = FramePointer::MakeFramePointer((LPVOID)(pData->info.frame));
12781278
}
12791279

1280-
#else // !FEATURE_EH_FUNCLETS
1280+
#else // !TARGET_X86
12811281
if ((pCF == NULL || !pCF->IsFrameless()) && pData->info.frame != NULL)
12821282
{
12831283
//
@@ -1299,7 +1299,7 @@ FramePointer GetFramePointerForDebugger(DebuggerFrameData* pData, CrawlFrame* pC
12991299
fpResult = FramePointer::MakeFramePointer((LPVOID)GetRegdisplayStackMark(&(pData->regDisplay)));
13001300
}
13011301

1302-
#endif // !FEATURE_EH_FUNCLETS
1302+
#endif // !TARGET_X86
13031303

13041304
LOG((LF_CORDB, LL_INFO100000, "GFPFD: Frame pointer is 0x%p\n", fpResult.GetSPValue()));
13051305

src/coreclr/inc/clrnt.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -211,7 +211,7 @@ RtlpGetFunctionEndAddress (
211211
_In_ TADDR ImageBase
212212
)
213213
{
214-
PUNWIND_INFO pUnwindInfo = (PUNWIND_INFO)(ImageBase + FunctionEntry->UnwindData);
214+
DPTR(UNWIND_INFO) pUnwindInfo = dac_cast<DPTR(UNWIND_INFO)>(ImageBase + FunctionEntry->UnwindData);
215215

216216
return FunctionEntry->BeginAddress + pUnwindInfo->FunctionLength;
217217
}

src/coreclr/inc/eetwain.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -259,14 +259,15 @@ virtual void * GetGSCookieAddr(PREGDISPLAY pContext,
259259
virtual bool IsInPrologOrEpilog(DWORD relPCOffset,
260260
GCInfoToken gcInfoToken,
261261
size_t* prologSize) = 0;
262-
262+
#ifndef FEATURE_EH_FUNCLETS
263263
/*
264264
Returns true if the given IP is in the synchronized region of the method (valid for synchronized methods only)
265265
*/
266266
virtual bool IsInSynchronizedRegion(
267267
DWORD relOffset,
268268
GCInfoToken gcInfoToken,
269269
unsigned flags) = 0;
270+
#endif // FEATURE_EH_FUNCLETS
270271
#endif // !USE_GC_INFO_DECODER
271272

272273
/*
@@ -513,6 +514,7 @@ bool IsInPrologOrEpilog(
513514
GCInfoToken gcInfoToken,
514515
size_t* prologSize);
515516

517+
#ifndef FEATURE_EH_FUNCLETS
516518
/*
517519
Returns true if the given IP is in the synchronized region of the method (valid for synchronized functions only)
518520
*/
@@ -521,6 +523,7 @@ bool IsInSynchronizedRegion(
521523
DWORD relOffset,
522524
GCInfoToken gcInfoToken,
523525
unsigned flags);
526+
#endif // FEATURE_EH_FUNCLETS
524527
#endif // !USE_GC_INFO_DECODER
525528

526529
/*
@@ -710,6 +713,7 @@ bool IsInPrologOrEpilog(
710713
return false;
711714
}
712715

716+
#ifndef FEATURE_EH_FUNCLETS
713717
virtual
714718
bool IsInSynchronizedRegion(
715719
DWORD relOffset,
@@ -720,6 +724,7 @@ bool IsInSynchronizedRegion(
720724
_ASSERTE(FALSE);
721725
return false;
722726
}
727+
#endif // FEATURE_EH_FUNCLETS
723728
#endif // !USE_GC_INFO_DECODER
724729

725730
virtual

src/coreclr/inc/regdisp.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,11 @@ inline TADDR GetRegdisplayFP(REGDISPLAY *display) {
132132
inline LPVOID GetRegdisplayFPAddress(REGDISPLAY *display) {
133133
LIMITED_METHOD_CONTRACT;
134134

135+
#ifdef FEATURE_EH_FUNCLETS
136+
return &display->pCurrentContext->Ebp;
137+
#else
135138
return (LPVOID)display->GetEbpLocation();
139+
#endif
136140
}
137141

138142
inline TADDR GetRegdisplayPCTAddr(REGDISPLAY *display)

src/coreclr/jit/codegenxarch.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11049,6 +11049,13 @@ void CodeGen::genFuncletProlog(BasicBlock* block)
1104911049
#ifdef UNIX_X86_ABI
1105011050
// Add a padding for 16-byte alignment
1105111051
inst_RV_IV(INS_sub, REG_SPBASE, 12, EA_PTRSIZE);
11052+
#else
11053+
if (!compiler->IsTargetAbi(CORINFO_NATIVEAOT_ABI))
11054+
{
11055+
// Funclet prologs need to have at least 1 byte or the IL->Native mapping data will not
11056+
// include the first IL instruction in the funclet.
11057+
instGen(INS_nop);
11058+
}
1105211059
#endif
1105311060

1105411061
genClearAvxStateInProlog();

src/coreclr/jit/gcencode.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1606,6 +1606,22 @@ size_t GCInfo::gcInfoBlockHdrSave(
16061606
assert(header->epilogCount <= 1);
16071607
}
16081608
#endif
1609+
if (compiler->UsesFunclets() && compiler->info.compFlags & CORINFO_FLG_SYNCH)
1610+
{
1611+
// While the sync start offset and end offset are not used by the stackwalker/EH system
1612+
// in funclets mode, we do need to know if the code is synchronized if we are generating
1613+
// an edit and continue method, so that we can properly manage the stack during a Remap
1614+
// operation, for determining the ParamTypeArg for collectible generics purposes, and
1615+
// for determining the offset of the localloc variable in the stack frame.
1616+
// Instead of inventing a new encoding, just encode some non-0 offsets into these fields.
1617+
// to indicate that the method is synchronized.
1618+
//
1619+
// Use 1 for both offsets, since that doesn't actually make sense and implies that the
1620+
// sync region is 0 bytes long. The JIT will never emit a sync region of 0 bytes in non-
1621+
// funclet mode.
1622+
header->syncStartOffset = 1;
1623+
header->syncEndOffset = 1;
1624+
}
16091625

16101626
header->revPInvokeOffset = INVALID_REV_PINVOKE_OFFSET;
16111627
if (compiler->opts.IsReversePInvoke())

src/coreclr/jit/lclvars.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3673,6 +3673,9 @@ PhaseStatus Compiler::lvaMarkLocalVars()
36733673
// saved EBP <-- EBP points here
36743674
// other callee-saved registers // InfoHdrSmall.savedRegsCountExclFP specifies this size
36753675
// optional GS cookie // InfoHdrSmall.security is 1 if this exists
3676+
// if FEATURE_EH_FUNCLETS
3677+
// issynchronized bool if it is a synchronized method
3678+
// endif // FEATURE_EH_FUNCLETS
36763679
// LocAllocSP slot
36773680
// -- lower addresses --
36783681
//
@@ -4085,13 +4088,15 @@ unsigned Compiler::lvaGetMaxSpillTempSize()
40854088
* | security object |
40864089
* |-----------------------|
40874090
* | ParamTypeArg |
4091+
// If funclet support is disabled
40884092
* |-----------------------|
40894093
* | Last-executed-filter |
40904094
* |-----------------------|
40914095
* | |
40924096
* ~ Shadow SPs ~
40934097
* | |
40944098
* |-----------------------|
4099+
// Endif funclet support is disabled
40954100
* | |
40964101
* ~ Variables ~
40974102
* | |

src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/StackFrameIterator.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4+
using System.Diagnostics;
45
using System.Runtime.InteropServices;
56
#if !NATIVEAOT
67
using System.Runtime.ExceptionServices;
@@ -60,6 +61,7 @@ internal unsafe struct StackFrameIterator
6061
#pragma warning restore CA1822
6162
#endif // NATIVEAOT
6263

64+
[StackTraceHidden]
6365
internal bool Init(EH.PAL_LIMITED_CONTEXT* pStackwalkCtx, bool instructionFault = false, bool* fIsExceptionIntercepted = null)
6466
{
6567
return InternalCalls.RhpSfiInit(ref this, pStackwalkCtx, instructionFault, fIsExceptionIntercepted);

src/coreclr/vm/eetwain.cpp

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ bool VarIsInReg(ICorDebugInfo::VarLoc varLoc)
161161
* could easily duplicate what they do, which is why we're calling into them.
162162
*/
163163

164-
HRESULT EECodeManager::FixContextForEnC(PCONTEXT pCtx,
164+
HRESULT EECodeManager::FixContextForEnC(PCONTEXT pCtx,
165165
EECodeInfo * pOldCodeInfo,
166166
const ICorDebugInfo::NativeVarInfo * oldMethodVars,
167167
SIZE_T oldMethodVarsCount,
@@ -206,6 +206,11 @@ HRESULT EECodeManager::FixContextForEnC(PCONTEXT pCtx,
206206
return E_FAIL; // stack should be empty - <TODO> @TODO : Barring localloc</TODO>
207207
}
208208

209+
#ifdef FEATURE_EH_FUNCLETS
210+
// EnC remap inside handlers is not supported
211+
if (pOldCodeInfo->IsFunclet() || pNewCodeInfo->IsFunclet())
212+
return CORDBG_E_ENC_IN_FUNCLET;
213+
#else
209214
if (oldInfo->handlers)
210215
{
211216
bool hasInnerFilter;
@@ -229,13 +234,16 @@ HRESULT EECodeManager::FixContextForEnC(PCONTEXT pCtx,
229234
}
230235
}
231236
}
237+
#endif // FEATURE_EH_FUNCLETS
232238

233239
/* @TODO: Check if we have grown out of space for locals, in the face of localloc */
234240
_ASSERTE(!oldInfo->localloc && !newInfo->localloc);
235241

242+
#ifndef FEATURE_EH_FUNCLETS
236243
// @TODO: If nesting level grows above the MAX_EnC_HANDLER_NESTING_LEVEL,
237244
// we should return EnC_NESTED_HANLDERS
238245
_ASSERTE(oldInfo->handlers && newInfo->handlers);
246+
#endif
239247

240248
LOG((LF_ENC, LL_INFO100, "EECM::FixContextForEnC: Checks out\n"));
241249

@@ -1552,7 +1560,7 @@ OBJECTREF EECodeManager::GetInstance( PREGDISPLAY pContext,
15521560
#endif
15531561

15541562
#else // FEATURE_EH_FUNCLETS
1555-
if (pCodeInfo->GetMethodDesc()->AcquiresInstMethodTableFromThis()) // Generic Context is "this"
1563+
if (pCodeInfo->GetMethodDesc()->AcquiresInstMethodTableFromThis() && (hdrInfoBody->genericsContext)) // Generic Context is "this"
15561564
{
15571565
// Untracked table must have at least one entry - this pointer
15581566
_ASSERTE(hdrInfoBody->untrackedCnt > 0);
@@ -1807,6 +1815,7 @@ bool EECodeManager::IsInPrologOrEpilog(DWORD relPCoffset,
18071815
(info.epilogOffs != hdrInfo::NOT_IN_EPILOG));
18081816
}
18091817

1818+
#ifndef FEATURE_EH_FUNCLETS
18101819
/*****************************************************************************
18111820
*
18121821
* Returns true if the given IP is in the synchronized region of the method (valid for synchronized functions only)
@@ -1837,6 +1846,7 @@ bool EECodeManager::IsInSynchronizedRegion(DWORD relOffset,
18371846
// Everything after the epilog is also in synchronized region.
18381847
(info.epilogCnt != 0 && info.syncEpilogStart + info.epilogSize <= relOffset);
18391848
}
1849+
#endif // FEATURE_EH_FUNCLETS
18401850
#endif // !USE_GC_INFO_DECODER
18411851

18421852
/*****************************************************************************

0 commit comments

Comments
 (0)