Skip to content

Implement cDAC TraverseLoaderHeap via Loader contract#125129

Open
Copilot wants to merge 21 commits intomainfrom
copilot/implement-cdac-api-traverse-loader-heap
Open

Implement cDAC TraverseLoaderHeap via Loader contract#125129
Copilot wants to merge 21 commits intomainfrom
copilot/implement-cdac-api-traverse-loader-heap

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Mar 3, 2026

Implements ISOSDacInterface13::TraverseLoaderHeap (with and without kind) in the managed cDAC stack via the Loader contract. Adds separate cDAC types for LoaderHeap and ExplicitControlLoaderHeap, with a LoaderHeapKind enum embedded in GetFirstLoaderHeapBlock.

Description

C++ VM

  • loaderheap.h: forward-declares cdac_data<T>, adds friend struct cdac_data<UnlockedLoaderHeapBaseTraversable>, and provides a cdac_data<UnlockedLoaderHeapBaseTraversable> specialization exposing FirstBlock — matching the pattern used in arraylist.h and loaderallocator.hpp.
  • datadescriptor.inc: two separate cDAC types (LoaderHeap, ExplicitControlLoaderHeap) each with a FirstBlock field via cdac_data<UnlockedLoaderHeapBaseTraversable>::FirstBlock; LoaderHeapBlock fields via direct offsetof (public struct members).

Managed contracts

  • LoaderHeapKind enum (Normal = 0, ExplicitControl = 1) in ILoader.cs; GetFirstLoaderHeapBlock takes a LoaderHeapKind and dispatches to Data.LoaderHeap or Data.ExplicitControlLoaderHeap accordingly — unknown kinds throw NotImplementedException.
  • New Data/ExplicitControlLoaderHeap.cs (IData<T>) reads FirstBlock from the ExplicitControlLoaderHeap cDAC type.
  • DataType.ExplicitControlLoaderHeap added.

Legacy DAC (SOSDacImpl.cs)

TraverseLoaderHeap (no-kind) and TraverseLoaderHeapWithKind implemented independently via the same ILoader contract methods — no cross-dependency. kind maps to LoaderHeapKind; out-of-range values return E_NOTIMPL.

Tests

28 unit tests covering empty heap, single block, and multi-block scenarios across both heap kinds and 4 pointer-size variants.

Docs

Loader.md updated with LoaderHeapKind enum, updated GetFirstLoaderHeapBlock signature, ExplicitControlLoaderHeap data descriptor row, and revised algorithm pseudocode for both dispatch paths.


✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.

@dotnet-policy-service
Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @agocke
See info in area-owners.md if you want to be subscribed.

Co-authored-by: rcj1 <77995559+rcj1@users.noreply.github.com>
… typo fix

Co-authored-by: rcj1 <77995559+rcj1@users.noreply.github.com>
Copilot AI changed the title [WIP] Add cdac API TraverseLoaderHeap implementation Implement cDAC TraverseLoaderHeap via IGC contract Mar 3, 2026
Copilot finished work on behalf of rcj1 March 3, 2026 17:40
Co-authored-by: rcj1 <77995559+rcj1@users.noreply.github.com>
Copilot AI changed the title Implement cDAC TraverseLoaderHeap via IGC contract Implement cDAC TraverseLoaderHeap via Loader contract Mar 3, 2026
Copilot finished work on behalf of rcj1 March 3, 2026 17:52
…ructs for LoaderHeap types

Co-authored-by: rcj1 <77995559+rcj1@users.noreply.github.com>
…loaderheap.h

Co-authored-by: rcj1 <77995559+rcj1@users.noreply.github.com>
Copilot finished work on behalf of rcj1 March 3, 2026 18:34
…ype; embed kind check in GetFirstLoaderHeapBlock

Co-authored-by: rcj1 <77995559+rcj1@users.noreply.github.com>
…to loaderheap.h

Co-authored-by: rcj1 <77995559+rcj1@users.noreply.github.com>
@max-charlamb
Copy link
Copy Markdown
Member

We should also rebase/merge on top of main and fix any conflicts or problems that arise.

@rcj1
Copy link
Copy Markdown
Contributor

rcj1 commented Mar 7, 2026

@jkotas adding the virtual destructor did not cause the correct offsetof() to be picked up by the cDAC in, for example, LoaderAllocator. DavidWr has indicated that it is not reliable / platform dependent whether this will be optimized out.

@jkotas
Copy link
Copy Markdown
Member

jkotas commented Mar 9, 2026

@jkotas adding the virtual destructor did not cause the correct offsetof() to be picked up by the cDAC in, for example, LoaderAllocator. DavidWr has indicated that it is not reliable / platform dependent whether this will be optimized out.

Ok.

I think we are still exposed to the fragility of offsetof and multiple inheritance as copilot pointed it in #125129 (comment) . If it is not causing observable issues, we can leave it like that.


// Returns the first block of the loader heap linked list, or TargetPointer.Null if the heap has no blocks.
// Throws NotImplementedException for unknown kind values.
TargetPointer GetFirstLoaderHeapBlock(TargetPointer loaderHeap, LoaderHeapKind kind);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

From what I can see, this LoaderHeapKind appears to have no impact on the final result. Both kinds derive from UnlockedLoaderHeapBaseTraversable and the m_pFirstBlock pointer that we need is also a member of that type. Let me know if I am missing something.

I'm thinking we should drop this kind argument from the API. If we did I'm guessing a number of other things would change too:

  • change the cDac contract to depend on a single UnlockedLoaderHeapBaseTraversable::m_pFirst field instead of per-derived type definitions of the same field.
  • get rid of the kind enum
  • Reduce the two IData types down to a single one for the base class
  • Change SOSDacInterface13.TraverseLoaderHeap impl to ignore the kind argument from the caller

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

From what I can see, this LoaderHeapKind appears to have no impact on the final result

Unfortunately, it does have an impact as discussed above. Loader heap implementation uses C++ multiple inheritance that introduces multiple different this pointers for the same object.

I agree that it would be nice to do what you are proposing. We would have to get the multiple inheritance under control first.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Ah I walked into the midst unaware!

What if we removed the multiple inheritance with a pattern like this:

class LoaderHeapBackout final : public ILoaderHeapBackout
{
   LoaderHeap* m_owner;
   void RealBackoutMem(...) override { m_owner->RealBackoutMem(...); }
};
   
class UnlockedLoaderHeapBase : public UnlockedLoaderHeapBaseTraversable
{
   LoaderHeapBackout m_backout; // pass out the address of this field when an
                                // ILoaderHeapBackout is needed.
};

IMO the conceptual simplification to data contract would be worth doing a small runtime refactor.

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.

Adding a virtual destructor onto the base class solves the issue.

@rcj1 rcj1 force-pushed the copilot/implement-cdac-api-traverse-loader-heap branch from a5d88bc to 36f229e Compare March 24, 2026 17:05
@rcj1
Copy link
Copy Markdown
Contributor

rcj1 commented Mar 24, 2026

wait on codeflow from microsoft/clrmd#1410 for clean CI

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.

6 participants