Skip to content

Fix cross-bitness dump reading: replace IntPtr.Size with target PointerSize#1421

Merged
max-charlamb merged 2 commits intomicrosoft:mainfrom
max-charlamb:fix/cross-bitness-dump-reading
Apr 17, 2026
Merged

Fix cross-bitness dump reading: replace IntPtr.Size with target PointerSize#1421
max-charlamb merged 2 commits intomicrosoft:mainfrom
max-charlamb:fix/cross-bitness-dump-reading

Conversation

@max-charlamb
Copy link
Copy Markdown
Contributor

@max-charlamb max-charlamb commented Apr 15, 2026

Summary

Replace ~50 uses of IntPtr.Size (host pointer size) with the target process's PointerSize across 14 files so that dumps from a different architecture can be read correctly (e.g. reading ARM64 dumps from an x64 host, or x86 dumps from an x64 host).

Motivation

Cross-platform diagnostic scenarios (e.g. this CI pipeline) fail when reading dumps from a target with a different pointer size than the host. The root cause is that ClrMD uses IntPtr.Size in many places to compute object layout offsets, field addresses, GC descriptor sizes, and heap alignment — all of which must use the target's pointer size, not the host's.

Changes

Files modified (14):

File What changed
ClrArray.cs Array length offset, multi-dim detection, rank calc, element addressing (6 locations)
ClrObject.cs Boxed value header offset
ClrInstanceField.cs Field address calculation
ClrField.cs GetSize accepts optional pointerSize parameter
ClrException.cs All 4 offset methods + stack trace parsing
ClrHeap.cs String offsets, alignment, min object size, GC ref scanning, finalize queue walking, MemoryCache.ReadPointer (12+ locations)
ClrDacType.cs GC descriptor reading, array element type detection, base array offset
ClrTypeFactory.cs String type StaticSize
GCDesc.cs New constructor overload with explicit pointerSize; all internal methods converted
DacHeap.cs Frozen segment committed start
DacThreadHelpers.cs IP/SP reads use target pointer size
CommonMemoryReader.cs Bug fix (see below)
SpanExtensions.cs New 3-arg AsPointer overload (pointerSize, offset)
SigParser.cs _pointerSize field for PeekElemTypeSize

Bug fix: CommonMemoryReader.ReadPointer

ReadPointer was calling AsPointer(pointerSize) which resolved to the 2-arg overload AsPointer(span, int offset), treating the pointer size as an offset. Fixed to AsPointer(pointerSize, 0) to use the correct 3-arg overload.

Testing

  • x64 tests: 379 pass, 1 fail (GCRootTests.FindAllPaths — pre-existing on baseline), 4 skip
  • x86 tests: 257 pass, 123 fail, 4 skip — identical to baseline
  • Cross-bitness smoke test: Successfully loaded 21 ARM64 Windows dumps from x64 host — pointer size correctly detected as 8, architecture as Arm64, modules enumerated, CLR versions found
  • No regressions: Verified by stashing changes and running baseline — identical results

What was NOT changed

Files using IntPtr.Size for host-side purposes (correctly):

  • CacheOptions.cs, DacDataTargetCOM.cs, WindowsProcessDataReader.cs, VTableBuilder.cs, ComCallableIUnknown.cs, cache entry classes

…erSize

Replace ~50 uses of IntPtr.Size (host pointer size) with the target process's
PointerSize so that dumps from a different architecture can be read correctly
(e.g. reading ARM64 dumps from an x64 host).

Changes across 14 files:
- ClrArray: array length offset, multi-dim detection, rank, element addressing
- ClrObject: boxed value header offset
- ClrInstanceField: field address calculation
- ClrField: GetSize accepts optional pointerSize parameter
- ClrException: all offset methods + stack trace parsing
- ClrHeap: string offsets, alignment, min object size, GC ref scanning,
  finalize queue walking, MemoryCache.ReadPointer
- ClrDacType: GC descriptor reading, array element type, base array offset
- ClrTypeFactory: string type StaticSize
- GCDesc: new constructor overload with explicit pointerSize; all internal
  methods converted
- DacHeap: frozen segment committed start
- DacThreadHelpers: IP/SP reads
- CommonMemoryReader: fix ReadPointer calling AsPointer(pointerSize) which
  bound to the 2-arg overload treating pointerSize as offset instead of size
- SpanExtensions: new 3-arg AsPointer overload (pointerSize, offset)
- SigParser: pointerSize field for PeekElemTypeSize

Bug fix: CommonMemoryReader.ReadPointer was calling AsPointer(pointerSize)
which resolved to the 2-arg overload (span, offset), treating the pointer
size as an offset. Changed to AsPointer(pointerSize, 0) to use the correct
3-arg overload.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Comment thread src/Microsoft.Diagnostics.Runtime/Extensions/SpanExtensions.cs Outdated
@leculver
Copy link
Copy Markdown
Contributor

Look at these as well:

ulong targetMethod = field.Read<UIntPtr>(Object, interior: false).ToUInt64();

ClrElementType.NativeInt => *(nint*)ppValue,
ClrElementType.NativeUInt => *(nuint*)ppValue,

Maybe AsPointer overloads should all take in a pointer size and not have any IntPtr.Size itself.

It's ok to make breaking changes here if you have to. We haven't yet shipped clrmd 4 yet.

Comment thread src/Microsoft.Diagnostics.Runtime/Utilities/SigParser/SigParser.cs Outdated
@max-charlamb max-charlamb force-pushed the fix/cross-bitness-dump-reading branch 2 times, most recently from 903df23 to 7e9d4d8 Compare April 16, 2026 17:49
@max-charlamb max-charlamb force-pushed the fix/cross-bitness-dump-reading branch from 7e9d4d8 to 1376aee Compare April 16, 2026 19:09
Copy link
Copy Markdown
Contributor

@leculver leculver left a comment

Choose a reason for hiding this comment

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

A few remaining issues, but overall looks great.

Comment thread src/Microsoft.Diagnostics.Runtime/ClrInstanceField.cs Outdated
Comment thread src/Microsoft.Diagnostics.Runtime/ClrObject.cs
Comment thread .gitignore Outdated
Comment thread src/Microsoft.Diagnostics.Runtime/DacInterface/MetadataImport.cs Outdated
@max-charlamb max-charlamb force-pushed the fix/cross-bitness-dump-reading branch 3 times, most recently from 6433fa5 to 96516e7 Compare April 16, 2026 20:05
…zed reads

Per review feedback from @leculver:

- SpanExtensions: Remove 0-arg and 1-arg AsPointer overloads that defaulted
  to IntPtr.Size. The ulong-offset overload now requires explicit pointerSize.
- SigParser: Remove 2-arg constructor defaulting to IntPtr.Size. All callers
  (ClrEnum, ClrField) now pass explicit pointer size. Remove dead code:
  PeekElemTypeSize method (never called).
- GCDesc: Remove 1-arg constructor defaulting to IntPtr.Size.
- ClrField.GetSize: Remove pointerSize=0 fallback to IntPtr.Size; parameter
  is now required. Updated ClrPrimitiveType caller.
- MetadataImport: Remove dead _pointerSize field and GetSigFromToken method
  (never called externally). Simplify constructors.
- ClrInstanceField.ReadPointer: Mark as internal, not public.
- ClrObject.AsRuntimeType: Add braces to if clause per style.

Additional fixes for host-sized target reads:
- ClrDelegate: Replace Read<UIntPtr> with ReadPointer (target-pointer-aware).
  Replace ReadValues<UIntPtr> loop with GetObjectValue iteration.
- ClrObject.AsRuntimeType: Replace ReadField<nint> with ReadPointer for
  m_handle/m_ptr fields.
- ClrArray.GetObjectValue: Replace ReadValue<nuint> with ReadPointer to
  read target-sized object references.
- ClrEnum: Replace *(nint*) and *(nuint*) with pointer-size-conditional
  reads using target PointerSize.
- ClrInstanceField: Add ReadPointer method for target-pointer-aware reads.
- SigParserBoundsTests: Updated to pass explicit pointer size.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@max-charlamb max-charlamb force-pushed the fix/cross-bitness-dump-reading branch from 96516e7 to bb40de6 Compare April 16, 2026 20:07
@max-charlamb max-charlamb merged commit b509d9e into microsoft:main Apr 17, 2026
8 checks passed
@max-charlamb max-charlamb deleted the fix/cross-bitness-dump-reading branch April 17, 2026 16:08
max-charlamb pushed a commit to dotnet/runtime that referenced this pull request Apr 17, 2026
This version includes cross-bitness dump reading support (microsoft/clrmd#1421),
allowing correct reading of dumps from different architectures (e.g., ARM64
dumps from an x86 host).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants