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 }); } } }