Skip to content

Fix managed PE image mapping under LLDB - #6039

Open
max-charlamb wants to merge 3 commits into
dotnet:mainfrom
max-charlamb:fix-lldb-managed-pe-mapping
Open

max-charlamb wants to merge 3 commits into
dotnet:mainfrom
max-charlamb:fix-lldb-managed-pe-mapping

Conversation

@max-charlamb

Copy link
Copy Markdown
Member

Summary

  • scope the LLDB PE-probing exclusion to modules supplied by debugger services
  • allow managed modules enumerated separately through ClrMD to use the existing PE image-mapping path
  • preserve the existing LLDB native ELF/Mach-O module behavior

Linux Heap core dumps can omit file-backed pages from managed PE images. Under LLDB, ImageMappingMemoryService enumerates managed modules through ClrMD, but the host-wide LLDB exclusion prevented those modules from being probed as PE images. This moves the exclusion to ModuleServiceFromDebuggerServices, where the original assumption applies.

Validation

  • Build.cmd
  • Microsoft.Diagnostics.DebugServices.UnitTests: 43 passed, excluding two pre-existing ClrmaTests theory-data failures

Copilot AI lite review requested due to automatic review settings September 16, 2026 16:00
@max-charlamb
max-charlamb requested a review from a team as a code owner September 16, 2026 16:00
Scope the LLDB PE probing exclusion to modules supplied by the native debugger. Managed modules enumerated separately through ClrMD can then be recognized as PE images and recovered by the image mapping memory service.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: b013d849-0633-4b39-87ad-36002a21ae4f
@max-charlamb
max-charlamb force-pushed the fix-lldb-managed-pe-mapping branch from b7c4284 to 567aea9 Compare September 16, 2026 16:08

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.

🟡 Changes recommended

Add the requested LLDB managed-module regression test.

Get a fresh assessment by requesting another Copilot review.

Pull request overview

Scopes LLDB’s PE-probing exclusion to debugger-service modules, allowing managed modules to use image mapping while preserving native handling.

Changes:

  • Adds configurable PE probing to ModuleService.
  • Disables probing only for LLDB debugger-service modules.
  • Preserves native ELF/Mach-O behavior.
File summaries
File Reviewed change
src/SOS/SOS.Extensions/ModuleServiceFromDebuggerServices.cs Applies the LLDB-specific exclusion; add regression coverage for managed-module probing.
src/Microsoft.Diagnostics.DebugServices.Implementation/ModuleService.cs Adds the configurable PE-probing capability.
Review details
  • Files reviewed: 2/2 changed files
  • Comments generated: 1
  • Review effort level: Lite

💡 Add a code-review agent skill for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/SOS/SOS.Extensions/ModuleServiceFromDebuggerServices.cs
Add a cross-platform Mini dump integration test that selects a CoreLib range proven absent from the dump and verifies managed SOS can map it from the PE image. Route CDB SOS commands through the universal !sos dispatcher and add typed clrmodules parsing for module image information.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: b013d849-0633-4b39-87ad-36002a21ae4f
public SosOutput Execute(string command) => new(Name, command, Send(command));

public SosOutput Sos(string command) => new(Name, command, Send("!" + command));
public SosOutput Sos(string command) => new(Name, command, Send("!sos " + command));

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Modifying this for cdb. The managed db command conflicts with cdb's native db command so it needs the !sos prefix. Now the target.Sos(...) method will always use the extension command and never conflict with native debugger commands.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: b013d849-0633-4b39-87ad-36002a21ae4f
Comment thread src/tests/SOS.TestHarness/ChildEngineClient.cs
@hoyosjs

hoyosjs commented Sep 16, 2026

Copy link
Copy Markdown
Member

While this solves the issue for ImageMappingMemoryService, MetadataMappingMemoryService still has it: its _moduleService is a ModuleServiceFromDebuggerServices, which still reports !IsPEModuleProbeSupported, so GetMetaData() returns empty. It's silly, because the _memoryService it wraps is the mapping one and would be able to succeed. In practice the fallback hides most of this - ReadMemory delegates to _memoryService after the region walk, so when lldb fails or short-reads, the image mapper handles it. What's left is the case the metadata class was written for: lldb returning successful all-zero reads for memory that isn't in the dump. There bytesRequested == bytesRead, so ImageMappingMemoryService never consults its module cache and the zeros propagate to the caller. The metadata service is the only layer that can intercept that, and it's still disabled. This can't be expressed through the class hierarchy, because at the ModuleServiceFromDebuggerServices level there are callers that can succeed here and callers that can't - the same instance produces both lldb-enumerated native modules and ClrMD-sourced managed PEs from CreateModule(-1, ClrModule). The gate should live on module creation (a CreateModule overload, or a flag on ModuleFromAddress) so it follows where the module came from rather than which service built it.

@max-charlamb

Copy link
Copy Markdown
Member Author

While this solves the issue for ImageMappingMemoryService, MetadataMappingMemoryService still has it: its _moduleService is a ModuleServiceFromDebuggerServices, which still reports !IsPEModuleProbeSupported, so GetMetaData() returns empty. It's silly, because the _memoryService it wraps is the mapping one and would be able to succeed. In practice the fallback hides most of this - ReadMemory delegates to _memoryService after the region walk, so when lldb fails or short-reads, the image mapper handles it. What's left is the case the metadata class was written for: lldb returning successful all-zero reads for memory that isn't in the dump. There bytesRequested == bytesRead, so ImageMappingMemoryService never consults its module cache and the zeros propagate to the caller. The metadata service is the only layer that can intercept that, and it's still disabled. This can't be expressed through the class hierarchy, because at the ModuleServiceFromDebuggerServices level there are callers that can succeed here and callers that can't - the same instance produces both lldb-enumerated native modules and ClrMD-sourced managed PEs from CreateModule(-1, ClrModule). The gate should live on module creation (a CreateModule overload, or a flag on ModuleFromAddress) so it follows where the module came from rather than which service built it.

Updated gate to live on the Module and happen at Module creation.

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.

3 participants