diff --git a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs index a8cbf2ae7969a..51c60b74f4168 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs @@ -1,28 +1,115 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Buffers.Binary; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Reflection; -using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Serialization; using System.Runtime.Versioning; -using System.Threading; namespace System.Runtime.CompilerServices { public static partial class RuntimeHelpers { [Intrinsic] - [MethodImpl(MethodImplOptions.InternalCall)] - public static extern void InitializeArray(Array array, RuntimeFieldHandle fldHandle); + public static unsafe void InitializeArray(Array array, RuntimeFieldHandle fldHandle) + { + IRuntimeFieldInfo fldInfo = fldHandle.GetRuntimeFieldInfo(); - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern unsafe void* GetSpanDataFrom( + if (array is null) + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array); + + if ((RuntimeFieldHandle.GetAttributes(fldInfo.Value) & FieldAttributes.HasFieldRVA) == 0) + throw new ArgumentException(SR.Argument_BadFieldForInitializeArray); + + // Note that we do not check that the field is actually in the PE file that is initializing + // the array. Basically the data being published is can be accessed by anyone with the proper + // permissions (C# marks these as assembly visibility, and thus are protected from outside + // snooping) + + if (!array.GetCorElementTypeOfElementType().IsPrimitiveType()) // Enum is included + throw new ArgumentException(SR.Argument_MustBePrimitiveArray); + + MethodTable* pMT = GetMethodTable(array); + nuint totalSize = pMT->ComponentSize * array.NativeLength; + + uint size = RuntimeFieldHandle.GetFieldSize(new QCallFieldHandle(ref fldInfo)); + + // make certain you don't go off the end of the rva static + if (totalSize > size) + throw new ArgumentException(SR.Argument_BadFieldForInitializeArray); + + void* src = (void*)RuntimeFieldHandle.GetStaticFieldAddress(fldInfo); + ref byte dst = ref MemoryMarshal.GetArrayDataReference(array); + + Debug.Assert(!pMT->GetArrayElementTypeHandle().AsMethodTable()->ContainsGCPointers); + + if (BitConverter.IsLittleEndian) + { + SpanHelpers.Memmove(ref dst, ref *(byte*)src, totalSize); + } + else + { + switch (pMT->ComponentSize) + { + case 1: + SpanHelpers.Memmove(ref dst, ref *(byte*)src, totalSize); + break; + case 2: + BinaryPrimitives.ReverseEndianness( + new ReadOnlySpan(src, array.Length), + new Span(ref Unsafe.As(ref dst), array.Length)); + break; + case 4: + BinaryPrimitives.ReverseEndianness( + new ReadOnlySpan(src, array.Length), + new Span(ref Unsafe.As(ref dst), array.Length)); + break; + case 8: + BinaryPrimitives.ReverseEndianness( + new ReadOnlySpan(src, array.Length), + new Span(ref Unsafe.As(ref dst), array.Length)); + break; + default: + Debug.Fail("Incorrect primitive type size!"); + break; + } + } + } + + private static unsafe void* GetSpanDataFrom( RuntimeFieldHandle fldHandle, RuntimeTypeHandle targetTypeHandle, - out int count); + out int count) + { + IRuntimeFieldInfo fldInfo = fldHandle.GetRuntimeFieldInfo(); + + if ((RuntimeFieldHandle.GetAttributes(fldInfo.Value) & FieldAttributes.HasFieldRVA) == 0) + throw new ArgumentException(SR.Argument_BadFieldForInitializeArray); + + TypeHandle th = new TypeHandle((void*)targetTypeHandle.Value); + Debug.Assert(!th.IsTypeDesc); // TypeDesc can't be used as generic parameter + + if (!th.GetVerifierCorElementType().IsPrimitiveType()) // Enum is included + throw new ArgumentException(SR.Argument_MustBePrimitiveArray); + + uint totalSize = RuntimeFieldHandle.GetFieldSize(new QCallFieldHandle(ref fldInfo)); + uint targetTypeSize = th.AsMethodTable()->GetNumInstanceFieldBytes(); + + IntPtr data = RuntimeFieldHandle.GetStaticFieldAddress(fldInfo); + if (data % targetTypeSize != 0) + throw new ArgumentException(SR.Argument_BadFieldForInitializeArray); + + if (!BitConverter.IsLittleEndian) + { + throw new PlatformNotSupportedException(); + } + + count = (int)(totalSize / targetTypeSize); + return (void*)data; + } // GetObjectValue is intended to allow value classes to be manipulated as 'Object' // but have aliasing behavior of a value class. The intent is that you would use diff --git a/src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs b/src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs index 380981993451e..472f4f6318a3d 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs @@ -1085,7 +1085,7 @@ public RuntimeFieldInfoStub(RuntimeFieldHandleInternal fieldHandle, object keepa } [NonVersionable] - public unsafe struct RuntimeFieldHandle : IEquatable, ISerializable + public unsafe partial struct RuntimeFieldHandle : IEquatable, ISerializable { // Returns handle for interop with EE. The handle is guaranteed to be non-null. internal RuntimeFieldHandle GetNativeHandle() @@ -1187,7 +1187,10 @@ internal static RuntimeType GetApproxDeclaringType(IRuntimeFieldInfo field) internal static extern int GetInstanceFieldOffset(RtFieldInfo field); [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern IntPtr GetStaticFieldAddress(RtFieldInfo field); + internal static extern IntPtr GetStaticFieldAddress(IRuntimeFieldInfo field); + + [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "RuntimeFieldHandle_GetFieldSize")] + internal static partial uint GetFieldSize(QCallFieldHandle field); [MethodImpl(MethodImplOptions.InternalCall)] internal static extern int GetToken(RtFieldInfo field); diff --git a/src/coreclr/classlibnative/bcltype/arraynative.cpp b/src/coreclr/classlibnative/bcltype/arraynative.cpp index a4b458b8ef571..2e4f61e934f24 100644 --- a/src/coreclr/classlibnative/bcltype/arraynative.cpp +++ b/src/coreclr/classlibnative/bcltype/arraynative.cpp @@ -441,116 +441,3 @@ FCIMPL3(void, ArrayNative::SetValue, ArrayBase* refThisUNSAFE, Object* objUNSAFE } } FCIMPLEND - -// This method will initialize an array from a TypeHandle to a field. - -FCIMPL2_IV(void, ArrayNative::InitializeArray, ArrayBase* pArrayRef, FCALLRuntimeFieldHandle structField) -{ - FCALL_CONTRACT; - - BASEARRAYREF arr = BASEARRAYREF(pArrayRef); - REFLECTFIELDREF refField = (REFLECTFIELDREF)ObjectToOBJECTREF(FCALL_RFH_TO_REFLECTFIELD(structField)); - HELPER_METHOD_FRAME_BEGIN_2(arr, refField); - - if ((arr == 0) || (refField == NULL)) - COMPlusThrow(kArgumentNullException); - - FieldDesc* pField = (FieldDesc*) refField->GetField(); - - if (!pField->IsRVA()) - COMPlusThrow(kArgumentException); - - // Note that we do not check that the field is actually in the PE file that is initializing - // the array. Basically the data being published is can be accessed by anyone with the proper - // permissions (C# marks these as assembly visibility, and thus are protected from outside - // snooping) - - if (!CorTypeInfo::IsPrimitiveType(arr->GetArrayElementType()) && !arr->GetArrayElementTypeHandle().IsEnum()) - COMPlusThrow(kArgumentException); - - SIZE_T dwCompSize = arr->GetComponentSize(); - SIZE_T dwElemCnt = arr->GetNumComponents(); - SIZE_T dwTotalSize = dwCompSize * dwElemCnt; - - DWORD size = pField->LoadSize(); - - // make certain you don't go off the end of the rva static - if (dwTotalSize > size) - COMPlusThrow(kArgumentException); - - void *src = pField->GetStaticAddressHandle(NULL); - void *dest = arr->GetDataPtr(); - -#if BIGENDIAN - DWORD i; - switch (dwCompSize) { - case 1: - memcpyNoGCRefs(dest, src, dwElemCnt); - break; - case 2: - for (i = 0; i < dwElemCnt; i++) - *((UINT16*)dest + i) = GET_UNALIGNED_VAL16((UINT16*)src + i); - break; - case 4: - for (i = 0; i < dwElemCnt; i++) - *((UINT32*)dest + i) = GET_UNALIGNED_VAL32((UINT32*)src + i); - break; - case 8: - for (i = 0; i < dwElemCnt; i++) - *((UINT64*)dest + i) = GET_UNALIGNED_VAL64((UINT64*)src + i); - break; - default: - // should not reach here. - UNREACHABLE_MSG("Incorrect primitive type size!"); - break; - } -#else - memcpyNoGCRefs(dest, src, dwTotalSize); -#endif - - HELPER_METHOD_FRAME_END(); -} -FCIMPLEND - -FCIMPL3_VVI(void*, ArrayNative::GetSpanDataFrom, FCALLRuntimeFieldHandle structField, FCALLRuntimeTypeHandle targetTypeUnsafe, INT32* count) -{ - FCALL_CONTRACT; - struct - { - REFLECTFIELDREF refField; - REFLECTCLASSBASEREF refClass; - } gc; - gc.refField = (REFLECTFIELDREF)ObjectToOBJECTREF(FCALL_RFH_TO_REFLECTFIELD(structField)); - gc.refClass = (REFLECTCLASSBASEREF)ObjectToOBJECTREF(FCALL_RTH_TO_REFLECTCLASS(targetTypeUnsafe)); - void* data = NULL; - HELPER_METHOD_FRAME_BEGIN_RET_PROTECT(gc); - - FieldDesc* pField = (FieldDesc*)gc.refField->GetField(); - - if (!pField->IsRVA()) - COMPlusThrow(kArgumentException); - - TypeHandle targetTypeHandle = gc.refClass->GetType(); - if (!CorTypeInfo::IsPrimitiveType(targetTypeHandle.GetSignatureCorElementType()) && !targetTypeHandle.IsEnum()) - COMPlusThrow(kArgumentException); - - DWORD totalSize = pField->LoadSize(); - DWORD targetTypeSize = targetTypeHandle.GetSize(); - - data = pField->GetStaticAddressHandle(NULL); - _ASSERTE(data != NULL); - _ASSERTE(count != NULL); - - if (AlignUp((UINT_PTR)data, targetTypeSize) != (UINT_PTR)data) - COMPlusThrow(kArgumentException); - - *count = (INT32)totalSize / targetTypeSize; - -#if BIGENDIAN - COMPlusThrow(kPlatformNotSupportedException); -#endif - - HELPER_METHOD_FRAME_END(); - return data; -} -FCIMPLEND diff --git a/src/coreclr/classlibnative/bcltype/arraynative.h b/src/coreclr/classlibnative/bcltype/arraynative.h index 508cd776ddff7..409bca1e7408e 100644 --- a/src/coreclr/classlibnative/bcltype/arraynative.h +++ b/src/coreclr/classlibnative/bcltype/arraynative.h @@ -31,13 +31,6 @@ class ArrayNative // This set of methods will set a value in an array static FCDECL3(void, SetValue, ArrayBase* refThisUNSAFE, Object* objUNSAFE, INT_PTR flattenedIndex); - // This method will initialize an array from a TypeHandle - // to a field. - static FCDECL2_IV(void, InitializeArray, ArrayBase* vArrayRef, FCALLRuntimeFieldHandle structField); - - // This method will acquire data to create a span from a TypeHandle - // to a field. - static FCDECL3_VVI(void*, GetSpanDataFrom, FCALLRuntimeFieldHandle structField, FCALLRuntimeTypeHandle targetTypeUnsafe, INT32* count); }; extern "C" void QCALLTYPE Array_CreateInstance(QCall::TypeHandle pTypeHnd, INT32 rank, INT32* pLengths, INT32* pBounds, BOOL createFromArrayType, QCall::ObjectHandleOnStack retArray); diff --git a/src/coreclr/vm/ecalllist.h b/src/coreclr/vm/ecalllist.h index 7d7681894bef7..67b855835deb0 100644 --- a/src/coreclr/vm/ecalllist.h +++ b/src/coreclr/vm/ecalllist.h @@ -439,8 +439,6 @@ FCFuncStart(gMonitorFuncs) FCFuncEnd() FCFuncStart(gRuntimeHelpers) - FCFuncElement("InitializeArray", ArrayNative::InitializeArray) - FCFuncElement("GetSpanDataFrom", ArrayNative::GetSpanDataFrom) FCFuncElement("PrepareDelegate", ReflectionInvocation::PrepareDelegate) FCFuncElement("GetHashCode", ObjectNative::GetHashCode) FCFuncElement("TryGetHashCode", ObjectNative::TryGetHashCode) diff --git a/src/coreclr/vm/qcall.h b/src/coreclr/vm/qcall.h index e3154c7b1334c..b63d2ffa7beac 100644 --- a/src/coreclr/vm/qcall.h +++ b/src/coreclr/vm/qcall.h @@ -293,6 +293,24 @@ class QCall } }; + struct FieldHandle + { + Object ** m_ppObject; + FieldDesc * m_pField; + + operator FieldDesc * () + { + LIMITED_METHOD_CONTRACT; + return m_pField; + } + + FieldDesc * operator -> () + { + LIMITED_METHOD_CONTRACT; + return m_pField; + } + }; + struct LoaderAllocatorHandle { LoaderAllocator * m_pLoaderAllocator; diff --git a/src/coreclr/vm/qcallentrypoints.cpp b/src/coreclr/vm/qcallentrypoints.cpp index e038db62831d9..71f7883fa15e6 100644 --- a/src/coreclr/vm/qcallentrypoints.cpp +++ b/src/coreclr/vm/qcallentrypoints.cpp @@ -132,6 +132,7 @@ static const Entry s_QCall[] = DllImportEntry(RuntimeModule_GetScopeName) DllImportEntry(RuntimeModule_GetFullyQualifiedName) DllImportEntry(RuntimeModule_GetTypes) + DllImportEntry(RuntimeFieldHandle_GetFieldSize) DllImportEntry(StackFrame_GetMethodDescFromNativeIP) DllImportEntry(ModuleBuilder_GetStringConstant) DllImportEntry(ModuleBuilder_GetTypeRef) diff --git a/src/coreclr/vm/reflectioninvocation.cpp b/src/coreclr/vm/reflectioninvocation.cpp index f57e21801253a..65192d5a68738 100644 --- a/src/coreclr/vm/reflectioninvocation.cpp +++ b/src/coreclr/vm/reflectioninvocation.cpp @@ -1246,17 +1246,32 @@ FCIMPL1(void*, RuntimeFieldHandle::GetStaticFieldAddress, ReflectFieldObject *pF // IsFastPathSupported needs to checked before calling this method. _ASSERTE(IsFastPathSupportedHelper(pFieldDesc)); - PTR_BYTE base = 0; - if (!pFieldDesc->IsRVA()) + if (pFieldDesc->IsRVA()) { - // For RVA the base is ignored and offset is used. - base = pFieldDesc->GetBase(); + Module* pModule = pFieldDesc->GetModule(); + return pModule->GetRvaField(pFieldDesc->GetOffset()); + } + else + { + PTR_BYTE base = pFieldDesc->GetBase(); + return PTR_VOID(base + pFieldDesc->GetOffset()); } - - return PTR_VOID(base + pFieldDesc->GetOffset()); } FCIMPLEND +extern "C" UINT QCALLTYPE RuntimeFieldHandle_GetFieldSize(QCall::FieldHandle pField) +{ + QCALL_CONTRACT; + + UINT ret = 0; + + BEGIN_QCALL; + ret = pField->LoadSize(); + END_QCALL; + + return ret; +} + extern "C" void QCALLTYPE ReflectionInvocation_CompileMethod(MethodDesc * pMD) { QCALL_CONTRACT; diff --git a/src/coreclr/vm/runtimehandles.h b/src/coreclr/vm/runtimehandles.h index 56bb2df245f9f..e0504d952880d 100644 --- a/src/coreclr/vm/runtimehandles.h +++ b/src/coreclr/vm/runtimehandles.h @@ -308,6 +308,7 @@ class RuntimeFieldHandle { static FCDECL1(FC_BOOL_RET, AcquiresContextFromThis, FieldDesc *pField); static FCDECL1(Object*, GetLoaderAllocator, FieldDesc *pField); }; +extern "C" UINT QCALLTYPE RuntimeFieldHandle_GetFieldSize(QCall::FieldHandle pField); class ModuleHandle { diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/QCallHandles.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/QCallHandles.cs index 240e9932bf624..e6ab54d12e5d7 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/QCallHandles.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/QCallHandles.cs @@ -95,4 +95,25 @@ internal QCallTypeHandle(ref RuntimeTypeHandle rth) _handle = rth.Value; } } + + // Wraps IRuntimeFieldInfo into a handle. Used to pass IRuntimeFieldInfo to native code without letting it be collected + internal unsafe ref struct QCallFieldHandle + { + private void* _ptr; + private IntPtr _handle; + +#if CORECLR + internal QCallFieldHandle(ref IRuntimeFieldInfo field) + { + _ptr = Unsafe.AsPointer(ref field); + _handle = field.Value.Value; + } +#endif + + internal QCallFieldHandle(ref RuntimeFieldHandle rth) + { + _ptr = Unsafe.AsPointer(ref rth); + _handle = rth.Value; + } + } }