Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Comment thread
jtschuster marked this conversation as resolved.
}

[Fact]
public void RuntimeAsyncMethodEmission()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,40 @@ public static bool HasExpectedArmHotColdRuntimeFunctionTargets(ReadyToRunReader
return result;
}

/// <summary>
/// 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.
/// </summary>
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<string>();

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<string> failures)
{
if (!reader.ReadyToRunHeader.Sections.TryGetValue(sectionType, out ReadyToRunSection section))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,9 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false)
}

byte[] manifestAssemblyMvidTable = _manifestNode.GetManifestAssemblyMvidTableData();
return new ObjectData(manifestAssemblyMvidTable, Array.Empty<Relocation>(), 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<Relocation>(), alignment: 4, new ISymbolDefinitionNode[] { this });
Comment thread
jtschuster marked this conversation as resolved.
}
}
}
Loading