diff --git a/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj b/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj
index b86d5f6f80ab37..78141e32f0fa66 100644
--- a/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj
+++ b/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj
@@ -186,7 +186,7 @@
-
+
diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeCustomAttributeData.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeCustomAttributeData.CoreCLR.cs
new file mode 100644
index 00000000000000..061b1c4e5721c8
--- /dev/null
+++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeCustomAttributeData.CoreCLR.cs
@@ -0,0 +1,866 @@
+// 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.Collections.Generic;
+using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Text;
+
+namespace System.Reflection
+{
+ internal sealed partial class RuntimeCustomAttributeData : CustomAttributeData
+ {
+ #region Private Static Methods
+ private static IList GetCustomAttributes(RuntimeModule module, int tkTarget)
+ {
+ CustomAttributeRecord[] records = GetCustomAttributeRecords(module, tkTarget);
+ if (records.Length == 0)
+ {
+ return Array.Empty();
+ }
+
+ CustomAttributeData[] customAttributes = new CustomAttributeData[records.Length];
+ for (int i = 0; i < records.Length; i++)
+ customAttributes[i] = new RuntimeCustomAttributeData(module, records[i].tkCtor, in records[i].blob);
+
+ return Array.AsReadOnly(customAttributes);
+ }
+ #endregion
+
+ #region Internal Static Members
+ internal static CustomAttributeRecord[] GetCustomAttributeRecords(RuntimeModule module, int targetToken)
+ {
+ MetadataImport scope = module.MetadataImport;
+
+ scope.EnumCustomAttributes(targetToken, out MetadataEnumResult tkCustomAttributeTokens);
+
+ if (tkCustomAttributeTokens.Length == 0)
+ {
+ return [];
+ }
+
+ CustomAttributeRecord[] records = new CustomAttributeRecord[tkCustomAttributeTokens.Length];
+
+ for (int i = 0; i < records.Length; i++)
+ {
+ scope.GetCustomAttributeProps(tkCustomAttributeTokens[i],
+ out records[i].tkCtor.Value, out records[i].blob);
+ }
+ GC.KeepAlive(module);
+
+ return records;
+ }
+
+ internal static CustomAttributeTypedArgument Filter(IList attrs, Type? caType, int parameter)
+ {
+ for (int i = 0; i < attrs.Count; i++)
+ {
+ if (attrs[i].Constructor.DeclaringType == caType)
+ {
+ return attrs[i].ConstructorArguments[parameter];
+ }
+ }
+
+ return default;
+ }
+ #endregion
+ }
+
+ public readonly partial struct CustomAttributeTypedArgument
+ {
+ private static RuntimeType ResolveType(RuntimeModule scope, string typeName)
+ {
+ RuntimeType type = TypeNameResolver.GetTypeReferencedByCustomAttribute(typeName, scope);
+ Debug.Assert(type is not null);
+ return type;
+ }
+ }
+
+ internal struct CustomAttributeRecord
+ {
+ internal ConstArray blob;
+ internal MetadataToken tkCtor;
+
+ public CustomAttributeRecord(int token, ConstArray blob)
+ {
+ tkCtor = new MetadataToken(token);
+ this.blob = blob;
+ }
+ }
+
+ internal sealed partial class CustomAttributeEncodedArgument
+ {
+ private static CustomAttributeEncodedArgument ParseCustomAttributeValue(
+ ref CustomAttributeDataParser parser,
+ CustomAttributeType type,
+ RuntimeModule module)
+ {
+ CustomAttributeType attributeType = type.EncodedType == CustomAttributeEncoding.Object
+ ? ParseCustomAttributeType(ref parser, module)
+ : type;
+
+ CustomAttributeEncodedArgument arg = new(attributeType);
+
+ CustomAttributeEncoding underlyingType = attributeType.EncodedType == CustomAttributeEncoding.Enum
+ ? attributeType.EncodedEnumType
+ : attributeType.EncodedType;
+
+ switch (underlyingType)
+ {
+ case CustomAttributeEncoding.Boolean:
+ case CustomAttributeEncoding.Byte:
+ case CustomAttributeEncoding.SByte:
+ arg.PrimitiveValue = new PrimitiveValue() { Byte4 = parser.GetU1() };
+ break;
+ case CustomAttributeEncoding.Char:
+ case CustomAttributeEncoding.Int16:
+ case CustomAttributeEncoding.UInt16:
+ arg.PrimitiveValue = new PrimitiveValue() { Byte4 = parser.GetU2() };
+ break;
+ case CustomAttributeEncoding.Int32:
+ case CustomAttributeEncoding.UInt32:
+ arg.PrimitiveValue = new PrimitiveValue() { Byte4 = parser.GetI4() };
+ break;
+ case CustomAttributeEncoding.Int64:
+ case CustomAttributeEncoding.UInt64:
+ arg.PrimitiveValue = new PrimitiveValue() { Byte8 = parser.GetI8() };
+ break;
+ case CustomAttributeEncoding.Float:
+ arg.PrimitiveValue = new PrimitiveValue() { Byte4 = BitConverter.SingleToInt32Bits(parser.GetR4()) };
+ break;
+ case CustomAttributeEncoding.Double:
+ arg.PrimitiveValue = new PrimitiveValue() { Byte8 = BitConverter.DoubleToInt64Bits(parser.GetR8()) };
+ break;
+ case CustomAttributeEncoding.String:
+ case CustomAttributeEncoding.Type:
+ arg.StringValue = parser.GetString();
+ break;
+ case CustomAttributeEncoding.Array:
+ {
+ arg.ArrayValue = null;
+ int len = parser.GetI4();
+ if (len != -1) // indicates array is null - ECMA-335 II.23.3.
+ {
+ attributeType = new CustomAttributeType(
+ attributeType.EncodedArrayType,
+ CustomAttributeEncoding.Undefined, // Array type
+ attributeType.EncodedEnumType,
+ attributeType.EnumType);
+ arg.ArrayValue = new CustomAttributeEncodedArgument[len];
+ for (int i = 0; i < len; ++i)
+ {
+ arg.ArrayValue[i] = ParseCustomAttributeValue(ref parser, attributeType, module);
+ }
+ }
+ break;
+ }
+ default:
+ throw new BadImageFormatException();
+ }
+
+ return arg;
+ }
+
+ private static CustomAttributeType ParseCustomAttributeType(ref CustomAttributeDataParser parser, RuntimeModule module)
+ {
+ CustomAttributeEncoding arrayTag = CustomAttributeEncoding.Undefined;
+ CustomAttributeEncoding enumTag = CustomAttributeEncoding.Undefined;
+ Type? enumType = null;
+
+ CustomAttributeEncoding tag = parser.GetTag();
+ if (tag is CustomAttributeEncoding.Array)
+ {
+ arrayTag = parser.GetTag();
+ }
+
+ // Load the enum type if needed.
+ if (tag is CustomAttributeEncoding.Enum
+ || (tag is CustomAttributeEncoding.Array
+ && arrayTag is CustomAttributeEncoding.Enum))
+ {
+ // We cannot determine the underlying type without loading the enum.
+ string enumTypeMaybe = parser.GetString() ?? throw new BadImageFormatException();
+ enumType = TypeNameResolver.GetTypeReferencedByCustomAttribute(enumTypeMaybe, module);
+ if (!enumType.IsEnum)
+ {
+ throw new BadImageFormatException();
+ }
+
+ enumTag = RuntimeCustomAttributeData.TypeToCustomAttributeEncoding((RuntimeType)enumType.GetEnumUnderlyingType());
+ }
+ return new CustomAttributeType(tag, arrayTag, enumTag, enumType);
+ }
+
+ ///
+ /// Used to parse CustomAttribute data. See ECMA-335 II.23.3.
+ ///
+ private ref struct CustomAttributeDataParser
+ {
+ private int _curr;
+ private ReadOnlySpan _blob;
+
+ public CustomAttributeDataParser(ConstArray attributeBlob)
+ {
+ unsafe
+ {
+ _blob = new ReadOnlySpan((void*)attributeBlob.Signature, attributeBlob.Length);
+ }
+ _curr = 0;
+ }
+
+ private ReadOnlySpan PeekData(int size) => _blob.Slice(_curr, size);
+
+ private ReadOnlySpan ReadData(int size)
+ {
+ ReadOnlySpan tmp = PeekData(size);
+ Debug.Assert(size <= (_blob.Length - _curr));
+ _curr += size;
+ return tmp;
+ }
+
+ public byte GetU1()
+ {
+ ReadOnlySpan tmp = ReadData(sizeof(byte));
+ return tmp[0];
+ }
+
+ public sbyte GetI1() => (sbyte)GetU1();
+
+ public ushort GetU2()
+ {
+ ReadOnlySpan tmp = ReadData(sizeof(ushort));
+ return BinaryPrimitives.ReadUInt16LittleEndian(tmp);
+ }
+
+ public short GetI2() => (short)GetU2();
+
+ public uint GetU4()
+ {
+ ReadOnlySpan tmp = ReadData(sizeof(uint));
+ return BinaryPrimitives.ReadUInt32LittleEndian(tmp);
+ }
+
+ public int GetI4() => (int)GetU4();
+
+ public ulong GetU8()
+ {
+ ReadOnlySpan tmp = ReadData(sizeof(ulong));
+ return BinaryPrimitives.ReadUInt64LittleEndian(tmp);
+ }
+
+ public long GetI8() => (long)GetU8();
+
+ public float GetR4()
+ {
+ ReadOnlySpan tmp = ReadData(sizeof(float));
+ return BinaryPrimitives.ReadSingleLittleEndian(tmp);
+ }
+
+ public CustomAttributeEncoding GetTag()
+ {
+ return (CustomAttributeEncoding)GetI1();
+ }
+
+ public double GetR8()
+ {
+ ReadOnlySpan tmp = ReadData(sizeof(double));
+ return BinaryPrimitives.ReadDoubleLittleEndian(tmp);
+ }
+
+ public ushort GetProlog() => GetU2();
+
+ public bool ValidateProlog()
+ {
+ ushort val = GetProlog();
+ return val == 0x0001;
+ }
+
+ public string? GetString()
+ {
+ byte packedLengthBegin = PeekData(sizeof(byte))[0];
+
+ // Check if the embedded string indicates a 'null' string (0xff).
+ if (packedLengthBegin == 0xff) // ECMA 335- II.23.3
+ {
+ // Consume the indicator.
+ ReadData(1);
+ return null;
+ }
+
+ // Not a null string, return a non-null string value.
+ // The embedded string a UTF-8 prefixed by an ECMA-335 packed integer.
+ int length = GetPackedLength(packedLengthBegin);
+ if (length == 0)
+ {
+ return string.Empty;
+ }
+
+ ReadOnlySpan utf8ByteSpan = ReadData(length);
+ return Encoding.UTF8.GetString(utf8ByteSpan);
+ }
+
+ private int GetPackedLength(byte firstByte)
+ {
+ if ((firstByte & 0x80) == 0)
+ {
+ // Consume one byte.
+ ReadData(1);
+ return firstByte & 0x7f;
+ }
+
+ int len;
+ ReadOnlySpan data;
+ if ((firstByte & 0xC0) == 0x80)
+ {
+ // Consume the bytes.
+ data = ReadData(2);
+ len = (data[0] & 0x3f) << 8;
+ return len + data[1];
+ }
+
+ if ((firstByte & 0xE0) == 0xC0)
+ {
+ // Consume the bytes.
+ data = ReadData(4);
+ len = (data[0] & 0x1f) << 24;
+ len += data[1] << 16;
+ len += data[2] << 8;
+ return len + data[3];
+ }
+
+ throw new OverflowException();
+ }
+ }
+ }
+
+ internal static unsafe partial class CustomAttribute
+ {
+ internal static bool IsAttributeDefined(RuntimeModule decoratedModule, int decoratedMetadataToken, int attributeCtorToken)
+ {
+ return IsCustomAttributeDefined(decoratedModule, decoratedMetadataToken, null, attributeCtorToken, false);
+ }
+
+ internal static bool IsCustomAttributeDefined(
+ RuntimeModule decoratedModule, int decoratedMetadataToken, RuntimeType? attributeFilterType)
+ {
+ return IsCustomAttributeDefined(decoratedModule, decoratedMetadataToken, attributeFilterType, 0, false);
+ }
+
+ private static bool IsCustomAttributeDefined(
+ RuntimeModule decoratedModule, int decoratedMetadataToken, RuntimeType? attributeFilterType, int attributeCtorToken, bool mustBeInheritable)
+ {
+ MetadataImport scope = decoratedModule.MetadataImport;
+
+ scope.EnumCustomAttributes(decoratedMetadataToken, out MetadataEnumResult attributeTokens);
+
+ if (attributeTokens.Length == 0)
+ {
+ return false;
+ }
+
+ CustomAttributeRecord record = default;
+ if (attributeFilterType is not null)
+ {
+ Debug.Assert(attributeCtorToken == 0);
+
+ ListBuilder