Align manifest assembly MVID table to 4 bytes in composite R2R images - #129017
Merged
Conversation
ManifestAssemblyMvidHeaderNode emitted the assembly MVID table (a packed array of 16-byte GUIDs) with alignment 1. The CoreCLR runtime reads each entry as a GUID by value, and GUID has a natural alignment of 4. When the table landed on a non-4-aligned RVA, 32-bit ARM faulted with BUS_ADRALN (SIGBUS) because it does not permit unaligned multi-word loads. Other architectures tolerated the unaligned access, so the crash only manifested on ARM32. The table's RVA depends on the size of the preceding Ordered ReadOnlyData node. In composites built with debug directory entries (e.g. --pdb), an odd-sized NativeDebugDirectoryEntryNode can precede the MVID table and push it off a 4-byte boundary. Setting the alignment to 4 pads the table to a valid boundary regardless of predecessor size. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Member
Author
|
/azp run runtime-coreclr crossgen2-composite |
|
Azure Pipelines successfully started running 1 pipeline(s). |
Contributor
There was a problem hiding this comment.
Pull request overview
This PR updates the ReadyToRun (crossgen2) emitter for composite images so the manifest assembly MVID table is emitted with 4-byte alignment, preventing potential unaligned GUID loads by the CoreCLR runtime on alignment-sensitive architectures (notably 32-bit ARM).
Changes:
- Change
ManifestAssemblyMvidHeaderNodeoutput alignment from 1 to 4 bytes. - Add an explanatory comment documenting the runtime read pattern and the alignment requirement.
jkotas
reviewed
Jun 5, 2026
…yAnalysis/ReadyToRun/ManifestAssemblyMvidHeaderNode.cs Co-authored-by: Jan Kotas <jkotas@microsoft.com>
Locks the 4-byte alignment contract of the manifest assembly MVID table in composite R2R images. The runtime reads each entry as a GUID by value, so a misaligned table causes alignment faults (SIGBUS) on 32-bit ARM. Adds the R2RAssert.ManifestAssemblyMvidsTableIsAligned helper and a composite test. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The cross-platform invariant test cannot reproduce the original misalignment because a plain composite always lays the MVID table 4-byte aligned. Passing --pdb injects an odd-sized NativeDebugDirectoryEntryNode immediately before the table, which is the exact layout that exposed the bug: without the fix the MVID table lands on a non-4-aligned RVA (verified: RVA 0x107AF unfixed vs 0x107B0 fixed for these inputs). --pdb writes the NI PDB through Microsoft.DiaSymReader.Native, which only exists on Windows, so the test is gated with [ConditionalFact(nameof(IsWindows))] and skips elsewhere. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Remove redundant comments where the assertion is self-describing, condense the --pdb rationale, and replace the always-4 alignment parameter with a RequiredAlignment constant so the helper's contract matches its documentation. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…re-green The odd-sized native debug directory entry that exposes the misalignment derives from the composite output name length, so a rename can silently move the MVID table back onto a 4-byte boundary. Warn future maintainers to re-verify. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
davidwrighton
approved these changes
Jun 5, 2026
eiriktsarpalis
pushed a commit
that referenced
this pull request
Jul 15, 2026
…#129017) ## Summary Fixes an ARM32 alignment fault when running composite Ready-to-Run images. `ManifestAssemblyMvidHeaderNode` emits the assembly MVID table — a packed array of 16-byte GUIDs — with `alignment: 1`. The CoreCLR runtime reads each entry as a `GUID` by value (`ReadyToRunInfo` in `readytoruninfo.cpp`), and `GUID` has a natural alignment of 4. When the table landed on a non-4-byte-aligned RVA, ARM32 faulted with `BUS_ADRALN` because it does not permit unaligned multi-word loads. x64/arm64 tolerate the unaligned access, so the crash only manifested on ARM32. In addition, the alignment of preceding sections would typically end at a 4-byte-aligned offset, so this only manifested when the --pdb option was used and the NativeDebugDirectoryEntryNode section was an odd number. The fix is simply to use `alignment: 4` for the section. --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: Jan Kotas <jkotas@microsoft.com>
simonrozsival
added a commit
that referenced
this pull request
Jul 16, 2026
…ns to 4 bytes (#130564) 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 #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) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes a 32-bit ARM
SIGBUS(alignment fault) when running composite Ready-to-Run images.ManifestAssemblyMvidHeaderNodeemits the assembly MVID table — a packed array of 16-byte GUIDs — withalignment: 1. The CoreCLR runtime reads each entry as aGUIDby value (ReadyToRunInfoinreadytoruninfo.cpp), andGUIDhas a natural alignment of 4. When the table landed on a non-4-aligned RVA, ARM32 faulted withBUS_ADRALNbecause it does not permit unaligned multi-word loads. x64/arm64 tolerate the unaligned access, so the crash only manifested on ARM32.Why the table could land unaligned
The table's RVA depends on the size of the preceding
OrderedReadOnlyDatanode. In composites built with debug directory entries (e.g.--pdb), an odd-sizedNativeDebugDirectoryEntryNode(RSDS record) sorts betweenRuntimeFunctionsTableNodeand the MVID table and can push it off a 4-byte boundary.The fix
Set the node alignment to 4 so the table is padded to a valid boundary regardless of predecessor size.
Validation
Reproduced on current
mainwith a controlled experiment (identical inputs and output name, so the predecessor node is byte-for-byte identical; the only variable is the alignment value):This matches the structure of a real failing ARM32 image, whose MVID table sat at RVA
0x28f49(&3 = 1), consistent with the fault'ssi_addrin the kernel core.Note
This pull request was authored with assistance from GitHub Copilot.