Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -401,6 +401,93 @@ static void Validate(ReadyToRunReader reader)
}
}

/// <summary>
/// Regression test for an ARM32 alignment fault (SIGBUS / BUS_ADRALN) when loading a composite
/// Ready-to-Run image. The manifest metadata root (STORAGESIGNATURE/STORAGEHEADER/STORAGESTREAM)
/// and the component assembly table (READYTORUN_COMPONENT_ASSEMBLIES_ENTRY) are packed arrays of
/// DWORD fields that the runtime reads in place, so their sections must start on a 4-byte
/// boundary. When they landed on an unaligned RVA the runtime faulted on ARM32 (which does not
/// permit unaligned multi-word loads) during coreclr_initialize; x64/arm64 tolerated it.
/// </summary>
[Fact]
public void CompositeManifestSectionsAreAligned()
{
var compositeLib = new CompiledAssembly
{
AssemblyName = "CompositeLib",
SourceResourceNames = ["CrossModuleInlining/Dependencies/CompositeLib.cs"],
};
var compositeMain = new CompiledAssembly
{
AssemblyName = nameof(CompositeManifestSectionsAreAligned),
SourceResourceNames = ["CrossModuleInlining/CompositeBasic.cs"],
References = [compositeLib]
};

new R2RTestRunner(_output).Run(new R2RTestCase(
nameof(CompositeManifestSectionsAreAligned),
[
new(nameof(CompositeManifestSectionsAreAligned),
[
new CrossgenAssembly(compositeLib),
new CrossgenAssembly(compositeMain),
])
{
Options = [Crossgen2Option.Composite, Crossgen2Option.Optimize],
Validate = Validate,
},
]));

static void Validate(ReadyToRunReader reader)
{
string diag;
Assert.True(R2RAssert.CompositeManifestSectionsAreAligned(reader, out diag), diag);
}
}

/// <summary>
/// Complements <see cref="CompositeManifestSectionsAreAligned"/> using the same trigger as the
/// MVID-table test: --pdb emits an odd-sized debug directory section that shifts the manifest
/// sections off a 4-byte boundary without the fix. Windows-only because it relies on Windows PDB
/// generation.
/// </summary>
[ConditionalFact(nameof(IsWindows))]
public void CompositeManifestSectionsArePaddedWhenPdbPresent()
{
var compositeLib = new CompiledAssembly
{
AssemblyName = "CompositeLib",
SourceResourceNames = ["CrossModuleInlining/Dependencies/CompositeLib.cs"],
};
var compositeMain = new CompiledAssembly
{
AssemblyName = nameof(CompositeManifestSectionsArePaddedWhenPdbPresent),
SourceResourceNames = ["CrossModuleInlining/CompositeBasic.cs"],
References = [compositeLib]
};

new R2RTestRunner(_output).Run(new R2RTestCase(
nameof(CompositeManifestSectionsArePaddedWhenPdbPresent),
[
new(nameof(CompositeManifestSectionsArePaddedWhenPdbPresent),
[
new CrossgenAssembly(compositeLib),
new CrossgenAssembly(compositeMain),
])
{
Options = [Crossgen2Option.Composite, Crossgen2Option.Optimize],
AdditionalArgs = ["--pdb"],
Validate = Validate,
},
]));

static void Validate(ReadyToRunReader reader)
{
string diag;
Assert.True(R2RAssert.CompositeManifestSectionsAreAligned(reader, out diag), diag);
}
}

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

/// <summary>
/// Returns true if the manifest metadata and component assembly tables in a composite image
/// start on 4-byte aligned RVAs. Both sections contain DWORD fields that the runtime reads
/// directly, so unaligned sections can fault on architectures such as 32-bit ARM.
/// </summary>
public static bool CompositeManifestSectionsAreAligned(ReadyToRunReader reader, out string diagnostic)
{
const int RequiredAlignment = 4;
var failures = new List<string>();

bool result = true;
result &= SectionRVAIsAligned(reader, ReadyToRunSectionType.ManifestMetadata, RequiredAlignment, failures);
result &= SectionRVAIsAligned(reader, ReadyToRunSectionType.ComponentAssemblies, RequiredAlignment, failures);

diagnostic = result
? $"Composite manifest sections are {RequiredAlignment}-byte aligned."
: string.Join(Environment.NewLine, failures);
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
Expand Down Expand Up @@ -150,6 +170,30 @@ public static bool ManifestAssemblyMvidsTableIsAligned(ReadyToRunReader reader,
return failures.Count == 0;
}

private static bool SectionRVAIsAligned(ReadyToRunReader reader, ReadyToRunSectionType sectionType, int requiredAlignment, List<string> failures)
{
if (!reader.ReadyToRunHeader.Sections.TryGetValue(sectionType, out ReadyToRunSection section))
{
failures.Add($"Expected {sectionType} section not found.");
return false;
}

bool result = true;
if (section.Size <= 0)
{
failures.Add($"Expected {sectionType} section to be non-empty.");
result = false;
}

if ((section.RelativeVirtualAddress % requiredAlignment) != 0)
{
failures.Add($"{sectionType} section RVA 0x{section.RelativeVirtualAddress:X8} should be aligned to {requiredAlignment} bytes.");
result = false;
}

return result;
}

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 @@ -39,6 +39,7 @@ public override void AppendMangledName(NameMangler nameMangler, Utf8StringBuilde
public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false)
{
ObjectDataBuilder builder = new ObjectDataBuilder(factory, relocsOnly);
builder.RequireInitialAlignment(sizeof(uint));
builder.AddSymbol(this);
foreach (AssemblyHeaderNode assemblyHeader in _assemblyHeaders)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -294,7 +294,7 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false)
{
if (relocsOnly)
{
return new ObjectData(Array.Empty<byte>(), null, 1, null);
return new ObjectData(Array.Empty<byte>(), null, 4, null);
}

ComputeLastSetOfModuleIndices();
Expand All @@ -313,7 +313,8 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false)
return new ObjectData(
data: _mutableModule.MetadataBlob,
relocs: Array.Empty<Relocation>(),
alignment: 1,
// Metadata stream headers contain DWORD fields and require 4-byte alignment.
alignment: 4,
definedSymbols: new ISymbolDefinitionNode[] { this });
}

Expand Down
Loading