diff --git a/src/coreclr/nativeaot/Common/src/Internal/Runtime/CompilerHelpers/StartupCodeHelpers.cs b/src/coreclr/nativeaot/Common/src/Internal/Runtime/CompilerHelpers/StartupCodeHelpers.cs index 21c9282c1ecf87..10947ec8831368 100644 --- a/src/coreclr/nativeaot/Common/src/Internal/Runtime/CompilerHelpers/StartupCodeHelpers.cs +++ b/src/coreclr/nativeaot/Common/src/Internal/Runtime/CompilerHelpers/StartupCodeHelpers.cs @@ -153,7 +153,7 @@ private static unsafe void InitializeGlobalTablesForModule(TypeManagerHandle typ private static unsafe void InitializeModuleFrozenObjectSegment(IntPtr segmentStart, int length) { - if (RuntimeImports.RhpRegisterFrozenSegment(segmentStart, (IntPtr)length) == IntPtr.Zero) + if (RuntimeImports.RhRegisterFrozenSegment((void*)segmentStart, (nuint)length, (nuint)length, (nuint)length) == IntPtr.Zero) { // This should only happen if we ran out of memory. RuntimeExceptionHelpers.FailFast("Failed to register frozen object segment for the module."); diff --git a/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/RuntimeImports.cs b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/RuntimeImports.cs index 76835dd8b7ed95..c1663688004012 100644 --- a/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/RuntimeImports.cs +++ b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/RuntimeImports.cs @@ -13,14 +13,6 @@ internal static class RuntimeImports { private const string RuntimeLibrary = "*"; - [MethodImpl(MethodImplOptions.InternalCall)] - [RuntimeImport(RuntimeLibrary, "RhpRegisterFrozenSegment")] - internal static extern IntPtr RhpRegisterFrozenSegment(IntPtr pSegmentStart, IntPtr length); - - [MethodImpl(MethodImplOptions.InternalCall)] - [RuntimeImport(RuntimeLibrary, "RhpUnregisterFrozenSegment")] - internal static extern void RhpUnregisterFrozenSegment(IntPtr pSegmentHandle); - [RuntimeImport(RuntimeLibrary, "RhpGetModuleSection")] [MethodImplAttribute(MethodImplOptions.InternalCall)] private static extern IntPtr RhGetModuleSection(ref TypeManagerHandle module, ReadyToRunSectionType section, out int length); diff --git a/src/coreclr/nativeaot/Runtime/MiscHelpers.cpp b/src/coreclr/nativeaot/Runtime/MiscHelpers.cpp index 1dd79b73d9436c..b30678b38f2bc4 100644 --- a/src/coreclr/nativeaot/Runtime/MiscHelpers.cpp +++ b/src/coreclr/nativeaot/Runtime/MiscHelpers.cpp @@ -356,12 +356,17 @@ EXTERN_C NATIVEAOT_API int32_t __cdecl RhpGetCurrentThreadStackTrace(void* pOutp return RhpCalculateStackTraceWorker(pOutputBuffer, outputBufferLength, pAddressInCurrentFrame); } -COOP_PINVOKE_HELPER(void*, RhpRegisterFrozenSegment, (void* pSegmentStart, size_t length)) +EXTERN_C NATIVEAOT_API void* __cdecl RhRegisterFrozenSegment(void * pSection, size_t allocSize, size_t commitSize, size_t reservedSize) { - return RedhawkGCInterface::RegisterFrozenSegment(pSegmentStart, length); + return RedhawkGCInterface::RegisterFrozenSegment(pSection, allocSize, commitSize, reservedSize); } -COOP_PINVOKE_HELPER(void, RhpUnregisterFrozenSegment, (void* pSegmentHandle)) +EXTERN_C NATIVEAOT_API void __cdecl RhUpdateFrozenSegment(void* pSegmentHandle, uint8_t* allocated, uint8_t* committed) +{ + RedhawkGCInterface::UpdateFrozenSegment((GcSegmentHandle)pSegmentHandle, allocated, committed); +} + +EXTERN_C NATIVEAOT_API void __cdecl RhUnregisterFrozenSegment(void* pSegmentHandle) { RedhawkGCInterface::UnregisterFrozenSegment((GcSegmentHandle)pSegmentHandle); } diff --git a/src/coreclr/nativeaot/Runtime/gcrhenv.cpp b/src/coreclr/nativeaot/Runtime/gcrhenv.cpp index 09280870510c95..3b457682140aed 100644 --- a/src/coreclr/nativeaot/Runtime/gcrhenv.cpp +++ b/src/coreclr/nativeaot/Runtime/gcrhenv.cpp @@ -368,16 +368,19 @@ void RedhawkGCInterface::BulkEnumGcObjRef(PTR_RtuObjectRef pRefs, uint32_t cRefs } // static -GcSegmentHandle RedhawkGCInterface::RegisterFrozenSegment(void * pSection, size_t SizeSection) +GcSegmentHandle RedhawkGCInterface::RegisterFrozenSegment(void * pSection, size_t allocSize, size_t commitSize, size_t reservedSize) { + ASSERT(allocSize <= commitSize); + ASSERT(commitSize <= reservedSize); + #ifdef FEATURE_BASICFREEZE segment_info seginfo; seginfo.pvMem = pSection; seginfo.ibFirstObject = sizeof(ObjHeader); - seginfo.ibAllocated = SizeSection; - seginfo.ibCommit = seginfo.ibAllocated; - seginfo.ibReserved = seginfo.ibAllocated; + seginfo.ibAllocated = allocSize; + seginfo.ibCommit = commitSize; + seginfo.ibReserved = reservedSize; return (GcSegmentHandle)GCHeapUtilities::GetGCHeap()->RegisterFrozenSegment(&seginfo); #else // FEATURE_BASICFREEZE @@ -385,6 +388,14 @@ GcSegmentHandle RedhawkGCInterface::RegisterFrozenSegment(void * pSection, size_ #endif // FEATURE_BASICFREEZE } +// static +void RedhawkGCInterface::UpdateFrozenSegment(GcSegmentHandle seg, uint8_t* allocated, uint8_t* committed) +{ + ASSERT(allocated <= committed); + + GCHeapUtilities::GetGCHeap()->UpdateFrozenSegment((segment_handle)seg, allocated, committed); +} + // static void RedhawkGCInterface::UnregisterFrozenSegment(GcSegmentHandle segment) { diff --git a/src/coreclr/nativeaot/Runtime/gcrhinterface.h b/src/coreclr/nativeaot/Runtime/gcrhinterface.h index b9279e00902157..b53e9af9e083e8 100644 --- a/src/coreclr/nativeaot/Runtime/gcrhinterface.h +++ b/src/coreclr/nativeaot/Runtime/gcrhinterface.h @@ -129,7 +129,8 @@ class RedhawkGCInterface void * pfnEnumCallback, void * pvCallbackData); - static GcSegmentHandle RegisterFrozenSegment(void * pSection, size_t SizeSection); + static GcSegmentHandle RegisterFrozenSegment(void * pSection, size_t allocSize, size_t commitSize, size_t reservedSize); + static void UpdateFrozenSegment(GcSegmentHandle seg, uint8_t* allocated, uint8_t* committed); static void UnregisterFrozenSegment(GcSegmentHandle segment); #ifdef FEATURE_GC_STRESS diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/FrozenObjectHeapManager.Unix.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/FrozenObjectHeapManager.Unix.cs new file mode 100644 index 00000000000000..7ebc8867c5565e --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/FrozenObjectHeapManager.Unix.cs @@ -0,0 +1,39 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; + +namespace Internal.Runtime +{ + internal unsafe partial class FrozenObjectHeapManager + { + static void* ClrVirtualReserve(nuint size) + { + // The shim will return null for failure + return (void*)Interop.Sys.MMap( + 0, + size, + Interop.Sys.MemoryMappedProtections.PROT_NONE, + Interop.Sys.MemoryMappedFlags.MAP_PRIVATE | Interop.Sys.MemoryMappedFlags.MAP_ANONYMOUS, + -1, + 0); + + } + + static void* ClrVirtualCommit(void* pBase, nuint size) + { + int result = Interop.Sys.MProtect( + (nint)pBase, + size, + Interop.Sys.MemoryMappedProtections.PROT_READ | Interop.Sys.MemoryMappedProtections.PROT_WRITE); + + return result == 0 ? pBase : null; + } + + static void ClrVirtualFree(void* pBase, nuint size) + { + Debug.Assert(size != 0); + Interop.Sys.MUnmap((nint)pBase, size); + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/FrozenObjectHeapManager.Windows.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/FrozenObjectHeapManager.Windows.cs new file mode 100644 index 00000000000000..3927b46ae1ff3f --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/FrozenObjectHeapManager.Windows.cs @@ -0,0 +1,31 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; + +namespace Internal.Runtime +{ + internal unsafe partial class FrozenObjectHeapManager + { + static void* ClrVirtualReserve(nuint size) + { + return Interop.Kernel32.VirtualAlloc(null, size, Interop.Kernel32.MemOptions.MEM_RESERVE, Interop.Kernel32.PageOptions.PAGE_READWRITE); + } + + static void* ClrVirtualCommit(void* pBase, nuint size) + { + return Interop.Kernel32.VirtualAlloc(pBase, size, Interop.Kernel32.MemOptions.MEM_COMMIT, Interop.Kernel32.PageOptions.PAGE_READWRITE); + } + + static void ClrVirtualFree(void* pBase, nuint size) + { + // We require the size parameter for Unix implementation sake. + // The Win32 API ignores this parameter because we must pass zero. + // If the caller passed zero, this is going to be broken on Unix + // so let's at least assert that. + Debug.Assert(size != 0); + + Interop.Kernel32.VirtualFree(pBase, 0, Interop.Kernel32.MemOptions.MEM_RELEASE); + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/FrozenObjectHeapManager.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/FrozenObjectHeapManager.cs new file mode 100644 index 00000000000000..597341da8a173e --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/FrozenObjectHeapManager.cs @@ -0,0 +1,213 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime; +using System.Runtime.CompilerServices; +using System.Threading; + +using Debug = System.Diagnostics.Debug; + +// Rewrite of src\coreclr\vm\frozenobjectheap.cpp in C# + +namespace Internal.Runtime +{ + internal unsafe partial class FrozenObjectHeapManager + { + public static readonly FrozenObjectHeapManager Instance = new FrozenObjectHeapManager(); + + private readonly LowLevelLock m_Crst = new LowLevelLock(); + private FrozenObjectSegment m_CurrentSegment; + + // Default size to reserve for a frozen segment + private const nuint FOH_SEGMENT_DEFAULT_SIZE = 4 * 1024 * 1024; + // Size to commit on demand in that reserved space + private const nuint FOH_COMMIT_SIZE = 64 * 1024; + + public T? TryAllocateObject() where T : class + { + MethodTable* pMT = MethodTable.Of(); + return Unsafe.As(TryAllocateObject(pMT, pMT->BaseSize)); + } + + private object? TryAllocateObject(MethodTable* type, nuint objectSize) + { + HalfBakedObject* obj = null; + + m_Crst.Acquire(); + + try + { + Debug.Assert(type != null); + // _ASSERT(FOH_COMMIT_SIZE >= MIN_OBJECT_SIZE); + + // Currently we don't support frozen objects with special alignment requirements + // TODO: We should also give up on arrays of doubles on 32-bit platforms. + // (we currently never allocate them on frozen segments) +#if FEATURE_64BIT_ALIGNMENT + if (type->RequiresAlign8) + { + // Align8 objects are not supported yet + return nullptr; + } +#endif + + // NOTE: objectSize is expected be the full size including header + // _ASSERT(objectSize >= MIN_OBJECT_SIZE); + + if (objectSize > FOH_COMMIT_SIZE) + { + // The current design doesn't allow objects larger than FOH_COMMIT_SIZE and + // since FrozenObjectHeap is just an optimization, let's not fill it with huge objects. + return null; + } + + obj = m_CurrentSegment == null ? null : m_CurrentSegment.TryAllocateObject(type, objectSize); + // obj is nullptr if the current segment is full or hasn't been allocated yet + if (obj == null) + { + nuint newSegmentSize = FOH_SEGMENT_DEFAULT_SIZE; + if (m_CurrentSegment != null) + { + // Double the reserved size to reduce the number of frozen segments in apps with lots of frozen objects + // Use the same size in case if prevSegmentSize*2 operation overflows. + nuint prevSegmentSize = m_CurrentSegment.m_Size; + newSegmentSize = Math.Max(prevSegmentSize, prevSegmentSize * 2); + } + + m_CurrentSegment = new FrozenObjectSegment(newSegmentSize); + + // Try again + obj = m_CurrentSegment.TryAllocateObject(type, objectSize); + + // This time it's not expected to be null + Debug.Assert(obj != null); + } + } // end of m_Crst lock + finally + { + m_Crst.Release(); + } + + IntPtr result = (IntPtr)obj; + + return Unsafe.As(ref result); + } + + private class FrozenObjectSegment + { + // Start of the reserved memory, the first object starts at "m_pStart + sizeof(ObjHeader)" (its pMT) + private byte* m_pStart; + + // Pointer to the end of the current segment, ready to be used as a pMT for a new object + // meaning that "m_pCurrent - sizeof(ObjHeader)" is the actual start of the new object (header). + // + // m_pCurrent <= m_SizeCommitted + public byte* m_pCurrent; + + // Memory committed in the current segment + // + // m_SizeCommitted <= m_pStart + FOH_SIZE_RESERVED + public nuint m_SizeCommitted; + + // Total memory reserved for the current segment + public nuint m_Size; + + private IntPtr m_SegmentHandle; + + public FrozenObjectSegment(nuint sizeHint) + { + m_Size = sizeHint; + + Debug.Assert(m_Size > FOH_COMMIT_SIZE); + Debug.Assert(m_Size % FOH_COMMIT_SIZE == 0); + + void* alloc = ClrVirtualReserve(m_Size); + if (alloc == null) + { + // Try again with the default FOH size + if (m_Size > FOH_SEGMENT_DEFAULT_SIZE) + { + m_Size = FOH_SEGMENT_DEFAULT_SIZE; + Debug.Assert(m_Size > FOH_COMMIT_SIZE); + Debug.Assert(m_Size % FOH_COMMIT_SIZE == 0); + alloc = ClrVirtualReserve(m_Size); + } + + if (alloc == null) + { + throw new OutOfMemoryException(); + } + } + + // Commit a chunk in advance + m_pStart = (byte*)ClrVirtualCommit(alloc, FOH_COMMIT_SIZE); + if (m_pStart == null) + { + ClrVirtualFree(alloc, m_Size); + throw new OutOfMemoryException(); + } + + m_pCurrent = m_pStart + sizeof(ObjHeader); + + m_SegmentHandle = RuntimeImports.RhRegisterFrozenSegment(m_pStart, (nuint)m_pCurrent - (nuint)m_pStart, FOH_COMMIT_SIZE, m_Size); + if (m_SegmentHandle == IntPtr.Zero) + { + ClrVirtualFree(alloc, m_Size); + throw new OutOfMemoryException(); + } + + m_SizeCommitted = FOH_COMMIT_SIZE; + } + + public HalfBakedObject* TryAllocateObject(MethodTable* type, nuint objectSize) + { + Debug.Assert((m_pStart != null) && (m_Size > 0)); + //_ASSERT(IS_ALIGNED(m_pCurrent, DATA_ALIGNMENT)); + //_ASSERT(IS_ALIGNED(objectSize, DATA_ALIGNMENT)); + Debug.Assert(objectSize <= FOH_COMMIT_SIZE); + Debug.Assert(m_pCurrent >= m_pStart + sizeof(ObjHeader)); + + nuint spaceUsed = (nuint)(m_pCurrent - m_pStart); + nuint spaceLeft = m_Size - spaceUsed; + + Debug.Assert(spaceUsed >= (nuint)sizeof(ObjHeader)); + Debug.Assert(spaceLeft >= (nuint)sizeof(ObjHeader)); + + // Test if we have a room for the given object (including extra sizeof(ObjHeader) for next object) + if (spaceLeft - (nuint)sizeof(ObjHeader) < objectSize) + { + return null; + } + + // Check if we need to commit a new chunk + if (spaceUsed + objectSize + (nuint)sizeof(ObjHeader) > m_SizeCommitted) + { + // Make sure we don't go out of bounds during this commit + Debug.Assert(m_SizeCommitted + FOH_COMMIT_SIZE <= m_Size); + + if (ClrVirtualCommit(m_pStart + m_SizeCommitted, FOH_COMMIT_SIZE) == null) + { + throw new OutOfMemoryException(); + } + m_SizeCommitted += FOH_COMMIT_SIZE; + } + + HalfBakedObject* obj = (HalfBakedObject*)m_pCurrent; + obj->SetMethodTable(type); + + m_pCurrent += objectSize; + + RuntimeImports.RhUpdateFrozenSegment(m_SegmentHandle, m_pCurrent, m_pStart + m_SizeCommitted); + + return obj; + } + } + + private struct HalfBakedObject + { + private MethodTable* _methodTable; + public void SetMethodTable(MethodTable* methodTable) => _methodTable = methodTable; + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj b/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj index 79321635129465..bd6f9a3127a34e 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj @@ -94,6 +94,7 @@ Common\src\Internal\Runtime\TypeLoader\ExternalReferencesTable.cs + @@ -252,6 +253,7 @@ + @@ -291,6 +293,7 @@ + @@ -302,6 +305,15 @@ Interop\Unix\System.Native\Interop.Exit.cs + + Interop\Unix\System.Native\Interop.MMap.cs + + + Interop\Unix\System.Native\Interop.MProtect.cs + + + Interop\Unix\System.Native\Interop.MUnmap.cs + diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/GC.NativeAot.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/GC.NativeAot.cs index 3d263d5de6b63d..8ecade80ffc253 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/GC.NativeAot.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/GC.NativeAot.cs @@ -741,14 +741,14 @@ public static long GetTotalMemory(bool forceFullCollection) return size; } - private static IntPtr _RegisterFrozenSegment(IntPtr sectionAddress, IntPtr sectionSize) + private static unsafe IntPtr _RegisterFrozenSegment(IntPtr sectionAddress, IntPtr sectionSize) { - return RuntimeImports.RhpRegisterFrozenSegment(sectionAddress, sectionSize); + return RuntimeImports.RhRegisterFrozenSegment((void*)sectionAddress, (nuint)sectionSize, (nuint)sectionSize, (nuint)sectionSize); } private static void _UnregisterFrozenSegment(IntPtr segmentHandle) { - RuntimeImports.RhpUnregisterFrozenSegment(segmentHandle); + RuntimeImports.RhUnregisterFrozenSegment(segmentHandle); } public static long GetAllocatedBytesForCurrentThread() diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/RuntimeImports.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/RuntimeImports.cs index 46e91a79aa636c..957badf100ed80 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/RuntimeImports.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/RuntimeImports.cs @@ -165,13 +165,17 @@ internal static void RhWaitForPendingFinalizers(bool allowReentrantWait) [RuntimeImport(RuntimeLibrary, "RhGetLastGCDuration")] internal static extern long RhGetLastGCDuration(int generation); - [MethodImpl(MethodImplOptions.InternalCall)] - [RuntimeImport(RuntimeLibrary, "RhpRegisterFrozenSegment")] - internal static extern IntPtr RhpRegisterFrozenSegment(IntPtr pSegmentStart, IntPtr length); + [LibraryImport(RuntimeLibrary)] + [UnmanagedCallConv(CallConvs = new Type[] { typeof(CallConvCdecl) })] + internal static unsafe partial IntPtr RhRegisterFrozenSegment(void* pSegmentStart, nuint allocSize, nuint commitSize, nuint reservedSize); - [MethodImpl(MethodImplOptions.InternalCall)] - [RuntimeImport(RuntimeLibrary, "RhpUnregisterFrozenSegment")] - internal static extern void RhpUnregisterFrozenSegment(IntPtr pSegmentHandle); + [LibraryImport(RuntimeLibrary)] + [UnmanagedCallConv(CallConvs = new Type[] { typeof(CallConvCdecl) })] + internal static unsafe partial void RhUpdateFrozenSegment(IntPtr seg, void* allocated, void* committed); + + [LibraryImport(RuntimeLibrary)] + [UnmanagedCallConv(CallConvs = new Type[] { typeof(CallConvCdecl) })] + internal static partial void RhUnregisterFrozenSegment(IntPtr pSegmentHandle); [MethodImpl(MethodImplOptions.InternalCall)] [RuntimeImport(RuntimeLibrary, "RhRegisterForFullGCNotification")] diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Type.NativeAot.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Type.NativeAot.cs index 0a9cd70dd643e1..d9c30882033000 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Type.NativeAot.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Type.NativeAot.cs @@ -40,31 +40,36 @@ internal static unsafe RuntimeType GetTypeFromMethodTable(MethodTable* pMT) } } + private static class AllocationLockHolder + { + public static LowLevelLock AllocationLock = new LowLevelLock(); + } + [MethodImpl(MethodImplOptions.NoInlining)] private static unsafe RuntimeType GetTypeFromMethodTableSlow(MethodTable* pMT) { - // TODO: instead of fragmenting the frozen object heap, we should have our own allocator - // for objects that live forever outside the GC heap. + // Allocate and set the RuntimeType under a lock - there's no way to free it if there is a race. + AllocationLockHolder.AllocationLock.Acquire(); + try + { + ref RuntimeType? runtimeTypeCache = ref Unsafe.AsRef(pMT->WritableData); + if (runtimeTypeCache != null) + return runtimeTypeCache; - RuntimeType? type = null; - RuntimeImports.RhAllocateNewObject( - (IntPtr)MethodTable.Of(), - (uint)GC_ALLOC_FLAGS.GC_ALLOC_PINNED_OBJECT_HEAP, - Unsafe.AsPointer(ref type)); + RuntimeType? type = FrozenObjectHeapManager.Instance.TryAllocateObject(); + if (type == null) + throw new OutOfMemoryException(); - if (type == null) - throw new OutOfMemoryException(); + type.DangerousSetUnderlyingEEType(pMT); - type.DangerousSetUnderlyingEEType(pMT); + runtimeTypeCache = type; - ref RuntimeType? runtimeTypeCache = ref Unsafe.AsRef(pMT->WritableData); - if (Interlocked.CompareExchange(ref runtimeTypeCache, type, null) == null) + return type; + } + finally { - // Create and leak a GC handle - UnsafeGCHandle.Alloc(type); + AllocationLockHolder.AllocationLock.Release(); } - - return runtimeTypeCache; } // diff --git a/src/coreclr/nativeaot/Test.CoreLib/src/System/Runtime/RuntimeImports.cs b/src/coreclr/nativeaot/Test.CoreLib/src/System/Runtime/RuntimeImports.cs index 5232ab073a6ca8..70d82828cd008e 100644 --- a/src/coreclr/nativeaot/Test.CoreLib/src/System/Runtime/RuntimeImports.cs +++ b/src/coreclr/nativeaot/Test.CoreLib/src/System/Runtime/RuntimeImports.cs @@ -40,13 +40,14 @@ internal static IntPtr RhHandleAlloc(object value, GCHandleType type) return h; } - [MethodImpl(MethodImplOptions.InternalCall)] - [RuntimeImport(RuntimeLibrary, "RhpRegisterFrozenSegment")] - internal static extern IntPtr RhpRegisterFrozenSegment(IntPtr pSegmentStart, IntPtr length); + [DllImport(RuntimeLibrary, CallingConvention = CallingConvention.Cdecl)] + internal static extern unsafe IntPtr RhRegisterFrozenSegment(void* pSegmentStart, nuint allocSize, nuint commitSize, nuint reservedSize); - [MethodImpl(MethodImplOptions.InternalCall)] - [RuntimeImport(RuntimeLibrary, "RhpUnregisterFrozenSegment")] - internal static extern void RhpUnregisterFrozenSegment(IntPtr pSegmentHandle); + [DllImport(RuntimeLibrary, CallingConvention = CallingConvention.Cdecl)] + internal static extern unsafe void RhUpdateFrozenSegment(IntPtr seg, void* allocated, void* committed); + + [DllImport(RuntimeLibrary, CallingConvention = CallingConvention.Cdecl)] + internal static extern void RhUnregisterFrozenSegment(IntPtr pSegmentHandle); [RuntimeImport(RuntimeLibrary, "RhpGetModuleSection")] [MethodImplAttribute(MethodImplOptions.InternalCall)] diff --git a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.MMap.cs b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.MMap.cs index 8797980a80c1a9..07a7e295fa7acb 100644 --- a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.MMap.cs +++ b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.MMap.cs @@ -33,5 +33,12 @@ internal static partial IntPtr MMap( IntPtr addr, ulong len, MemoryMappedProtections prot, MemoryMappedFlags flags, SafeFileHandle fd, long offset); + + // NOTE: Shim returns null pointer on failure, not non-null MAP_FAILED sentinel. + [LibraryImport(Libraries.SystemNative, EntryPoint = "SystemNative_MMap", SetLastError = true)] + internal static partial IntPtr MMap( + IntPtr addr, ulong len, + MemoryMappedProtections prot, MemoryMappedFlags flags, + IntPtr fd, long offset); } } diff --git a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.MProtect.cs b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.MProtect.cs new file mode 100644 index 00000000000000..482848da93cf08 --- /dev/null +++ b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.MProtect.cs @@ -0,0 +1,15 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; +using Microsoft.Win32.SafeHandles; + +internal static partial class Interop +{ + internal static partial class Sys + { + [LibraryImport(Libraries.SystemNative, EntryPoint = "SystemNative_MProtect", SetLastError = true)] + internal static partial int MProtect(IntPtr addr, ulong len, MemoryMappedProtections prot); + } +} diff --git a/src/native/libs/System.Native/entrypoints.c b/src/native/libs/System.Native/entrypoints.c index f4fcd6f8d4d86c..1a5b7dcae503fd 100644 --- a/src/native/libs/System.Native/entrypoints.c +++ b/src/native/libs/System.Native/entrypoints.c @@ -93,6 +93,7 @@ static const Entry s_sysNative[] = DllImportEntry(SystemNative_MksTemps) DllImportEntry(SystemNative_MMap) DllImportEntry(SystemNative_MUnmap) + DllImportEntry(SystemNative_MProtect) DllImportEntry(SystemNative_MAdvise) DllImportEntry(SystemNative_MSync) DllImportEntry(SystemNative_SysConf) diff --git a/src/native/libs/System.Native/pal_io.c b/src/native/libs/System.Native/pal_io.c index d6d4a84e1faeb2..9c65db55c4ee03 100644 --- a/src/native/libs/System.Native/pal_io.c +++ b/src/native/libs/System.Native/pal_io.c @@ -1019,6 +1019,19 @@ int32_t SystemNative_MUnmap(void* address, uint64_t length) return munmap(address, (size_t)length); } +int32_t SystemNative_MProtect(void* address, uint64_t length, int32_t protection) +{ + if (length > SIZE_MAX) + { + errno = ERANGE; + return -1; + } + + protection = ConvertMMapProtection(protection); + + return mprotect(address, (size_t)length, protection); +} + int32_t SystemNative_MAdvise(void* address, uint64_t length, int32_t advice) { if (length > SIZE_MAX) diff --git a/src/native/libs/System.Native/pal_io.h b/src/native/libs/System.Native/pal_io.h index 5ad83e29ed99ee..03fd94cea25417 100644 --- a/src/native/libs/System.Native/pal_io.h +++ b/src/native/libs/System.Native/pal_io.h @@ -600,6 +600,13 @@ PALEXPORT void* SystemNative_MMap(void* address, */ PALEXPORT int32_t SystemNative_MUnmap(void* address, uint64_t length); +/** + * Change the access protections for the specified memory pages. + * + * Returns 0 for success, -1 for failure. Sets errno on failure. + */ +PALEXPORT int32_t SystemNative_MProtect(void* address, uint64_t length, int32_t protection); + /** * Give advice about use of memory. Implemented as shim to madvise(2). *