Skip to content
This repository was archived by the owner on Nov 1, 2020. It is now read-only.

Commit cb9c859

Browse files
authored
Unhandled exception handling on Unix (#2157)
- Reduced differences between the unwind info, gc info and eh info encodings between Windows and Unix. - Miscellaneous tweaks to get unhandled exceptions fail with gracefully with error message and abort with this change
1 parent ff66359 commit cb9c859

File tree

5 files changed

+101
-51
lines changed

5 files changed

+101
-51
lines changed

src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ObjectWriter.cs

Lines changed: 27 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -356,17 +356,18 @@ public void BuildCFIMap(NodeFactory factory, ObjectNode node)
356356
ObjectNode.ObjectData ehInfo = nodeWithCodeInfo.EHInfo;
357357
string mainEhInfoSymbolName = null;
358358

359-
int i = 0;
360-
foreach (var frameInfo in frameInfos)
359+
for (int i = 0; i < frameInfos.Length; i++)
361360
{
361+
FrameInfo frameInfo = frameInfos[i];
362+
362363
int start = frameInfo.StartOffset;
363364
int end = frameInfo.EndOffset;
364365
int len = frameInfo.BlobData.Length;
365366
byte[] blob = frameInfo.BlobData;
366367

367368
if (_targetPlatform.OperatingSystem == TargetOS.Windows)
368369
{
369-
string blobSymbolName = "_unwind" + (i++).ToStringInvariant() + _currentNodeName;
370+
string blobSymbolName = "_unwind" + i.ToStringInvariant() + _currentNodeName;
370371

371372
ObjectNodeSection section = ObjectNodeSection.XDataSection;
372373
if (node.ShouldShareNodeAcrossModules(factory) && factory.Target.OperatingSystem == TargetOS.Windows)
@@ -421,37 +422,48 @@ public void BuildCFIMap(NodeFactory factory, ObjectNode node)
421422
}
422423
else
423424
{
424-
string blobSymbolName = "_lsda" + (i++).ToStringInvariant() + _currentNodeName;
425+
string blobSymbolName = "_lsda" + i.ToStringInvariant() + _currentNodeName;
425426

426427
SwitchSection(_nativeObjectWriter, LsdaSection.Name);
427428

428-
EmitAlignment(4);
429429
EmitSymbolDef(blobSymbolName);
430430

431-
// emit relative offset from the main function
432-
EmitIntValue((ulong)(start - frameInfos[0].StartOffset), 4);
433-
434-
// emit last byte from the blob - the function kind
435431
FrameInfoFlags flags = frameInfo.Flags;
436-
if ((ehInfo != null) || (mainEhInfoSymbolName != null))
432+
if (ehInfo != null)
437433
{
438434
flags |= FrameInfoFlags.HasEHInfo;
439435
}
440436
EmitIntValue((byte)flags, 1);
441437

438+
if (i != 0)
439+
{
440+
EmitSymbolRef("_lsda0" + _currentNodeName, RelocType.IMAGE_REL_BASED_REL32, 4);
441+
442+
// emit relative offset from the main function
443+
EmitIntValue((ulong)(start - frameInfos[0].StartOffset), 4);
444+
}
445+
446+
if (ehInfo != null)
447+
{
448+
mainEhInfoSymbolName = "_ehInfo" + _currentNodeName;
449+
EmitSymbolRef(mainEhInfoSymbolName, RelocType.IMAGE_REL_BASED_REL32, 4);
450+
}
451+
452+
if (gcInfo != null)
453+
{
454+
EmitBlob(gcInfo);
455+
gcInfo = null;
456+
}
457+
442458
if (ehInfo != null)
443459
{
460+
// TODO: Place EHInfo into different section for better locality
444461
Debug.Assert(ehInfo.Alignment == 1);
445462
Debug.Assert(ehInfo.DefinedSymbols.Length == 0);
446-
mainEhInfoSymbolName = "_ehInfo" + _currentNodeName;
447463
EmitSymbolDef(mainEhInfoSymbolName);
448464
EmitBlobWithRelocs(ehInfo.Data, ehInfo.Relocs);
449465
ehInfo = null;
450466
}
451-
else if (mainEhInfoSymbolName != null)
452-
{
453-
EmitSymbolRef(mainEhInfoSymbolName, RelocType.IMAGE_REL_BASED_REL32, 4);
454-
}
455467

456468
// For Unix, we build CFI blob map for each offset.
457469
Debug.Assert(len % CfiCodeSize == 0);

src/Native/Bootstrap/platform.unix.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,12 +68,11 @@ extern "C"
6868

6969
void OutputDebugStringW()
7070
{
71-
throw "OutputDebugStringW";
7271
}
7372

7473
uint32_t GetCurrentThreadId()
7574
{
76-
throw "GetCurrentThreadId";
75+
return 42;
7776
}
7877

7978
uint32_t RhCompatibleReentrantWaitAny(uint32_t alertable, uint32_t timeout, uint32_t count, void* pHandles)

src/Native/Runtime/unix/UnixContext.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -403,6 +403,10 @@ void UnwindCursorToRegDisplay(unw_cursor_t *cursor, unw_context_t *unwContext, R
403403
unw_get_reg(cursor, UNW_REG_IP, (unw_word_t *) &regDisplay->IP);
404404
unw_get_reg(cursor, UNW_REG_SP, (unw_word_t *) &regDisplay->SP);
405405

406+
#if defined(_AMD64_)
407+
regDisplay->pIP = PTR_PCODE(regDisplay->SP - sizeof(TADDR));
408+
#endif
409+
406410
#if defined(_ARM_) || defined(_ARM64_)
407411
regDisplay->IP |= 1;
408412
#endif

src/Native/Runtime/unix/UnixNativeCodeManager.cpp

Lines changed: 57 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@
1414

1515
#include "CommonMacros.inl"
1616

17+
#define GCINFODECODER_NO_EE
18+
#include "coreclr/gcinfodecoder.cpp"
19+
1720
#include "UnixContext.h"
1821

1922
#define UBF_FUNC_KIND_MASK 0x03
@@ -23,11 +26,13 @@
2326

2427
#define UBF_FUNC_HAS_EHINFO 0x04
2528

29+
#define UBF_FUNC_REVERSE_PINVOKE 0x08
30+
2631
struct UnixNativeMethodInfo
2732
{
2833
PTR_VOID pMethodStartAddress;
29-
PTR_UInt8 pEhInfo;
30-
uint8_t funcFlags;
34+
PTR_UInt8 pMainLSDA;
35+
PTR_UInt8 pLSDA;
3136
bool executionAborted;
3237
};
3338

@@ -57,30 +62,26 @@ bool UnixNativeCodeManager::FindMethodInfo(PTR_VOID ControlPC,
5762
return false;
5863
}
5964

60-
PTR_UInt8 lsdaPtr = dac_cast<PTR_UInt8>(lsda);
65+
PTR_UInt8 p = dac_cast<PTR_UInt8>(lsda);
6166

62-
int offsetFromMainFunction = *dac_cast<PTR_Int32>(lsdaPtr);
63-
lsdaPtr += sizeof(int);
64-
pMethodInfo->funcFlags = *lsdaPtr;
65-
++lsdaPtr;
67+
pMethodInfo->pLSDA = p;
6668

67-
PTR_UInt8 ehInfoPtr = NULL;
68-
if (pMethodInfo->funcFlags & UBF_FUNC_HAS_EHINFO)
69+
uint8_t unwindBlockFlags = *p++;
70+
71+
if ((unwindBlockFlags & UBF_FUNC_KIND_MASK) != UBF_FUNC_KIND_ROOT)
6972
{
70-
// main function contains the EH info blob in its LSDA, funclets just refer
71-
// to the main function's blob
72-
if (offsetFromMainFunction == 0)
73-
{
74-
ehInfoPtr = lsdaPtr;
75-
}
76-
else
77-
{
78-
ehInfoPtr = lsdaPtr + *dac_cast<PTR_Int32>(lsdaPtr);
79-
}
73+
// Funclets just refer to the main function's blob
74+
pMethodInfo->pMainLSDA = p + *dac_cast<PTR_Int32>(p);
75+
p += sizeof(int32_t);
76+
77+
pMethodInfo->pMethodStartAddress = dac_cast<PTR_VOID>(startAddress - *dac_cast<PTR_Int32>(p));
78+
}
79+
else
80+
{
81+
pMethodInfo->pMainLSDA = dac_cast<PTR_UInt8>(lsda);
82+
pMethodInfo->pMethodStartAddress = dac_cast<PTR_VOID>(startAddress);
8083
}
8184

82-
pMethodInfo->pMethodStartAddress = (PTR_VOID)(startAddress - offsetFromMainFunction);
83-
pMethodInfo->pEhInfo = ehInfoPtr;
8485
pMethodInfo->executionAborted = false;
8586

8687
return true;
@@ -90,7 +91,8 @@ bool UnixNativeCodeManager::IsFunclet(MethodInfo * pMethodInfo)
9091
{
9192
UnixNativeMethodInfo * pNativeMethodInfo = (UnixNativeMethodInfo *)pMethodInfo;
9293

93-
return (pNativeMethodInfo->funcFlags & UBF_FUNC_KIND_MASK) != UBF_FUNC_KIND_ROOT;
94+
uint8_t unwindBlockFlags = *(pNativeMethodInfo->pLSDA);
95+
return (unwindBlockFlags & UBF_FUNC_KIND_MASK) != UBF_FUNC_KIND_ROOT;
9496
}
9597

9698
PTR_VOID UnixNativeCodeManager::GetFramePointer(MethodInfo * pMethodInfo,
@@ -99,7 +101,7 @@ PTR_VOID UnixNativeCodeManager::GetFramePointer(MethodInfo * pMethodInfo,
99101
UnixNativeMethodInfo * pNativeMethodInfo = (UnixNativeMethodInfo *)pMethodInfo;
100102

101103
// Return frame pointer for methods with EH and funclets
102-
uint8_t unwindBlockFlags = pNativeMethodInfo->funcFlags;
104+
uint8_t unwindBlockFlags = *(pNativeMethodInfo->pLSDA);
103105
if ((unwindBlockFlags & UBF_FUNC_HAS_EHINFO) != 0 || (unwindBlockFlags & UBF_FUNC_KIND_MASK) != UBF_FUNC_KIND_ROOT)
104106
{
105107
return (PTR_VOID)pRegisterSet->GetFP();
@@ -127,14 +129,37 @@ bool UnixNativeCodeManager::UnwindStackFrame(MethodInfo * pMethodInfo,
127129
REGDISPLAY * pRegisterSet, // in/out
128130
PTR_VOID * ppPreviousTransitionFrame) // out
129131
{
130-
if (!VirtualUnwind(pRegisterSet))
132+
UnixNativeMethodInfo * pNativeMethodInfo = (UnixNativeMethodInfo *)pMethodInfo;
133+
134+
PTR_UInt8 p = pNativeMethodInfo->pMainLSDA;
135+
136+
uint8_t unwindBlockFlags = *p++;
137+
138+
if ((unwindBlockFlags & UBF_FUNC_REVERSE_PINVOKE) != 0)
131139
{
132-
return false;
140+
// Reverse PInvoke transition should on the main function body only
141+
assert(pNativeMethodInfo->pMainLSDA == pNativeMethodInfo->pLSDA);
142+
143+
if ((unwindBlockFlags & UBF_FUNC_HAS_EHINFO) != 0)
144+
p += sizeof(int32_t);
145+
146+
GcInfoDecoder decoder(GCInfoToken(p), DECODE_REVERSE_PINVOKE_VAR);
147+
148+
// @TODO: CORERT: Encode reverse PInvoke frame slot in GCInfo: https://github.com/dotnet/corert/issues/2115
149+
// INT32 slot = decoder.GetReversePInvokeFrameStackSlot();
150+
// assert(slot != NO_REVERSE_PINVOKE_FRAME);
151+
152+
*ppPreviousTransitionFrame = (PTR_VOID)-1;
153+
return true;
133154
}
134155

135-
// @TODO: CORERT: PInvoke transitions
136156
*ppPreviousTransitionFrame = NULL;
137157

158+
if (!VirtualUnwind(pRegisterSet))
159+
{
160+
return false;
161+
}
162+
138163
return true;
139164
}
140165

@@ -187,8 +212,12 @@ bool UnixNativeCodeManager::EHEnumInit(MethodInfo * pMethodInfo, PTR_VOID * pMet
187212

188213
UnixNativeMethodInfo * pNativeMethodInfo = (UnixNativeMethodInfo *)pMethodInfo;
189214

215+
PTR_UInt8 p = pNativeMethodInfo->pMainLSDA;
216+
217+
uint8_t unwindBlockFlags = *p++;
218+
190219
// return if there is no EH info associated with this method
191-
if ((pNativeMethodInfo->funcFlags & UBF_FUNC_HAS_EHINFO) == 0)
220+
if ((unwindBlockFlags & UBF_FUNC_HAS_EHINFO) == 0)
192221
{
193222
return false;
194223
}
@@ -198,7 +227,7 @@ bool UnixNativeCodeManager::EHEnumInit(MethodInfo * pMethodInfo, PTR_VOID * pMet
198227
*pMethodStartAddress = pNativeMethodInfo->pMethodStartAddress;
199228

200229
pEnumState->pMethodStartAddress = dac_cast<PTR_UInt8>(pNativeMethodInfo->pMethodStartAddress);
201-
pEnumState->pEHInfo = pNativeMethodInfo->pEhInfo;
230+
pEnumState->pEHInfo = dac_cast<PTR_UInt8>(p + *dac_cast<PTR_Int32>(p));
202231
pEnumState->uClause = 0;
203232
pEnumState->nClauses = VarInt::ReadUnsigned(pEnumState->pEHInfo);
204233

src/Native/Runtime/windows/CoffNativeCodeManager.cpp

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
#include "CommonMacros.inl"
1818

1919
#define GCINFODECODER_NO_EE
20-
#include "coreclr\gcinfodecoder.cpp"
20+
#include "coreclr/gcinfodecoder.cpp"
2121

2222
#define UBF_FUNC_KIND_MASK 0x03
2323
#define UBF_FUNC_KIND_ROOT 0x00
@@ -306,27 +306,32 @@ bool CoffNativeCodeManager::UnwindStackFrame(MethodInfo * pMethodInfo,
306306
CoffNativeMethodInfo * pNativeMethodInfo = (CoffNativeMethodInfo *)pMethodInfo;
307307

308308
size_t unwindDataBlobSize;
309-
PTR_VOID pUnwindDataBlob = GetUnwindDataBlob(m_moduleBase, pNativeMethodInfo->mainRuntimeFunction, &unwindDataBlobSize);
309+
PTR_VOID pUnwindDataBlob = GetUnwindDataBlob(m_moduleBase, pNativeMethodInfo->runtimeFunction, &unwindDataBlobSize);
310310

311311
PTR_UInt8 p = dac_cast<PTR_UInt8>(pUnwindDataBlob) + unwindDataBlobSize;
312312

313313
uint8_t unwindBlockFlags = *p++;
314314

315315
if ((unwindBlockFlags & UBF_FUNC_REVERSE_PINVOKE) != 0)
316316
{
317+
// Reverse PInvoke transition should on the main function body only
318+
assert(pNativeMethodInfo->mainRuntimeFunction == pNativeMethodInfo->runtimeFunction);
319+
320+
if ((unwindBlockFlags & UBF_FUNC_HAS_EHINFO) != 0)
321+
p += sizeof(int32_t);
322+
317323
GcInfoDecoder decoder(GCInfoToken(p), DECODE_REVERSE_PINVOKE_VAR);
318324

319325
// @TODO: CORERT: Encode reverse PInvoke frame slot in GCInfo: https://github.com/dotnet/corert/issues/2115
320326
// INT32 slot = decoder.GetReversePInvokeFrameStackSlot();
321327
// assert(slot != NO_REVERSE_PINVOKE_FRAME);
322328

323329
*ppPreviousTransitionFrame = (PTR_VOID)-1;
324-
}
325-
else
326-
{
327-
*ppPreviousTransitionFrame = NULL;
330+
return true;
328331
}
329332

333+
*ppPreviousTransitionFrame = NULL;
334+
330335
CONTEXT context;
331336
KNONVOLATILE_CONTEXT_POINTERS contextPointers;
332337

@@ -444,6 +449,7 @@ bool CoffNativeCodeManager::EHEnumInit(MethodInfo * pMethodInfo, PTR_VOID * pMet
444449
}
445450

446451
*pMethodStartAddress = dac_cast<PTR_VOID>(m_moduleBase + pNativeMethodInfo->mainRuntimeFunction->BeginAddress);
452+
447453
pEnumState->pMethodStartAddress = dac_cast<PTR_UInt8>(*pMethodStartAddress);
448454
pEnumState->pEHInfo = dac_cast<PTR_UInt8>(m_moduleBase + *dac_cast<PTR_Int32>(p));
449455
pEnumState->uClause = 0;

0 commit comments

Comments
 (0)