Skip to content

[R2R] Align composite ManifestMetadata and ComponentAssemblies sections to 4 bytes#130564

Open
simonrozsival wants to merge 1 commit into
dotnet:mainfrom
simonrozsival:dev/simonrozsival/manifest-metadata-alignment
Open

[R2R] Align composite ManifestMetadata and ComponentAssemblies sections to 4 bytes#130564
simonrozsival wants to merge 1 commit into
dotnet:mainfrom
simonrozsival:dev/simonrozsival/manifest-metadata-alignment

Conversation

@simonrozsival

Copy link
Copy Markdown
Member

Follow-up to #129017.

Summary

That PR fixed a 32-bit ARM SIGBUS (BUS_ADRALN) in composite Ready-to-Run images caused by emitting the manifest MVID table with alignment: 1. The adjacent ManifestMetadataTableNode (the metadata root) and AssemblyTableNode (ComponentAssemblies) have the identical latent bug: both are packed arrays of DWORD-typed fields that the CoreCLR runtime reads in place, but both are emitted with alignment: 1, so they can land on a non-4-aligned RVA. On ARM32 that faults with BUS_ADRALN (unaligned multi-word load); x64/arm64 tolerate it, so it stayed latent until android-arm composite R2R was exercised.

In the failing android-arm MAUI R2R crash (dotnet/android#12026) the MVID table was already aligned (thanks to #129017), but the metadata root landed on an odd RVA and faulted in PEDecoder::CheckCorHeader during coreclr_initialize. So #129017 was necessary but not sufficient for this configuration; this change completes it by aligning the remaining manifest-region sections.

Root cause

Loading a composite image parses the metadata root via PEDecoder::CheckCorHeader, which walks STORAGESTREAM headers (ULONG iOffset; ULONG iSize; char rcName[]). The compiler coalesces the two adjacent DWORD reads (iOffset/iSize) into a single LDRD, which requires 4-byte alignment on ARM32:

Fatal signal 7 (SIGBUS), code 1 (BUS_ADRALN)
  #00 pc ... libcoreclr.so  PEDecoder::CheckCorHeader() const   (pedecoder.cpp)
  ...
  #23 pc ... libcoreclr.so  coreclr_initialize

ComponentAssemblies (READYTORUN_COMPONENT_ASSEMBLIES_ENTRY) is likewise a packed DWORD array read in place, and the regression test shows it landing unaligned without the fix.

Provenance

alignment: 1 on ManifestMetadataTableNode dates back to the initial population of crossgen2 from the CoreRT sources (ac857a2e, 2019) and was never a deliberate choice — no commit or discussion in the file's history addressed alignment; the value was carried verbatim through later refactors (e.g. #71271 only switched to named arguments). The sibling ManifestAssemblyMvidHeaderNode shared the same default and was corrected in #129017.

Why 4, and why not configurable / a size concern

  • The metadata root and component-assembly entries are all DWORD fields whose natural alignment is 4; ECMA-335 already pads the metadata version string so stream headers are 4-aligned within the blob. 4-byte section-start alignment is the correct requirement (same rationale as Align manifest assembly MVID table to 4 bytes in composite R2R images #129017's "GUID has a natural alignment of 4").
  • No meaningful size impact: these are singleton sections (once per composite image), so the change adds at most 3 padding bytes per section.
  • Not a user-facing knob: every other crossgen2 section uses a fixed natural alignment (RuntimeFunctions=4, GCRefMap=4, …); a configurable "may emit a misaligned image" switch would be a footgun, so this stays a fixed value.

Testing

Mirrors #129017's test approach: a CompositeManifestSectionsAreAligned(...) checker in R2RResultChecker, driven by a plain [Fact] and a Windows-only [ConditionalFact(IsWindows)] --pdb variant that uses the same composite inputs.

Validation

Mirrors #129017: the --pdb variant emits an odd-sized NativeDebugDirectoryEntryNode that, without the fix, shifts the manifest sections off a 4-byte boundary; with the fix they are padded to a 4-byte RVA. Verified end-to-end by building an android-arm CoreCLR composite R2R APK with the fixed crossgen2 — R2RDump confirms ManifestMetadata, ComponentAssemblies, and ManifestAssemblyMvids all start on 4-byte-aligned RVAs — resolving the crash reported in dotnet/android#12026.

cc @jtschuster (author of the sibling fix #129017)

…ns to 4 bytes

Follow-up to dotnet#129017. That PR fixed a 32-bit ARM SIGBUS (BUS_ADRALN) in composite
Ready-to-Run images caused by emitting the manifest MVID table with alignment: 1.
The adjacent ManifestMetadataTableNode (metadata root) and AssemblyTableNode
(ComponentAssemblies) have the identical latent bug: both are packed arrays of
DWORD-typed fields the runtime reads in place, but are emitted with alignment: 1,
so they can land on a non-4-aligned RVA and fault on ARM32 (unaligned multi-word
load) during coreclr_initialize. x64/arm64 tolerate the unaligned access.

In the failing android-arm MAUI R2R crash (dotnet/android#12026) the MVID table was
already aligned (thanks to dotnet#129017), but the metadata root landed on an odd RVA and
faulted in PEDecoder::CheckCorHeader, so dotnet#129017 alone did not cover this case.

Align both sections to 4 bytes (their contents' natural alignment) and add a
regression test mirroring the MVID-table tests (a plain composite case plus a
Windows-only --pdb variant that deterministically exposes the misalignment).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 77c12619-9a25-4209-9469-b44bd280f2bc
@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to 'arch-android': @vitek-karas, @simonrozsival, @steveisok, @akoeplinger
See info in area-owners.md if you want to be subscribed.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates crossgen2 composite ReadyToRun emission to ensure manifest-related sections that contain 32-bit fields are emitted on 4-byte boundaries, avoiding potential unaligned in-place reads on targets that require alignment (notably ARM32). It also adds regression checks in the ReadyToRun test suite to validate the resulting section RVAs are properly aligned.

Changes:

  • Align ManifestMetadataTableNode output to 4 bytes (including the relocsOnly path) since it contains DWORD-based metadata stream headers.
  • Require 4-byte initial alignment for the ComponentAssemblies table (AssemblyTableNode).
  • Add a new test helper plus composite test cases that assert ManifestMetadata and ComponentAssemblies section RVAs are 4-byte aligned (with a Windows-only --pdb variant).
Show a summary per file
File Description
src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ManifestMetadataTableNode.cs Changes manifest metadata node alignment from 1 to 4 to ensure safe in-place parsing of DWORD fields.
src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/AssemblyTableNode.cs Requires 4-byte alignment for the component assemblies table emission.
src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/R2RResultChecker.cs Adds alignment-check helpers for composite manifest sections (metadata + component assemblies).
src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCases/R2RTestSuites.cs Adds regression tests validating the new alignment constraints (including a --pdb padding trigger on Windows).

Copilot's findings

  • Files reviewed: 4/4 changed files
  • Comments generated: 0

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants