diff --git a/src/libraries/System.Reflection.Metadata/src/System.Reflection.Metadata.csproj b/src/libraries/System.Reflection.Metadata/src/System.Reflection.Metadata.csproj
index 4b183a58768914..75258d65b0e081 100644
--- a/src/libraries/System.Reflection.Metadata/src/System.Reflection.Metadata.csproj
+++ b/src/libraries/System.Reflection.Metadata/src/System.Reflection.Metadata.csproj
@@ -6,6 +6,8 @@
$(NetCoreAppCurrent);netstandard2.0;net461
enable
true
+ true
+ 1
This packages provides a low-level .NET (ECMA-335) metadata reader and writer. It's geared for performance and is the ideal choice for building higher-level libraries that intend to provide their own object model, such as compilers.
Commonly Used Types:
diff --git a/src/libraries/System.Reflection.Metadata/src/System/Reflection/PortableExecutable/ManagedTextSection.cs b/src/libraries/System.Reflection.Metadata/src/System/Reflection/PortableExecutable/ManagedTextSection.cs
index 33bd80dec21f75..1762b12d928ba0 100644
--- a/src/libraries/System.Reflection.Metadata/src/System/Reflection/PortableExecutable/ManagedTextSection.cs
+++ b/src/libraries/System.Reflection.Metadata/src/System/Reflection/PortableExecutable/ManagedTextSection.cs
@@ -121,7 +121,7 @@ public ManagedTextSection(
public const int MappedFieldDataAlignment = 8;
- public int CalculateOffsetToMappedFieldDataStream()
+ internal int CalculateOffsetToMappedFieldDataStreamUnaligned()
{
int result = ComputeOffsetToImportTable();
@@ -135,6 +135,16 @@ public int CalculateOffsetToMappedFieldDataStream()
return result;
}
+ public int CalculateOffsetToMappedFieldDataStream()
+ {
+ int result = CalculateOffsetToMappedFieldDataStreamUnaligned();
+ if (MappedFieldDataSize != 0)
+ {
+ result = BitArithmetic.Align(result, MappedFieldDataAlignment);
+ }
+ return result;
+ }
+
internal int ComputeOffsetToDebugDirectory()
{
Debug.Assert(MetadataSize % 4 == 0);
@@ -185,7 +195,7 @@ public int GetEntryPointAddress(int rva)
{
// TODO: constants
return RequiresStartupStub ?
- rva + CalculateOffsetToMappedFieldDataStream() - (Is32Bit ? 6 : 10) :
+ rva + CalculateOffsetToMappedFieldDataStreamUnaligned() - (Is32Bit ? 6 : 10) :
0;
}
@@ -293,6 +303,8 @@ public void Serialize(
// mapped field data:
if (mappedFieldDataBuilderOpt != null)
{
+ if (mappedFieldDataBuilderOpt.Count != 0)
+ builder.Align(MappedFieldDataAlignment);
builder.LinkSuffix(mappedFieldDataBuilderOpt);
}
diff --git a/src/libraries/System.Reflection.Metadata/tests/PortableExecutable/PEBuilderTests.cs b/src/libraries/System.Reflection.Metadata/tests/PortableExecutable/PEBuilderTests.cs
index 763e26dbc1811c..c3dab37bbd3590 100644
--- a/src/libraries/System.Reflection.Metadata/tests/PortableExecutable/PEBuilderTests.cs
+++ b/src/libraries/System.Reflection.Metadata/tests/PortableExecutable/PEBuilderTests.cs
@@ -64,7 +64,8 @@ private static void WritePEImage(
Blob mvidFixup = default(Blob),
byte[] privateKeyOpt = null,
bool publicSigned = false,
- Machine machine = 0)
+ Machine machine = 0,
+ BlobBuilder? mappedFieldData = null)
{
var peHeaderBuilder = new PEHeaderBuilder(imageCharacteristics: entryPointHandle.IsNil ? Characteristics.Dll : Characteristics.ExecutableImage,
machine: machine);
@@ -75,7 +76,8 @@ private static void WritePEImage(
ilBuilder,
entryPoint: entryPointHandle,
flags: CorFlags.ILOnly | (privateKeyOpt != null || publicSigned ? CorFlags.StrongNameSigned : 0),
- deterministicIdProvider: content => s_contentId);
+ deterministicIdProvider: content => s_contentId,
+ mappedFieldData: mappedFieldData);
var peBlob = new BlobBuilder();
@@ -487,6 +489,98 @@ private static MethodDefinitionHandle ComplexEmit(MetadataBuilder metadata, Blob
return default(MethodDefinitionHandle);
}
+ [Theory] // Validate FieldRVA alignment on common machine types
+ [MemberData(nameof(AllMachineTypes))]
+ public void FieldRVAAlignmentVerify(Machine machine)
+ {
+ using (var peStream = new MemoryStream())
+ {
+ var ilBuilder = new BlobBuilder();
+ var mappedRVADataBuilder = new BlobBuilder();
+ var metadataBuilder = new MetadataBuilder();
+ double validationNumber = 0.100001;
+ var fieldDef = FieldRVAValidationEmit(metadataBuilder, mappedRVADataBuilder, validationNumber);
+
+ WritePEImage(peStream, metadataBuilder, ilBuilder, default(MethodDefinitionHandle),
+ mappedFieldData: mappedRVADataBuilder,
+ machine: machine);
+
+ // Validate FieldRVA is aligned as ManagedPEBuilder.MappedFieldDataAlignemnt
+ peStream.Position = 0;
+ using (var peReader = new PEReader(peStream, PEStreamOptions.LeaveOpen))
+ {
+ var mdReader = peReader.GetMetadataReader();
+
+ // Validate that there is only 1 field rva entry
+ Assert.Equal(1, mdReader.FieldRvaTable.NumberOfRows);
+
+ // Validate that the RVA is aligned properly (which should be at least an 8 byte alignment
+ Assert.Equal(0, mdReader.FieldRvaTable.GetRva(1) % ManagedPEBuilder.MappedFieldDataAlignment);
+
+ // Validate that the correct data is at the RVA
+ var fieldRVAData = peReader.GetSectionData(mdReader.FieldRvaTable.GetRva(1));
+ Assert.Equal(validationNumber, fieldRVAData.GetReader().ReadDouble());
+ }
+
+ VerifyPE(peStream, machine);
+ }
+ }
+
+ private static FieldDefinitionHandle FieldRVAValidationEmit(MetadataBuilder metadata, BlobBuilder mappedRVAData, double doubleToWriteAsData)
+ {
+ metadata.AddModule(
+ 0,
+ metadata.GetOrAddString("ConsoleApplication.exe"),
+ metadata.GetOrAddGuid(s_guid),
+ default(GuidHandle),
+ default(GuidHandle));
+
+ metadata.AddAssembly(
+ metadata.GetOrAddString("ConsoleApplication"),
+ version: new Version(1, 0, 0, 0),
+ culture: default(StringHandle),
+ publicKey: metadata.GetOrAddBlob(ImmutableArray.Create(Misc.KeyPair_PublicKey)),
+ flags: AssemblyFlags.PublicKey,
+ hashAlgorithm: AssemblyHashAlgorithm.Sha1);
+
+ var mscorlibAssemblyRef = metadata.AddAssemblyReference(
+ name: metadata.GetOrAddString("mscorlib"),
+ version: new Version(4, 0, 0, 0),
+ culture: default(StringHandle),
+ publicKeyOrToken: metadata.GetOrAddBlob(ImmutableArray.Create(0xB7, 0x7A, 0x5C, 0x56, 0x19, 0x34, 0xE0, 0x89)),
+ flags: default(AssemblyFlags),
+ hashValue: default(BlobHandle));
+
+ var systemObjectTypeRef = metadata.AddTypeReference(
+ mscorlibAssemblyRef,
+ metadata.GetOrAddString("System"),
+ metadata.GetOrAddString("Object"));
+
+ mappedRVAData.WriteDouble(doubleToWriteAsData);
+
+ var rvaFieldSignature = new BlobBuilder();
+
+ new BlobEncoder(rvaFieldSignature).
+ FieldSignature().Double();
+
+ var fieldRVADef = metadata.AddFieldDefinition(
+ FieldAttributes.Public | FieldAttributes.Static | FieldAttributes.HasFieldRVA,
+ metadata.GetOrAddString("RvaField"),
+ metadata.GetOrAddBlob(rvaFieldSignature));
+
+ metadata.AddFieldRelativeVirtualAddress(fieldRVADef, 0);
+
+ metadata.AddTypeDefinition(
+ TypeAttributes.Class | TypeAttributes.Public | TypeAttributes.AutoLayout | TypeAttributes.BeforeFieldInit,
+ metadata.GetOrAddString("ConsoleApplication"),
+ metadata.GetOrAddString("Program"),
+ systemObjectTypeRef,
+ fieldList: fieldRVADef,
+ methodList: MetadataTokens.MethodDefinitionHandle(1));
+
+ return fieldRVADef;
+ }
+
private class TestResourceSectionBuilder : ResourceSectionBuilder
{
public TestResourceSectionBuilder()