Skip to content

Split ELF NT_FILE mappings of the same file into separate modules#1496

Open
leculver wants to merge 3 commits into
microsoft:mainfrom
leculver:fix/elf-r2r-module-split
Open

Split ELF NT_FILE mappings of the same file into separate modules#1496
leculver wants to merge 3 commits into
microsoft:mainfrom
leculver:fix/elf-r2r-module-split

Conversation

@leculver

@leculver leculver commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

R2R managed assemblies are mapped twice on Linux/macOS: a raw flat file layout and a runtime-laid-out loaded PE layout, both file-backed by the same path but at disjoint (often multi-GB apart) addresses. LoadFileTable grouped NT_FILE entries purely by path, collapsing the two mappings into a single ElfLoadedImage whose Size spanned the entire gap (observed up to hundreds of GB), producing bogus, mutually-overlapping module ranges and breaking exact-base module correlation for consumers.

Split each path's entries into contiguity clusters and emit one ElfLoadedImage per cluster. Gaps <=1MB stay in the same image; gaps >=256MB start a new one; borderline gaps consult the PE header's SizeOfImage at the cluster base, falling back to the previous merge behavior when the header is unreadable or the image is not a PE (native ELF .so). Entries are still added to each image in original file order to preserve base-address selection.

leculver and others added 3 commits July 9, 2026 13:14
R2R managed assemblies are mapped twice on Linux/macOS: a raw flat file
layout and a runtime-laid-out loaded PE layout, both file-backed by the
same path but at disjoint (often multi-GB apart) addresses. LoadFileTable
grouped NT_FILE entries purely by path, collapsing the two mappings into a
single ElfLoadedImage whose Size spanned the entire gap (observed up to
hundreds of GB), producing bogus, mutually-overlapping module ranges and
breaking exact-base module correlation for consumers.

Split each path's entries into contiguity clusters and emit one
ElfLoadedImage per cluster. Gaps <=1MB stay in the same image; gaps >=256MB
start a new one; borderline gaps consult the PE header's SizeOfImage at the
cluster base, falling back to the previous merge behavior when the header is
unreadable or the image is not a PE (native ELF .so). Entries are still added
to each image in original file order to preserve base-address selection.
Address deep-review findings on the same-path NT_FILE contiguity split:

- Probe the PE header at the cluster's first PageOffset==0 entry (the address
  ElfLoadedImage actually uses as its base), not the lowest-address entry. When
  a header entry joins a cluster mid-way, retarget the probe and re-read
  SizeOfImage, so the split decision reads the real MZ header.
- Make the declared PE SizeOfImage the primary bound for any gap size, and use
  the address-gap threshold only as a fallback for header-less native ELF
  images. This stops the 256 MB threshold from ever hard-splitting a PE image
  and narrows the heuristic to genuinely non-PE mappings.
- Compute the in-image test as (nextStart - headerBase) < SizeOfImage instead of
  nextStart < headerBase + SizeOfImage, avoiding a near-2^64 addition overflow.
- Assert on duplicate base addresses in debug builds while keeping the last-wins
  indexer (no throw on crafted dumps).
- Remove the now-unused SmallGapThreshold.

Validated: repro R2R dump still splits MyLib.dll into two small modules, and the
raw ElfLoadedImage output across 9 real ELF coredumps is byte-identical to the
prior fix. Elf/Module/PEImage unit tests pass (24 passed, 0 failed).

Co-authored-by: Copilot <[email protected]>
@leculver leculver requested a review from max-charlamb July 9, 2026 18:19
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