diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCases/R2RTestSuites.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCases/R2RTestSuites.cs
index 2dd6625bdd286f..061abd0b02f11b 100644
--- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCases/R2RTestSuites.cs
+++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCases/R2RTestSuites.cs
@@ -320,6 +320,86 @@ static void Validate(ReadyToRunReader reader)
}
}
+ [Fact]
+ public void CompositeManifestAssemblyMvidsAreAligned()
+ {
+ var compositeLib = new CompiledAssembly
+ {
+ AssemblyName = "MvidCompositeLib",
+ SourceResourceNames = ["CrossModuleInlining/Dependencies/CompositeLib.cs"],
+ };
+ var compositeMain = new CompiledAssembly
+ {
+ AssemblyName = nameof(CompositeManifestAssemblyMvidsAreAligned),
+ SourceResourceNames = ["CrossModuleInlining/CompositeBasic.cs"],
+ References = [compositeLib]
+ };
+
+ new R2RTestRunner(_output).Run(new R2RTestCase(
+ nameof(CompositeManifestAssemblyMvidsAreAligned),
+ [
+ new(nameof(CompositeManifestAssemblyMvidsAreAligned),
+ [
+ new CrossgenAssembly(compositeLib),
+ new CrossgenAssembly(compositeMain),
+ ])
+ {
+ Options = [Crossgen2Option.Composite, Crossgen2Option.Optimize],
+ Validate = Validate,
+ },
+ ]));
+
+ static void Validate(ReadyToRunReader reader)
+ {
+ string diag;
+ Assert.True(R2RAssert.ManifestAssemblyMvidsTableIsAligned(reader, out diag), diag);
+ }
+ }
+
+ public static bool IsWindows => System.OperatingSystem.IsWindows();
+
+ [ConditionalFact(nameof(IsWindows))]
+ public void CompositeManifestAssemblyMvidsArePaddedWhenPdbPresent()
+ {
+ var compositeLib = new CompiledAssembly
+ {
+ AssemblyName = "MvidCompositeLib",
+ SourceResourceNames = ["CrossModuleInlining/Dependencies/CompositeLib.cs"],
+ };
+ var compositeMain = new CompiledAssembly
+ {
+ AssemblyName = nameof(CompositeManifestAssemblyMvidsArePaddedWhenPdbPresent),
+ SourceResourceNames = ["CrossModuleInlining/CompositeBasic.cs"],
+ References = [compositeLib]
+ };
+
+ new R2RTestRunner(_output).Run(new R2RTestCase(
+ nameof(CompositeManifestAssemblyMvidsArePaddedWhenPdbPresent),
+ [
+ new(nameof(CompositeManifestAssemblyMvidsArePaddedWhenPdbPresent),
+ [
+ new CrossgenAssembly(compositeLib),
+ new CrossgenAssembly(compositeMain),
+ ])
+ {
+ // --pdb creates an odd-sized debug directory section that exposes the MVID table
+ // misalignment bug. The odd size derives from the composite output name length, so
+ // renaming this test can shift the table back onto a 4-byte boundary and silently
+ // neutralize the regression coverage; verify it still misaligns without the fix if
+ // the name changes.
+ Options = [Crossgen2Option.Composite, Crossgen2Option.Optimize],
+ AdditionalArgs = ["--pdb"],
+ Validate = Validate,
+ },
+ ]));
+
+ static void Validate(ReadyToRunReader reader)
+ {
+ string diag;
+ Assert.True(R2RAssert.ManifestAssemblyMvidsTableIsAligned(reader, out diag), diag);
+ }
+ }
+
[Fact]
public void RuntimeAsyncMethodEmission()
{
diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/R2RResultChecker.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/R2RResultChecker.cs
index 05db82b928afad..2b543617fe14b4 100644
--- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/R2RResultChecker.cs
+++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/R2RResultChecker.cs
@@ -116,6 +116,40 @@ public static bool HasExpectedArmHotColdRuntimeFunctionTargets(ReadyToRunReader
return result;
}
+ ///
+ /// Returns true if the manifest assembly MVID table in a composite image is present, holds a
+ /// whole number of 16-byte GUID entries, and starts on a 4-byte aligned RVA. The runtime reads
+ /// each entry as a GUID by value, so the table must be 4-byte aligned to avoid alignment faults
+ /// (SIGBUS) on architectures such as 32-bit ARM that do not permit unaligned multi-word loads.
+ ///
+ public static bool ManifestAssemblyMvidsTableIsAligned(ReadyToRunReader reader, out string diagnostic)
+ {
+ const int GuidByteSize = 16;
+ const int RequiredAlignment = 4;
+
+ if (!reader.ReadyToRunHeader.Sections.TryGetValue(ReadyToRunSectionType.ManifestAssemblyMvids, out ReadyToRunSection section))
+ {
+ diagnostic = "Expected ManifestAssemblyMvids section not found.";
+ return false;
+ }
+
+ var failures = new List();
+
+ if (section.Size <= 0)
+ failures.Add("Expected ManifestAssemblyMvids section to be non-empty.");
+
+ if (section.Size % GuidByteSize != 0)
+ failures.Add($"ManifestAssemblyMvids section size {section.Size} should be a multiple of {GuidByteSize} (a table of GUIDs).");
+
+ if ((section.RelativeVirtualAddress % RequiredAlignment) != 0)
+ failures.Add($"ManifestAssemblyMvids section RVA 0x{section.RelativeVirtualAddress:X8} should be aligned to {RequiredAlignment} bytes.");
+
+ diagnostic = failures.Count == 0
+ ? $"ManifestAssemblyMvids table is {RequiredAlignment}-byte aligned ({section.Size / GuidByteSize} entries)."
+ : string.Join(Environment.NewLine, failures);
+ return failures.Count == 0;
+ }
+
private static bool SectionRVAIsEven(ReadyToRunReader reader, ReadyToRunSectionType sectionType, List failures)
{
if (!reader.ReadyToRunHeader.Sections.TryGetValue(sectionType, out ReadyToRunSection section))
diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ManifestAssemblyMvidHeaderNode.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ManifestAssemblyMvidHeaderNode.cs
index 96ce435250cdcb..5d59c55f4ce2cd 100644
--- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ManifestAssemblyMvidHeaderNode.cs
+++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ManifestAssemblyMvidHeaderNode.cs
@@ -56,7 +56,9 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false)
}
byte[] manifestAssemblyMvidTable = _manifestNode.GetManifestAssemblyMvidTableData();
- return new ObjectData(manifestAssemblyMvidTable, Array.Empty(), alignment: 1, new ISymbolDefinitionNode[] { this });
+
+ // GUID has a natural alignment of 4, so the table must be at least 4-byte aligned.
+ return new ObjectData(manifestAssemblyMvidTable, Array.Empty(), alignment: 4, new ISymbolDefinitionNode[] { this });
}
}
}