From 92a6a3b1ae4aff4b3a904fed104d253f7d863a36 Mon Sep 17 00:00:00 2001 From: Fadi Hanna Date: Tue, 12 Sep 2017 15:07:07 -0700 Subject: [PATCH] These changes provide a way for ProjectX/CoreRT to get the target of custom instantiating unboxing stubs. The implementation uses a general purpose infrastructure that can associate custom data to methods with unwind info, given the fact that unwind info lookups are very fast. Similar to GC info and EH info, this implementation now provides a way to associate any custom data to methods, by adding a flag and a reloc to the data in the method's unwind info. The custom data that can be attached to methods begin with a flag (indicating what data follows), followed by data. Right now, the only custom data we have for methods are unboxing stub target pointers. The dependency analysis models this custom data using a new node type: MethodAssociatedDataNode. The custom data (when it exists) can be be retrieved using a new API on ICodeManager. The changes also include all the necessary plumbing to link custom data nodes symbols for PX (given that it's UTC that emits the unwind info) [tfs-changeset: 1673664] --- .../DependencyAnalysis/INodeWithCodeInfo.cs | 11 ++- .../ISpecialUnboxThunkNode.cs | 17 ++++ .../MethodAssociatedDataNode.cs | 83 +++++++++++++++++++ .../DependencyAnalysis/MethodCodeNode.cs | 26 +++++- .../DependencyAnalysis/NodeFactory.cs | 11 +++ .../NonExternMethodSymbolNode.cs | 30 ++++++- .../DependencyAnalysis/ObjectWriter.cs | 35 ++++++-- .../src/ILCompiler.Compiler.csproj | 6 +- src/Native/Runtime/ICodeManager.h | 11 +++ src/Native/Runtime/MiscHelpers.cpp | 5 ++ src/Native/Runtime/RuntimeInstance.cpp | 18 ++++ src/Native/Runtime/RuntimeInstance.h | 1 + src/Native/Runtime/module.cpp | 8 ++ src/Native/Runtime/module.h | 2 + .../Runtime/unix/UnixNativeCodeManager.cpp | 30 ++++++- .../Runtime/unix/UnixNativeCodeManager.h | 2 + .../Runtime/windows/CoffNativeCodeManager.cpp | 41 ++++++++- .../Runtime/windows/CoffNativeCodeManager.h | 2 + src/Native/jitinterface/JITCodeManager.cpp | 7 ++ src/Native/jitinterface/JITCodeManager.h | 4 +- .../Runtime/Augments/RuntimeAugments.cs | 5 ++ .../src/System/Runtime/RuntimeImports.cs | 4 + .../TypeLoader/TypeLoaderEnvironment.cs | 9 +- 23 files changed, 344 insertions(+), 24 deletions(-) create mode 100644 src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ISpecialUnboxThunkNode.cs create mode 100644 src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/MethodAssociatedDataNode.cs diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/INodeWithCodeInfo.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/INodeWithCodeInfo.cs index ffa254e76ae..48b48e656f6 100644 --- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/INodeWithCodeInfo.cs +++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/INodeWithCodeInfo.cs @@ -9,11 +9,12 @@ namespace ILCompiler.DependencyAnalysis [Flags] public enum FrameInfoFlags { - Handler = 0x01, - Filter = 0x02, + Handler = 0x01, + Filter = 0x02, - HasEHInfo = 0x04, - ReversePInvoke = 0x08, + HasEHInfo = 0x04, + ReversePInvoke = 0x08, + HasAssociatedData = 0x10, } public struct FrameInfo @@ -48,5 +49,7 @@ ObjectNode.ObjectData EHInfo { get; } + + ISymbolNode GetAssociatedDataNode(NodeFactory factory); } } diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ISpecialUnboxThunkNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ISpecialUnboxThunkNode.cs new file mode 100644 index 00000000000..3c8b62513c7 --- /dev/null +++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ISpecialUnboxThunkNode.cs @@ -0,0 +1,17 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Internal.TypeSystem; + +namespace ILCompiler.DependencyAnalysis +{ + /// + /// A dependency analysis node that represents a special instantiating unboxing stub. + /// + public interface ISpecialUnboxThunkNode : IMethodNode + { + bool IsSpecialUnboxingThunk { get; } + ISymbolNode GetUnboxingThunkTarget(NodeFactory factory); + } +} diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/MethodAssociatedDataNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/MethodAssociatedDataNode.cs new file mode 100644 index 00000000000..c942cf5d9b4 --- /dev/null +++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/MethodAssociatedDataNode.cs @@ -0,0 +1,83 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Diagnostics; + +using Internal.Text; +using Internal.TypeSystem; +using Internal.TypeSystem.Interop; + +namespace ILCompiler.DependencyAnalysis +{ + [Flags] + public enum AssociatedDataFlags : byte + { + None = 0, + HasUnboxingStubTarget = 1, + } + + /// + /// This node contains any custom data that we'd like to associated with a method. The unwind info of the method + /// will have a reloc to this custom data if it exists. Not all methods need custom data to be emitted. + /// This custom data excludes gcinfo and ehinfo (they are written by ObjectWriter during obj file emission). + /// + public class MethodAssociatedDataNode : ObjectNode, ISymbolDefinitionNode + { + private IMethodNode _methodNode; + + public MethodAssociatedDataNode(IMethodNode methodNode) + { + Debug.Assert(!methodNode.Method.IsAbstract); + Debug.Assert(methodNode.Method.GetCanonMethodTarget(CanonicalFormKind.Specific) == methodNode.Method); + _methodNode = methodNode; + } + + protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler); + + public override ObjectNodeSection Section => ObjectNodeSection.ReadOnlyDataSection; + public override bool StaticDependenciesAreComputed => true; + public int Offset => 0; + public override bool IsShareable => _methodNode.Method is InstantiatedMethod || EETypeNode.IsTypeNodeShareable(_methodNode.Method.OwningType); + + public virtual void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append("_associatedData_").Append(nameMangler.GetMangledMethodName(_methodNode.Method)); + } + + public static bool MethodHasAssociatedData(NodeFactory factory, IMethodNode methodNode) + { + // Instantiating unboxing stubs. We need to store their non-unboxing target pointer (looked up by runtime) + ISpecialUnboxThunkNode unboxThunk = methodNode as ISpecialUnboxThunkNode; + if(unboxThunk != null && unboxThunk.IsSpecialUnboxingThunk) + return true; + + return false; + } + + public override ObjectData GetData(NodeFactory factory, bool relocsOnly) + { + Debug.Assert(MethodHasAssociatedData(factory, _methodNode)); + + ObjectDataBuilder objData = new ObjectDataBuilder(factory, relocsOnly); + objData.RequireInitialAlignment(1); + objData.AddSymbol(this); + + AssociatedDataFlags flags = AssociatedDataFlags.None; + + var flagsReservation = objData.ReserveByte(); + + ISpecialUnboxThunkNode unboxThunkNode = _methodNode as ISpecialUnboxThunkNode; + if (unboxThunkNode != null && unboxThunkNode.IsSpecialUnboxingThunk) + { + flags |= AssociatedDataFlags.HasUnboxingStubTarget; + objData.EmitPointerReloc(unboxThunkNode.GetUnboxingThunkTarget(factory)); + } + + objData.EmitByte(flagsReservation, (byte)flags); + + return objData.ToObjectData(); + } + } +} diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/MethodCodeNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/MethodCodeNode.cs index 93a2e2b4fc0..228cf240107 100644 --- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/MethodCodeNode.cs +++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/MethodCodeNode.cs @@ -10,7 +10,7 @@ namespace ILCompiler.DependencyAnalysis { - public class MethodCodeNode : ObjectNode, IMethodBodyNode, INodeWithCodeInfo, INodeWithDebugInfo, IMethodCodeNode + public class MethodCodeNode : ObjectNode, IMethodBodyNode, INodeWithCodeInfo, INodeWithDebugInfo, IMethodCodeNode, ISpecialUnboxThunkNode { public static readonly ObjectNodeSection StartSection = new ObjectNodeSection(".managedcode$A", SectionType.Executable); public static readonly ObjectNodeSection WindowsContentSection = new ObjectNodeSection(".managedcode$I", SectionType.Executable); @@ -82,6 +82,12 @@ protected override DependencyList ComputeNonRelocationBasedDependencies(NodeFact } } + if (MethodAssociatedDataNode.MethodHasAssociatedData(factory, this)) + { + dependencies = dependencies ?? new DependencyList(); + dependencies.Add(new DependencyListEntry(factory.MethodAssociatedData(this), "Method associated data")); + } + CodeBasedDependencyAlgorithm.AddDependenciesDueToMethodCodePresence(ref dependencies, factory, _method); return dependencies; @@ -92,10 +98,28 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly) return _methodCode; } + public bool IsSpecialUnboxingThunk => ((CompilerTypeSystemContext)Method.Context).IsSpecialUnboxingThunk(_method); + + public ISymbolNode GetUnboxingThunkTarget(NodeFactory factory) + { + Debug.Assert(IsSpecialUnboxingThunk); + + MethodDesc nonUnboxingMethod = ((CompilerTypeSystemContext)Method.Context).GetTargetOfSpecialUnboxingThunk(_method); + return factory.MethodEntrypoint(nonUnboxingMethod, false); + } + public FrameInfo[] FrameInfos => _frameInfos; public byte[] GCInfo => _gcInfo; public ObjectData EHInfo => _ehInfo; + public ISymbolNode GetAssociatedDataNode(NodeFactory factory) + { + if (MethodAssociatedDataNode.MethodHasAssociatedData(factory, this)) + return factory.MethodAssociatedData(this); + + return null; + } + public void InitializeFrameInfos(FrameInfo[] frameInfos) { Debug.Assert(_frameInfos == null); diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NodeFactory.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NodeFactory.cs index 9d1d693c39c..91bcfdb3497 100644 --- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NodeFactory.cs +++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NodeFactory.cs @@ -296,6 +296,11 @@ private void CreateNodeCaches() _unboxingStubs = new NodeCache(CreateUnboxingStubNode); + _methodAssociatedData = new NodeCache(methodNode => + { + return new MethodAssociatedDataNode(methodNode); + }); + _fatFunctionPointers = new NodeCache(method => { return new FatFunctionPointerNode(method.Method, method.IsUnboxingStub); @@ -707,6 +712,7 @@ public IMethodNode StringAllocator(MethodDesc stringConstructor) private NodeCache _methodEntrypoints; private NodeCache _unboxingStubs; + private NodeCache _methodAssociatedData; public IMethodNode MethodEntrypoint(MethodDesc method, bool unboxingStub = false) { @@ -718,6 +724,11 @@ public IMethodNode MethodEntrypoint(MethodDesc method, bool unboxingStub = false return _methodEntrypoints.GetOrAdd(method); } + public MethodAssociatedDataNode MethodAssociatedData(IMethodNode methodNode) + { + return _methodAssociatedData.GetOrAdd(methodNode); + } + private NodeCache _fatFunctionPointers; public IMethodNode FatFunctionPointer(MethodDesc method, bool isUnboxingStub = false) diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NonExternMethodSymbolNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NonExternMethodSymbolNode.cs index 2dedef7c394..c39eda7e049 100644 --- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NonExternMethodSymbolNode.cs +++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NonExternMethodSymbolNode.cs @@ -17,9 +17,10 @@ namespace ILCompiler.DependencyAnalysis /// in the DependencyAnalysis infrastructure during compilation that is compiled /// in the current compilation process /// - public class NonExternMethodSymbolNode : ExternSymbolNode, IMethodBodyNodeWithFuncletSymbols + public class NonExternMethodSymbolNode : ExternSymbolNode, IMethodBodyNodeWithFuncletSymbols, ISpecialUnboxThunkNode { private MethodDesc _method; + private bool _isUnboxing; private List _compilationDiscoveredDependencies; ISymbolNode[] _funcletSymbols = Array.Empty(); bool _dependenciesQueried; @@ -29,6 +30,7 @@ public NonExternMethodSymbolNode(NodeFactory factory, MethodDesc method, bool is : base(isUnboxing ? UnboxingStubNode.GetMangledName(factory.NameMangler, method) : factory.NameMangler.GetMangledMethodName(method)) { + _isUnboxing = isUnboxing; _method = method; } @@ -42,6 +44,26 @@ public MethodDesc Method } } + public bool IsSpecialUnboxingThunk + { + get + { + if (_isUnboxing) + { + if (!_method.HasInstantiation && _method.OwningType.IsValueType && !_method.Signature.IsStatic) + return _method.IsCanonicalMethod(CanonicalFormKind.Any); + } + + return false; + } + } + public ISymbolNode GetUnboxingThunkTarget(NodeFactory factory) + { + Debug.Assert(IsSpecialUnboxingThunk); + + return factory.MethodEntrypoint(_method.GetCanonMethodTarget(CanonicalFormKind.Specific), false); + } + public bool HasCompiledBody => _hasCompiledBody; public void SetHasCompiledBody() { @@ -99,6 +121,12 @@ public override IEnumerable GetStaticDependencies(NodeFacto dependencies.AddRange(_compilationDiscoveredDependencies); } + if (MethodAssociatedDataNode.MethodHasAssociatedData(factory, this)) + { + dependencies = dependencies ?? new DependencyList(); + dependencies.Add(new DependencyListEntry(factory.MethodAssociatedData(this), "Method associated data")); + } + return dependencies; } diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ObjectWriter.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ObjectWriter.cs index 632a197d5a8..fbd44e9e033 100644 --- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ObjectWriter.cs +++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ObjectWriter.cs @@ -432,11 +432,15 @@ public void PublishUnwindInfo(ObjectNode node) FrameInfo[] frameInfos = nodeWithCodeInfo.FrameInfos; if (frameInfos == null) { + // Data should only be present if the method has unwind info + Debug.Assert(nodeWithCodeInfo.GetAssociatedDataNode(_nodeFactory) == null); + return; } byte[] gcInfo = nodeWithCodeInfo.GCInfo; ObjectData ehInfo = nodeWithCodeInfo.EHInfo; + ISymbolNode associatedDataNode = nodeWithCodeInfo.GetAssociatedDataNode(_nodeFactory); for (int i = 0; i < frameInfos.Length; i++) { @@ -458,15 +462,19 @@ public void PublishUnwindInfo(ObjectNode node) EmitSymbolDef(blobSymbolName); FrameInfoFlags flags = frameInfo.Flags; - if (ehInfo != null) - { - flags |= FrameInfoFlags.HasEHInfo; - } + flags |= ehInfo != null ? FrameInfoFlags.HasEHInfo : 0; + flags |= associatedDataNode != null ? FrameInfoFlags.HasAssociatedData : 0; EmitBlob(blob); EmitIntValue((byte)flags, 1); + if (associatedDataNode != null) + { + EmitSymbolRef(_sb.Clear().Append(associatedDataNode.GetMangledName(_nodeFactory.NameMangler)), RelocType.IMAGE_REL_BASED_ABSOLUTE); + associatedDataNode = null; + } + if (ehInfo != null) { EmitSymbolRef(_sb.Clear().Append(_nodeFactory.NameMangler.CompilationUnitPrefix).Append("_ehInfo").Append(_currentNodeZeroTerminatedName), RelocType.IMAGE_REL_BASED_ABSOLUTE); @@ -512,11 +520,15 @@ public void BuildCFIMap(NodeFactory factory, ObjectNode node) FrameInfo[] frameInfos = nodeWithCodeInfo.FrameInfos; if (frameInfos == null) { + // Data should only be present if the method has unwind info + Debug.Assert(nodeWithCodeInfo.GetAssociatedDataNode(_nodeFactory) == null); + return; } byte[] gcInfo = nodeWithCodeInfo.GCInfo; ObjectData ehInfo = nodeWithCodeInfo.EHInfo; + ISymbolNode associatedDataNode = nodeWithCodeInfo.GetAssociatedDataNode(_nodeFactory); for (int i = 0; i < frameInfos.Length; i++) { @@ -539,10 +551,9 @@ public void BuildCFIMap(NodeFactory factory, ObjectNode node) EmitSymbolDef(blobSymbolName); FrameInfoFlags flags = frameInfo.Flags; - if (ehInfo != null) - { - flags |= FrameInfoFlags.HasEHInfo; - } + flags |= ehInfo != null ? FrameInfoFlags.HasEHInfo : 0; + flags |= associatedDataNode != null ? FrameInfoFlags.HasAssociatedData : 0; + EmitIntValue((byte)flags, 1); if (i != 0) @@ -553,6 +564,14 @@ public void BuildCFIMap(NodeFactory factory, ObjectNode node) EmitIntValue((ulong)(start - frameInfos[0].StartOffset), 4); } + if (associatedDataNode != null) + { + _sb.Clear(); + AppendExternCPrefix(_sb); + EmitSymbolRef(_sb.Append(associatedDataNode.GetMangledName(_nodeFactory.NameMangler)), RelocType.IMAGE_REL_BASED_RELPTR32); + associatedDataNode = null; + } + if (ehInfo != null) { EmitSymbolRef(_sb.Clear().Append("_ehInfo").Append(_currentNodeZeroTerminatedName), RelocType.IMAGE_REL_BASED_RELPTR32); diff --git a/src/ILCompiler.Compiler/src/ILCompiler.Compiler.csproj b/src/ILCompiler.Compiler/src/ILCompiler.Compiler.csproj index 8785265d237..e79f04704da 100644 --- a/src/ILCompiler.Compiler/src/ILCompiler.Compiler.csproj +++ b/src/ILCompiler.Compiler/src/ILCompiler.Compiler.csproj @@ -190,8 +190,8 @@ - - + + @@ -208,9 +208,11 @@ + + diff --git a/src/Native/Runtime/ICodeManager.h b/src/Native/Runtime/ICodeManager.h index 7cbfc18386b..3d5af20fd32 100644 --- a/src/Native/Runtime/ICodeManager.h +++ b/src/Native/Runtime/ICodeManager.h @@ -80,6 +80,12 @@ enum class ClasslibFunctionId OnFirstChanceException = 6, }; +enum class AssociatedDataFlags : unsigned char +{ + None = 0, + HasUnboxingStubTarget = 1, +}; + class ICodeManager { public: @@ -119,4 +125,9 @@ class ICodeManager virtual PTR_VOID GetMethodStartAddress(MethodInfo * pMethodInfo) = 0; virtual void * GetClasslibFunction(ClasslibFunctionId functionId) = 0; + + // Returns any custom data attached to the method. Format: + // AssociatedDataFlags // 1 byte. Flags describing the data stored + // Data (stream of bytes) // Variable size (depending on flags). Custom data associated with method + virtual PTR_VOID GetAssociatedData(PTR_VOID ControlPC) = 0; }; diff --git a/src/Native/Runtime/MiscHelpers.cpp b/src/Native/Runtime/MiscHelpers.cpp index eaf55a79adb..507ffa608bb 100644 --- a/src/Native/Runtime/MiscHelpers.cpp +++ b/src/Native/Runtime/MiscHelpers.cpp @@ -332,6 +332,11 @@ COOP_PINVOKE_HELPER(void *, GetClasslibCCtorCheck, (void * pReturnAddress)) return pCallback; } +COOP_PINVOKE_HELPER(void *, RhGetTargetOfUnboxingAndInstantiatingStub, (void * pUnboxStub)) +{ + return GetRuntimeInstance()->GetTargetOfUnboxingAndInstantiatingStub(pUnboxStub); +} + COOP_PINVOKE_HELPER(Boolean, RhpHasDispatchMap, (EEType * pEEType)) { return pEEType->HasDispatchMap(); diff --git a/src/Native/Runtime/RuntimeInstance.cpp b/src/Native/Runtime/RuntimeInstance.cpp index b7254a401c3..35f22d38cb0 100644 --- a/src/Native/Runtime/RuntimeInstance.cpp +++ b/src/Native/Runtime/RuntimeInstance.cpp @@ -181,6 +181,24 @@ ICodeManager * RuntimeInstance::FindCodeManagerByAddress(PTR_VOID pvAddress) return NULL; } +PTR_UInt8 RuntimeInstance::GetTargetOfUnboxingAndInstantiatingStub(PTR_VOID ControlPC) +{ + ICodeManager * pCodeManager = FindCodeManagerByAddress(ControlPC); + if (pCodeManager != NULL) + { + PTR_UInt8 pData = (PTR_UInt8)pCodeManager->GetAssociatedData(ControlPC); + if (pData != NULL) + { + UInt8 flags = *pData++; + + if ((flags & (UInt8)AssociatedDataFlags::HasUnboxingStubTarget) != 0) + return *((PTR_PTR_UInt8)pData); + } + } + + return NULL; +} + GPTR_IMPL_INIT(RuntimeInstance, g_pTheRuntimeInstance, NULL); PTR_RuntimeInstance GetRuntimeInstance() diff --git a/src/Native/Runtime/RuntimeInstance.h b/src/Native/Runtime/RuntimeInstance.h index 3d1ab3ed664..bd001fd1988 100644 --- a/src/Native/Runtime/RuntimeInstance.h +++ b/src/Native/Runtime/RuntimeInstance.h @@ -174,6 +174,7 @@ class RuntimeInstance Module * FindModuleByReadOnlyDataAddress(PTR_VOID Data); Module * FindModuleByOsHandle(HANDLE hOsHandle); PTR_UInt8 FindMethodStartAddress(PTR_VOID ControlPC); + PTR_UInt8 GetTargetOfUnboxingAndInstantiatingStub(PTR_VOID ControlPC); void EnableConservativeStackReporting(); bool IsConservativeStackReportingEnabled() { return m_conservativeStackReportingEnabled; } diff --git a/src/Native/Runtime/module.cpp b/src/Native/Runtime/module.cpp index f17eb285768..a83571cae21 100644 --- a/src/Native/Runtime/module.cpp +++ b/src/Native/Runtime/module.cpp @@ -832,6 +832,14 @@ void * Module::GetClasslibFunction(ClasslibFunctionId functionId) return pMethod; } +PTR_VOID Module::GetAssociatedData(PTR_VOID ControlPC) +{ + UNREFERENCED_PARAMETER(ControlPC); + + // Not supported for ProjectN. + return NULL; +} + // Get classlib-defined helper for running deferred static class constructors. Returns NULL if this is not the // classlib module or the classlib doesn't implement this callback. void * Module::GetClasslibCheckStaticClassConstruction() diff --git a/src/Native/Runtime/module.h b/src/Native/Runtime/module.h index a037bc828d4..f1a48b2a83c 100644 --- a/src/Native/Runtime/module.h +++ b/src/Native/Runtime/module.h @@ -113,6 +113,8 @@ class Module : public ICodeManager void * RecoverLoopHijackTarget(UInt32 entryIndex, ModuleHeader * pModuleHeader); + PTR_VOID GetAssociatedData(PTR_VOID ControlPC); + private: Module(ModuleHeader * pModuleHeader); #ifdef FEATURE_CUSTOM_IMPORTS diff --git a/src/Native/Runtime/unix/UnixNativeCodeManager.cpp b/src/Native/Runtime/unix/UnixNativeCodeManager.cpp index e60e6013069..445b6e00a75 100644 --- a/src/Native/Runtime/unix/UnixNativeCodeManager.cpp +++ b/src/Native/Runtime/unix/UnixNativeCodeManager.cpp @@ -25,9 +25,9 @@ #define UBF_FUNC_KIND_HANDLER 0x01 #define UBF_FUNC_KIND_FILTER 0x02 -#define UBF_FUNC_HAS_EHINFO 0x04 - -#define UBF_FUNC_REVERSE_PINVOKE 0x08 +#define UBF_FUNC_HAS_EHINFO 0x04 +#define UBF_FUNC_REVERSE_PINVOKE 0x08 +#define UBF_FUNC_HAS_ASSOCIATED_DATA 0x10 struct UnixNativeMethodInfo { @@ -130,6 +130,9 @@ void UnixNativeCodeManager::EnumGcRefs(MethodInfo * pMethodInfo, uint8_t unwindBlockFlags = *p++; + if ((unwindBlockFlags & UBF_FUNC_HAS_ASSOCIATED_DATA) != 0) + p += sizeof(int32_t); + if ((unwindBlockFlags & UBF_FUNC_HAS_EHINFO) != 0) p += sizeof(int32_t); @@ -176,6 +179,9 @@ bool UnixNativeCodeManager::UnwindStackFrame(MethodInfo * pMethodInfo, uint8_t unwindBlockFlags = *p++; + if ((unwindBlockFlags & UBF_FUNC_HAS_ASSOCIATED_DATA) != 0) + p += sizeof(int32_t); + if ((unwindBlockFlags & UBF_FUNC_REVERSE_PINVOKE) != 0) { // Reverse PInvoke transition should on the main function body only @@ -265,6 +271,9 @@ bool UnixNativeCodeManager::EHEnumInit(MethodInfo * pMethodInfo, PTR_VOID * pMet uint8_t unwindBlockFlags = *p++; + if ((unwindBlockFlags & UBF_FUNC_HAS_ASSOCIATED_DATA) != 0) + p += sizeof(int32_t); + // return if there is no EH info associated with this method if ((unwindBlockFlags & UBF_FUNC_HAS_EHINFO) == 0) { @@ -356,6 +365,21 @@ void * UnixNativeCodeManager::GetClasslibFunction(ClasslibFunctionId functionId) return m_pClasslibFunctions[id]; } +PTR_VOID UnixNativeCodeManager::GetAssociatedData(PTR_VOID ControlPC) +{ + UnixNativeMethodInfo methodInfo; + if (!FindMethodInfo(ControlPC, &methodInfo)) + return NULL; + + PTR_UInt8 p = methodInfo.pMainLSDA; + + uint8_t unwindBlockFlags = *p++; + if ((unwindBlockFlags & UBF_FUNC_HAS_ASSOCIATED_DATA) == 0) + return NULL; + + return dac_cast(p + *dac_cast(p)); +} + extern "C" bool __stdcall RegisterCodeManager(ICodeManager * pCodeManager, PTR_VOID pvStartRange, UInt32 cbRange); extern "C" void __stdcall UnregisterCodeManager(ICodeManager * pCodeManager); extern "C" bool __stdcall RegisterUnboxingStubs(PTR_VOID pvStartRange, UInt32 cbRange); diff --git a/src/Native/Runtime/unix/UnixNativeCodeManager.h b/src/Native/Runtime/unix/UnixNativeCodeManager.h index e424a559cae..13333ee7370 100644 --- a/src/Native/Runtime/unix/UnixNativeCodeManager.h +++ b/src/Native/Runtime/unix/UnixNativeCodeManager.h @@ -58,4 +58,6 @@ class UnixNativeCodeManager : public ICodeManager PTR_VOID GetMethodStartAddress(MethodInfo * pMethodInfo); void * GetClasslibFunction(ClasslibFunctionId functionId); + + PTR_VOID GetAssociatedData(PTR_VOID ControlPC); }; diff --git a/src/Native/Runtime/windows/CoffNativeCodeManager.cpp b/src/Native/Runtime/windows/CoffNativeCodeManager.cpp index 6690ffeb2f7..fa162aa753e 100644 --- a/src/Native/Runtime/windows/CoffNativeCodeManager.cpp +++ b/src/Native/Runtime/windows/CoffNativeCodeManager.cpp @@ -25,9 +25,9 @@ #define UBF_FUNC_KIND_HANDLER 0x01 #define UBF_FUNC_KIND_FILTER 0x02 -#define UBF_FUNC_HAS_EHINFO 0x04 - -#define UBF_FUNC_REVERSE_PINVOKE 0x08 +#define UBF_FUNC_HAS_EHINFO 0x04 +#define UBF_FUNC_REVERSE_PINVOKE 0x08 +#define UBF_FUNC_HAS_ASSOCIATED_DATA 0x10 #if defined(_TARGET_AMD64_) @@ -297,6 +297,9 @@ void CoffNativeCodeManager::EnumGcRefs(MethodInfo * pMethodInfo, uint8_t unwindBlockFlags = *p++; + if ((unwindBlockFlags & UBF_FUNC_HAS_ASSOCIATED_DATA) != 0) + p += sizeof(int32_t); + if ((unwindBlockFlags & UBF_FUNC_HAS_EHINFO) != 0) p += sizeof(int32_t); @@ -347,6 +350,9 @@ bool CoffNativeCodeManager::UnwindStackFrame(MethodInfo * pMethodInfo, uint8_t unwindBlockFlags = *p++; + if ((unwindBlockFlags & UBF_FUNC_HAS_ASSOCIATED_DATA) != 0) + p += sizeof(int32_t); + if ((unwindBlockFlags & UBF_FUNC_REVERSE_PINVOKE) != 0) { // Reverse PInvoke transition should on the main function body only @@ -455,6 +461,9 @@ bool CoffNativeCodeManager::GetReturnAddressHijackInfo(MethodInfo * pMethodIn uint8_t unwindBlockFlags = *p++; + if ((unwindBlockFlags & UBF_FUNC_HAS_ASSOCIATED_DATA) != 0) + p += sizeof(int32_t); + // Check whether this is a funclet if ((unwindBlockFlags & UBF_FUNC_KIND_MASK) != UBF_FUNC_KIND_ROOT) return false; @@ -550,6 +559,9 @@ bool CoffNativeCodeManager::EHEnumInit(MethodInfo * pMethodInfo, PTR_VOID * pMet uint8_t unwindBlockFlags = *p++; + if ((unwindBlockFlags & UBF_FUNC_HAS_ASSOCIATED_DATA) != 0) + p += sizeof(int32_t); + // return if there is no EH info associated with this method if ((unwindBlockFlags & UBF_FUNC_HAS_EHINFO) == 0) { @@ -634,6 +646,29 @@ void * CoffNativeCodeManager::GetClasslibFunction(ClasslibFunctionId functionId) return m_pClasslibFunctions[id]; } +PTR_VOID CoffNativeCodeManager::GetAssociatedData(PTR_VOID ControlPC) +{ + TADDR relativePC = dac_cast(ControlPC) - m_moduleBase; + + int MethodIndex = LookupUnwindInfoForMethod((UInt32)relativePC, m_pRuntimeFunctionTable, 0, m_nRuntimeFunctionTable - 1); + if (MethodIndex < 0) + return NULL; + + PTR_RUNTIME_FUNCTION pRuntimeFunction = m_pRuntimeFunctionTable + MethodIndex; + + size_t unwindDataBlobSize; + PTR_UInt8 pUnwindDataBlob = GetUnwindDataBlob(m_moduleBase, pRuntimeFunction, &unwindDataBlobSize); + + PTR_UInt8 p = dac_cast(pUnwindDataBlob) + unwindDataBlobSize; + + uint8_t unwindBlockFlags = *p++; + if ((unwindBlockFlags & UBF_FUNC_HAS_ASSOCIATED_DATA) == 0) + return NULL; + + UInt32 dataRVA = *(UInt32*)p; + return m_moduleBase + dataRVA; +} + extern "C" bool __stdcall RegisterCodeManager(ICodeManager * pCodeManager, PTR_VOID pvStartRange, UInt32 cbRange); extern "C" void __stdcall UnregisterCodeManager(ICodeManager * pCodeManager); extern "C" bool __stdcall RegisterUnboxingStubs(PTR_VOID pvStartRange, UInt32 cbRange); diff --git a/src/Native/Runtime/windows/CoffNativeCodeManager.h b/src/Native/Runtime/windows/CoffNativeCodeManager.h index 09b96038644..b0d5390a478 100644 --- a/src/Native/Runtime/windows/CoffNativeCodeManager.h +++ b/src/Native/Runtime/windows/CoffNativeCodeManager.h @@ -94,4 +94,6 @@ class CoffNativeCodeManager : public ICodeManager PTR_VOID GetMethodStartAddress(MethodInfo * pMethodInfo); void * GetClasslibFunction(ClasslibFunctionId functionId); + + PTR_VOID GetAssociatedData(PTR_VOID ControlPC); }; diff --git a/src/Native/jitinterface/JITCodeManager.cpp b/src/Native/jitinterface/JITCodeManager.cpp index fac0f426e39..e16da72a9aa 100644 --- a/src/Native/jitinterface/JITCodeManager.cpp +++ b/src/Native/jitinterface/JITCodeManager.cpp @@ -730,3 +730,10 @@ void * JITCodeManager::GetClasslibFunction(ClasslibFunctionId functionId) assert(false); return false; } + +PTR_VOID JITCodeManager::GetAssociatedData(PTR_VOID ControlPC) +{ + // @TODO: CORERT: GetAssociatedData + assert(false); + return NULL; +} diff --git a/src/Native/jitinterface/JITCodeManager.h b/src/Native/jitinterface/JITCodeManager.h index 294a480162b..4b0212943d2 100644 --- a/src/Native/jitinterface/JITCodeManager.h +++ b/src/Native/jitinterface/JITCodeManager.h @@ -276,4 +276,6 @@ class JITCodeManager : ICodeManager PTR_VOID GetMethodStartAddress(MethodInfo * pMethodInfo); void * GetClasslibFunction(ClasslibFunctionId functionId); -}; \ No newline at end of file + + PTR_VOID GetAssociatedData(PTR_VOID ControlPC); +}; diff --git a/src/System.Private.CoreLib/src/Internal/Runtime/Augments/RuntimeAugments.cs b/src/System.Private.CoreLib/src/Internal/Runtime/Augments/RuntimeAugments.cs index 01336f2dac2..332ba8ab865 100644 --- a/src/System.Private.CoreLib/src/Internal/Runtime/Augments/RuntimeAugments.cs +++ b/src/System.Private.CoreLib/src/Internal/Runtime/Augments/RuntimeAugments.cs @@ -805,6 +805,11 @@ public static IntPtr GetCodeTarget(IntPtr functionPointer) return RuntimeImports.RhGetCodeTarget(functionPointer); } + public static IntPtr GetTargetOfUnboxingAndInstantiatingStub(IntPtr functionPointer) + { + return RuntimeImports.RhGetTargetOfUnboxingAndInstantiatingStub(functionPointer); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static IntPtr RuntimeCacheLookup(IntPtr context, IntPtr signature, int registeredResolutionFunction, object contextObject, out IntPtr auxResult) { diff --git a/src/System.Private.CoreLib/src/System/Runtime/RuntimeImports.cs b/src/System.Private.CoreLib/src/System/Runtime/RuntimeImports.cs index 0eeb8038e16..7893037ffd6 100644 --- a/src/System.Private.CoreLib/src/System/Runtime/RuntimeImports.cs +++ b/src/System.Private.CoreLib/src/System/Runtime/RuntimeImports.cs @@ -611,6 +611,10 @@ internal static uint RhGetLoadedModules(TypeManagerHandle[] resultArray) [RuntimeImport(RuntimeLibrary, "RhGetJmpStubCodeTarget")] internal static extern IntPtr RhGetJmpStubCodeTarget(IntPtr pCode); + [MethodImplAttribute(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "RhGetTargetOfUnboxingAndInstantiatingStub")] + public static extern IntPtr RhGetTargetOfUnboxingAndInstantiatingStub(IntPtr pCode); + // // EH helpers // diff --git a/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.cs b/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.cs index b3b39230ecd..6e72a1ed3ef 100644 --- a/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.cs +++ b/src/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeLoaderEnvironment.cs @@ -675,7 +675,14 @@ private struct UnboxingAndInstantiatingStubMapEntry public static unsafe bool TryGetTargetOfUnboxingAndInstantiatingStub(IntPtr maybeInstantiatingAndUnboxingStub, out IntPtr targetMethod) { - targetMethod = IntPtr.Zero; + targetMethod = RuntimeAugments.GetTargetOfUnboxingAndInstantiatingStub(maybeInstantiatingAndUnboxingStub); + if (targetMethod != IntPtr.Zero) + { + return true; + } + + // TODO: The rest of the code in this function is specific to ProjectN only. When we kill the binder, get rid of this + // linear search code (the only API that should be used for the lookup is the one above) // Get module IntPtr associatedModule = RuntimeAugments.GetOSModuleFromPointer(maybeInstantiatingAndUnboxingStub);