From 30089e2c9b0e0472bad614c96aa88197956e24ed Mon Sep 17 00:00:00 2001 From: Jan Kotas Date: Sun, 6 Nov 2016 23:29:01 -0800 Subject: [PATCH] Unhandled exception handling on Unix - 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 --- .../DependencyAnalysis/ObjectWriter.cs | 42 +++++---- src/Native/Bootstrap/platform.unix.cpp | 3 +- src/Native/Runtime/unix/UnixContext.cpp | 4 + .../Runtime/unix/UnixNativeCodeManager.cpp | 85 +++++++++++++------ .../Runtime/windows/CoffNativeCodeManager.cpp | 18 ++-- 5 files changed, 101 insertions(+), 51 deletions(-) diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ObjectWriter.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ObjectWriter.cs index 78c13d899c3..d68e920f8c8 100644 --- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ObjectWriter.cs +++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ObjectWriter.cs @@ -356,9 +356,10 @@ public void BuildCFIMap(NodeFactory factory, ObjectNode node) ObjectNode.ObjectData ehInfo = nodeWithCodeInfo.EHInfo; string mainEhInfoSymbolName = null; - int i = 0; - foreach (var frameInfo in frameInfos) + for (int i = 0; i < frameInfos.Length; i++) { + FrameInfo frameInfo = frameInfos[i]; + int start = frameInfo.StartOffset; int end = frameInfo.EndOffset; int len = frameInfo.BlobData.Length; @@ -366,7 +367,7 @@ public void BuildCFIMap(NodeFactory factory, ObjectNode node) if (_targetPlatform.OperatingSystem == TargetOS.Windows) { - string blobSymbolName = "_unwind" + (i++).ToStringInvariant() + _currentNodeName; + string blobSymbolName = "_unwind" + i.ToStringInvariant() + _currentNodeName; ObjectNodeSection section = ObjectNodeSection.XDataSection; if (node.ShouldShareNodeAcrossModules(factory) && factory.Target.OperatingSystem == TargetOS.Windows) @@ -421,37 +422,48 @@ public void BuildCFIMap(NodeFactory factory, ObjectNode node) } else { - string blobSymbolName = "_lsda" + (i++).ToStringInvariant() + _currentNodeName; + string blobSymbolName = "_lsda" + i.ToStringInvariant() + _currentNodeName; SwitchSection(_nativeObjectWriter, LsdaSection.Name); - EmitAlignment(4); EmitSymbolDef(blobSymbolName); - // emit relative offset from the main function - EmitIntValue((ulong)(start - frameInfos[0].StartOffset), 4); - - // emit last byte from the blob - the function kind FrameInfoFlags flags = frameInfo.Flags; - if ((ehInfo != null) || (mainEhInfoSymbolName != null)) + if (ehInfo != null) { flags |= FrameInfoFlags.HasEHInfo; } EmitIntValue((byte)flags, 1); + if (i != 0) + { + EmitSymbolRef("_lsda0" + _currentNodeName, RelocType.IMAGE_REL_BASED_REL32, 4); + + // emit relative offset from the main function + EmitIntValue((ulong)(start - frameInfos[0].StartOffset), 4); + } + + if (ehInfo != null) + { + mainEhInfoSymbolName = "_ehInfo" + _currentNodeName; + EmitSymbolRef(mainEhInfoSymbolName, RelocType.IMAGE_REL_BASED_REL32, 4); + } + + if (gcInfo != null) + { + EmitBlob(gcInfo); + gcInfo = null; + } + if (ehInfo != null) { + // TODO: Place EHInfo into different section for better locality Debug.Assert(ehInfo.Alignment == 1); Debug.Assert(ehInfo.DefinedSymbols.Length == 0); - mainEhInfoSymbolName = "_ehInfo" + _currentNodeName; EmitSymbolDef(mainEhInfoSymbolName); EmitBlobWithRelocs(ehInfo.Data, ehInfo.Relocs); ehInfo = null; } - else if (mainEhInfoSymbolName != null) - { - EmitSymbolRef(mainEhInfoSymbolName, RelocType.IMAGE_REL_BASED_REL32, 4); - } // For Unix, we build CFI blob map for each offset. Debug.Assert(len % CfiCodeSize == 0); diff --git a/src/Native/Bootstrap/platform.unix.cpp b/src/Native/Bootstrap/platform.unix.cpp index 876cc501abb..6937179dcfe 100644 --- a/src/Native/Bootstrap/platform.unix.cpp +++ b/src/Native/Bootstrap/platform.unix.cpp @@ -68,12 +68,11 @@ extern "C" void OutputDebugStringW() { - throw "OutputDebugStringW"; } uint32_t GetCurrentThreadId() { - throw "GetCurrentThreadId"; + return 42; } uint32_t RhCompatibleReentrantWaitAny(uint32_t alertable, uint32_t timeout, uint32_t count, void* pHandles) diff --git a/src/Native/Runtime/unix/UnixContext.cpp b/src/Native/Runtime/unix/UnixContext.cpp index 4812a1ec788..493a27e54ad 100644 --- a/src/Native/Runtime/unix/UnixContext.cpp +++ b/src/Native/Runtime/unix/UnixContext.cpp @@ -403,6 +403,10 @@ void UnwindCursorToRegDisplay(unw_cursor_t *cursor, unw_context_t *unwContext, R unw_get_reg(cursor, UNW_REG_IP, (unw_word_t *) ®Display->IP); unw_get_reg(cursor, UNW_REG_SP, (unw_word_t *) ®Display->SP); +#if defined(_AMD64_) + regDisplay->pIP = PTR_PCODE(regDisplay->SP - sizeof(TADDR)); +#endif + #if defined(_ARM_) || defined(_ARM64_) regDisplay->IP |= 1; #endif diff --git a/src/Native/Runtime/unix/UnixNativeCodeManager.cpp b/src/Native/Runtime/unix/UnixNativeCodeManager.cpp index ea6f71193e0..bfe00ad9e36 100644 --- a/src/Native/Runtime/unix/UnixNativeCodeManager.cpp +++ b/src/Native/Runtime/unix/UnixNativeCodeManager.cpp @@ -14,6 +14,9 @@ #include "CommonMacros.inl" +#define GCINFODECODER_NO_EE +#include "coreclr/gcinfodecoder.cpp" + #include "UnixContext.h" #define UBF_FUNC_KIND_MASK 0x03 @@ -23,11 +26,13 @@ #define UBF_FUNC_HAS_EHINFO 0x04 +#define UBF_FUNC_REVERSE_PINVOKE 0x08 + struct UnixNativeMethodInfo { PTR_VOID pMethodStartAddress; - PTR_UInt8 pEhInfo; - uint8_t funcFlags; + PTR_UInt8 pMainLSDA; + PTR_UInt8 pLSDA; bool executionAborted; }; @@ -57,30 +62,26 @@ bool UnixNativeCodeManager::FindMethodInfo(PTR_VOID ControlPC, return false; } - PTR_UInt8 lsdaPtr = dac_cast(lsda); + PTR_UInt8 p = dac_cast(lsda); - int offsetFromMainFunction = *dac_cast(lsdaPtr); - lsdaPtr += sizeof(int); - pMethodInfo->funcFlags = *lsdaPtr; - ++lsdaPtr; + pMethodInfo->pLSDA = p; - PTR_UInt8 ehInfoPtr = NULL; - if (pMethodInfo->funcFlags & UBF_FUNC_HAS_EHINFO) + uint8_t unwindBlockFlags = *p++; + + if ((unwindBlockFlags & UBF_FUNC_KIND_MASK) != UBF_FUNC_KIND_ROOT) { - // main function contains the EH info blob in its LSDA, funclets just refer - // to the main function's blob - if (offsetFromMainFunction == 0) - { - ehInfoPtr = lsdaPtr; - } - else - { - ehInfoPtr = lsdaPtr + *dac_cast(lsdaPtr); - } + // Funclets just refer to the main function's blob + pMethodInfo->pMainLSDA = p + *dac_cast(p); + p += sizeof(int32_t); + + pMethodInfo->pMethodStartAddress = dac_cast(startAddress - *dac_cast(p)); + } + else + { + pMethodInfo->pMainLSDA = dac_cast(lsda); + pMethodInfo->pMethodStartAddress = dac_cast(startAddress); } - pMethodInfo->pMethodStartAddress = (PTR_VOID)(startAddress - offsetFromMainFunction); - pMethodInfo->pEhInfo = ehInfoPtr; pMethodInfo->executionAborted = false; return true; @@ -90,7 +91,8 @@ bool UnixNativeCodeManager::IsFunclet(MethodInfo * pMethodInfo) { UnixNativeMethodInfo * pNativeMethodInfo = (UnixNativeMethodInfo *)pMethodInfo; - return (pNativeMethodInfo->funcFlags & UBF_FUNC_KIND_MASK) != UBF_FUNC_KIND_ROOT; + uint8_t unwindBlockFlags = *(pNativeMethodInfo->pLSDA); + return (unwindBlockFlags & UBF_FUNC_KIND_MASK) != UBF_FUNC_KIND_ROOT; } PTR_VOID UnixNativeCodeManager::GetFramePointer(MethodInfo * pMethodInfo, @@ -99,7 +101,7 @@ PTR_VOID UnixNativeCodeManager::GetFramePointer(MethodInfo * pMethodInfo, UnixNativeMethodInfo * pNativeMethodInfo = (UnixNativeMethodInfo *)pMethodInfo; // Return frame pointer for methods with EH and funclets - uint8_t unwindBlockFlags = pNativeMethodInfo->funcFlags; + uint8_t unwindBlockFlags = *(pNativeMethodInfo->pLSDA); if ((unwindBlockFlags & UBF_FUNC_HAS_EHINFO) != 0 || (unwindBlockFlags & UBF_FUNC_KIND_MASK) != UBF_FUNC_KIND_ROOT) { return (PTR_VOID)pRegisterSet->GetFP(); @@ -127,14 +129,37 @@ bool UnixNativeCodeManager::UnwindStackFrame(MethodInfo * pMethodInfo, REGDISPLAY * pRegisterSet, // in/out PTR_VOID * ppPreviousTransitionFrame) // out { - if (!VirtualUnwind(pRegisterSet)) + UnixNativeMethodInfo * pNativeMethodInfo = (UnixNativeMethodInfo *)pMethodInfo; + + PTR_UInt8 p = pNativeMethodInfo->pMainLSDA; + + uint8_t unwindBlockFlags = *p++; + + if ((unwindBlockFlags & UBF_FUNC_REVERSE_PINVOKE) != 0) { - return false; + // Reverse PInvoke transition should on the main function body only + assert(pNativeMethodInfo->pMainLSDA == pNativeMethodInfo->pLSDA); + + if ((unwindBlockFlags & UBF_FUNC_HAS_EHINFO) != 0) + p += sizeof(int32_t); + + GcInfoDecoder decoder(GCInfoToken(p), DECODE_REVERSE_PINVOKE_VAR); + + // @TODO: CORERT: Encode reverse PInvoke frame slot in GCInfo: https://github.com/dotnet/corert/issues/2115 + // INT32 slot = decoder.GetReversePInvokeFrameStackSlot(); + // assert(slot != NO_REVERSE_PINVOKE_FRAME); + + *ppPreviousTransitionFrame = (PTR_VOID)-1; + return true; } - // @TODO: CORERT: PInvoke transitions *ppPreviousTransitionFrame = NULL; + if (!VirtualUnwind(pRegisterSet)) + { + return false; + } + return true; } @@ -187,8 +212,12 @@ bool UnixNativeCodeManager::EHEnumInit(MethodInfo * pMethodInfo, PTR_VOID * pMet UnixNativeMethodInfo * pNativeMethodInfo = (UnixNativeMethodInfo *)pMethodInfo; + PTR_UInt8 p = pNativeMethodInfo->pMainLSDA; + + uint8_t unwindBlockFlags = *p++; + // return if there is no EH info associated with this method - if ((pNativeMethodInfo->funcFlags & UBF_FUNC_HAS_EHINFO) == 0) + if ((unwindBlockFlags & UBF_FUNC_HAS_EHINFO) == 0) { return false; } @@ -198,7 +227,7 @@ bool UnixNativeCodeManager::EHEnumInit(MethodInfo * pMethodInfo, PTR_VOID * pMet *pMethodStartAddress = pNativeMethodInfo->pMethodStartAddress; pEnumState->pMethodStartAddress = dac_cast(pNativeMethodInfo->pMethodStartAddress); - pEnumState->pEHInfo = pNativeMethodInfo->pEhInfo; + pEnumState->pEHInfo = dac_cast(p + *dac_cast(p)); pEnumState->uClause = 0; pEnumState->nClauses = VarInt::ReadUnsigned(pEnumState->pEHInfo); diff --git a/src/Native/Runtime/windows/CoffNativeCodeManager.cpp b/src/Native/Runtime/windows/CoffNativeCodeManager.cpp index 79ac103b621..e15056ada03 100644 --- a/src/Native/Runtime/windows/CoffNativeCodeManager.cpp +++ b/src/Native/Runtime/windows/CoffNativeCodeManager.cpp @@ -17,7 +17,7 @@ #include "CommonMacros.inl" #define GCINFODECODER_NO_EE -#include "coreclr\gcinfodecoder.cpp" +#include "coreclr/gcinfodecoder.cpp" #define UBF_FUNC_KIND_MASK 0x03 #define UBF_FUNC_KIND_ROOT 0x00 @@ -306,7 +306,7 @@ bool CoffNativeCodeManager::UnwindStackFrame(MethodInfo * pMethodInfo, CoffNativeMethodInfo * pNativeMethodInfo = (CoffNativeMethodInfo *)pMethodInfo; size_t unwindDataBlobSize; - PTR_VOID pUnwindDataBlob = GetUnwindDataBlob(m_moduleBase, pNativeMethodInfo->mainRuntimeFunction, &unwindDataBlobSize); + PTR_VOID pUnwindDataBlob = GetUnwindDataBlob(m_moduleBase, pNativeMethodInfo->runtimeFunction, &unwindDataBlobSize); PTR_UInt8 p = dac_cast(pUnwindDataBlob) + unwindDataBlobSize; @@ -314,6 +314,12 @@ bool CoffNativeCodeManager::UnwindStackFrame(MethodInfo * pMethodInfo, if ((unwindBlockFlags & UBF_FUNC_REVERSE_PINVOKE) != 0) { + // Reverse PInvoke transition should on the main function body only + assert(pNativeMethodInfo->mainRuntimeFunction == pNativeMethodInfo->runtimeFunction); + + if ((unwindBlockFlags & UBF_FUNC_HAS_EHINFO) != 0) + p += sizeof(int32_t); + GcInfoDecoder decoder(GCInfoToken(p), DECODE_REVERSE_PINVOKE_VAR); // @TODO: CORERT: Encode reverse PInvoke frame slot in GCInfo: https://github.com/dotnet/corert/issues/2115 @@ -321,12 +327,11 @@ bool CoffNativeCodeManager::UnwindStackFrame(MethodInfo * pMethodInfo, // assert(slot != NO_REVERSE_PINVOKE_FRAME); *ppPreviousTransitionFrame = (PTR_VOID)-1; - } - else - { - *ppPreviousTransitionFrame = NULL; + return true; } + *ppPreviousTransitionFrame = NULL; + CONTEXT context; KNONVOLATILE_CONTEXT_POINTERS contextPointers; @@ -444,6 +449,7 @@ bool CoffNativeCodeManager::EHEnumInit(MethodInfo * pMethodInfo, PTR_VOID * pMet } *pMethodStartAddress = dac_cast(m_moduleBase + pNativeMethodInfo->mainRuntimeFunction->BeginAddress); + pEnumState->pMethodStartAddress = dac_cast(*pMethodStartAddress); pEnumState->pEHInfo = dac_cast(m_moduleBase + *dac_cast(p)); pEnumState->uClause = 0;