File: SOSDacImpl.cs
Web Access
Project: src\src\runtime\src\native\managed\cdac\Microsoft.Diagnostics.DataContractReader.Legacy\Microsoft.Diagnostics.DataContractReader.Legacy.csproj (Microsoft.Diagnostics.DataContractReader.Legacy)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices.Marshalling;
using System.Text;

using Microsoft.Diagnostics.DataContractReader;
using Microsoft.Diagnostics.DataContractReader.Contracts;
using Microsoft.Diagnostics.DataContractReader.Contracts.Extensions;
using Microsoft.Diagnostics.DataContractReader.Contracts.StackWalkHelpers;
using System.Reflection.Metadata;
using System.Reflection.Metadata.Ecma335;

namespace Microsoft.Diagnostics.DataContractReader.Legacy;

/// <summary>
/// Implementation of ISOSDacInterface* interfaces intended to be passed out to consumers
/// interacting with the DAC via those COM interfaces.
/// </summary>
/// <remarks>
/// Functions on <see cref="ISOSDacInterface"/> are defined with PreserveSig. Target and Contracts
/// throw on errors. Implementations in this class should wrap logic in a try-catch and return the
/// corresponding error code.
/// </remarks>
[GeneratedComClass]
public sealed unsafe partial class SOSDacImpl
    : ISOSDacInterface, ISOSDacInterface2, ISOSDacInterface3, ISOSDacInterface4, ISOSDacInterface5,
      ISOSDacInterface6, ISOSDacInterface7, ISOSDacInterface8, ISOSDacInterface9, ISOSDacInterface10,
      ISOSDacInterface11, ISOSDacInterface12, ISOSDacInterface13, ISOSDacInterface14, ISOSDacInterface15,
      ISOSDacInterface16
{
    private readonly Target _target;

    // When this class is created, the runtime may not have loaded the string and object method tables and set the global pointers.
    // This is also the case for the GetUsefulGlobals API, which can be called as part of load notifications before runtime start.
    // They should be set when actually requested via other DAC APIs, so we lazily read the global pointers.
    private readonly Lazy<TargetPointer> _stringMethodTable;
    private readonly Lazy<TargetPointer> _objectMethodTable;
    private readonly ulong _rcwMask = 1UL;

    private readonly ISOSDacInterface? _legacyImpl;
    private readonly ISOSDacInterface2? _legacyImpl2;
    private readonly ISOSDacInterface3? _legacyImpl3;
    private readonly ISOSDacInterface4? _legacyImpl4;
    private readonly ISOSDacInterface5? _legacyImpl5;
    private readonly ISOSDacInterface6? _legacyImpl6;
    private readonly ISOSDacInterface7? _legacyImpl7;
    private readonly ISOSDacInterface8? _legacyImpl8;
    private readonly ISOSDacInterface9? _legacyImpl9;
    private readonly ISOSDacInterface10? _legacyImpl10;
    private readonly ISOSDacInterface11? _legacyImpl11;
    private readonly ISOSDacInterface12? _legacyImpl12;
    private readonly ISOSDacInterface13? _legacyImpl13;
    private readonly ISOSDacInterface14? _legacyImpl14;
    private readonly ISOSDacInterface15? _legacyImpl15;
    private readonly ISOSDacInterface16? _legacyImpl16;
    private readonly IXCLRDataProcess? _legacyProcess;
    private readonly IXCLRDataProcess2? _legacyProcess2;
    private readonly ICLRDataEnumMemoryRegions? _legacyEnumMemory;

    public SOSDacImpl(Target target, object? legacyObj)
    {
        _target = target;
        _stringMethodTable = new Lazy<TargetPointer>(
            () => _target.ReadPointer(_target.ReadGlobalPointer(Constants.Globals.StringMethodTable)));

        _objectMethodTable = new Lazy<TargetPointer>(
            () => _target.ReadPointer(_target.ReadGlobalPointer(Constants.Globals.ObjectMethodTable)));

        // Get all the interfaces for delegating to the legacy DAC
        if (legacyObj is not null)
        {
            _legacyImpl = legacyObj as ISOSDacInterface;
            _legacyImpl2 = legacyObj as ISOSDacInterface2;
            _legacyImpl3 = legacyObj as ISOSDacInterface3;
            _legacyImpl4 = legacyObj as ISOSDacInterface4;
            _legacyImpl5 = legacyObj as ISOSDacInterface5;
            _legacyImpl6 = legacyObj as ISOSDacInterface6;
            _legacyImpl7 = legacyObj as ISOSDacInterface7;
            _legacyImpl8 = legacyObj as ISOSDacInterface8;
            _legacyImpl9 = legacyObj as ISOSDacInterface9;
            _legacyImpl10 = legacyObj as ISOSDacInterface10;
            _legacyImpl11 = legacyObj as ISOSDacInterface11;
            _legacyImpl12 = legacyObj as ISOSDacInterface12;
            _legacyImpl13 = legacyObj as ISOSDacInterface13;
            _legacyImpl14 = legacyObj as ISOSDacInterface14;
            _legacyImpl15 = legacyObj as ISOSDacInterface15;
            _legacyImpl16 = legacyObj as ISOSDacInterface16;

            _legacyProcess = legacyObj as IXCLRDataProcess;
            _legacyProcess2 = legacyObj as IXCLRDataProcess2;

            _legacyEnumMemory = legacyObj as ICLRDataEnumMemoryRegions;
        }
    }

    #region ISOSDacInterface
    int ISOSDacInterface.GetAppDomainConfigFile(ClrDataAddress appDomain, int count, char* configFile, uint* pNeeded)
    {
        // Method is not supported on CoreCLR
        int hr = HResults.E_FAIL;

#if DEBUG
        if (_legacyImpl is not null)
        {
            int hrLocal = _legacyImpl.GetAppDomainConfigFile(appDomain, count, configFile, pNeeded);
            Debug.ValidateHResult(hr, hrLocal);
        }
#endif

        return hr;
    }
    int ISOSDacInterface.GetAppDomainData(ClrDataAddress addr, DacpAppDomainData* data)
    {
        int hr = HResults.S_OK;
        try
        {
            if (addr == 0)
                throw new ArgumentException();

            *data = default;
            data->AppDomainPtr = addr;
            TargetPointer systemDomainPointer = _target.ReadGlobalPointer(Constants.Globals.SystemDomain);
            ClrDataAddress systemDomain = _target.ReadPointer(systemDomainPointer).ToClrDataAddress(_target);
            Contracts.ILoader loader = _target.Contracts.Loader;
            TargetPointer globalLoaderAllocator = loader.GetGlobalLoaderAllocator();
            data->pHighFrequencyHeap = loader.GetHighFrequencyHeap(globalLoaderAllocator).ToClrDataAddress(_target);
            data->pLowFrequencyHeap = loader.GetLowFrequencyHeap(globalLoaderAllocator).ToClrDataAddress(_target);
            data->pStubHeap = loader.GetStubHeap(globalLoaderAllocator).ToClrDataAddress(_target);
            data->appDomainStage = DacpAppDomainDataStage.STAGE_OPEN;
            if (addr != systemDomain)
            {
                TargetPointer pAppDomain = addr.ToTargetPointer(_target);
                data->dwId = _target.ReadGlobal<uint>(Constants.Globals.DefaultADID);

                IEnumerable<Contracts.ModuleHandle> modules = loader.GetModuleHandles(
                    pAppDomain,
                    AssemblyIterationFlags.IncludeLoading |
                    AssemblyIterationFlags.IncludeLoaded |
                    AssemblyIterationFlags.IncludeExecution);

                foreach (Contracts.ModuleHandle module in modules)
                {
                    if (loader.IsAssemblyLoaded(module))
                    {
                        data->AssemblyCount++;
                    }
                }

                IEnumerable<Contracts.ModuleHandle> failedModules = loader.GetModuleHandles(
                    pAppDomain,
                    AssemblyIterationFlags.IncludeFailedToLoad);
                data->FailedAssemblyCount = failedModules.Count();
            }
        }
        catch (System.Exception ex)
        {
            hr = ex.HResult;
        }
#if DEBUG
        if (_legacyImpl is not null)
        {
            DacpAppDomainData dataLocal = default;
            int hrLocal = _legacyImpl.GetAppDomainData(addr, &dataLocal);
            Debug.ValidateHResult(hr, hrLocal);
            if (hr == HResults.S_OK)
            {
                Debug.Assert(data->AppDomainPtr == dataLocal.AppDomainPtr);
                Debug.Assert(data->pHighFrequencyHeap == dataLocal.pHighFrequencyHeap);
                Debug.Assert(data->pLowFrequencyHeap == dataLocal.pLowFrequencyHeap);
                Debug.Assert(data->pStubHeap == dataLocal.pStubHeap);
                Debug.Assert(data->DomainLocalBlock == dataLocal.DomainLocalBlock);
                Debug.Assert(data->pDomainLocalModules == dataLocal.pDomainLocalModules);
                Debug.Assert(data->dwId == dataLocal.dwId);
                Debug.Assert(data->appDomainStage == dataLocal.appDomainStage);
                Debug.Assert(data->AssemblyCount == dataLocal.AssemblyCount);
                Debug.Assert(data->FailedAssemblyCount == dataLocal.FailedAssemblyCount);
            }
        }
#endif
        return hr;

    }
    int ISOSDacInterface.GetAppDomainList(uint count, [In, MarshalUsing(CountElementName = "count"), Out] ClrDataAddress[] values, uint* pNeeded)
    {
        int hr = HResults.S_OK;
        try
        {
            uint i = 0;
            TargetPointer appDomainPointer = _target.ReadGlobalPointer(Constants.Globals.AppDomain);
            TargetPointer appDomain = _target.ReadPointer(appDomainPointer);

            if (appDomain != TargetPointer.Null && values.Length > 0)
            {
                values[0] = appDomain.ToClrDataAddress(_target);
                i = 1;
            }

            if (pNeeded is not null)
            {
                *pNeeded = i;
            }
        }
        catch (System.Exception ex)
        {
            hr = ex.HResult;
        }
#if DEBUG
        if (_legacyImpl is not null)
        {
            ClrDataAddress[] valuesLocal = new ClrDataAddress[count];
            uint neededLocal;
            int hrLocal = _legacyImpl.GetAppDomainList(count, valuesLocal, &neededLocal);
            Debug.ValidateHResult(hr, hrLocal);
            Debug.Assert(pNeeded == null || *pNeeded == neededLocal);
            if (values is not null && values.Length > 0 && valuesLocal.Length > 0)
            {
                Debug.Assert(values[0] == valuesLocal[0], $"cDAC: {values[0]:x}, DAC: {valuesLocal[0]:x}");
            }
        }
#endif
        return hr;
    }
    int ISOSDacInterface.GetAppDomainName(ClrDataAddress addr, uint count, char* name, uint* pNeeded)
    {
        int hr = HResults.S_OK;
        try
        {
            ILoader loader = _target.Contracts.Loader;
            TargetPointer systemDomainPtr = _target.ReadGlobalPointer(Constants.Globals.SystemDomain);
            ClrDataAddress systemDomain = _target.ReadPointer(systemDomainPtr).ToClrDataAddress(_target);

            string? friendlyName = null;
            if (addr != systemDomain)
            {
                try
                {
                    friendlyName = loader.GetAppDomainFriendlyName();
                }
                catch (VirtualReadException)
                {
                    // The FriendlyName field is a PTR_CWSTR (pointer to wide char string).
                    // ReadUtf16String throws VirtualReadException when the pointer targets
                    // unreadable memory (e.g. the name is not yet set during early init).
                    // The native DAC handles this via PTR_AppDomain->m_friendlyName.IsValid()
                    // and falls through to return an empty string. Match that behavior here.
                }
            }

            if (friendlyName is null || friendlyName.Length == 0)
            {
                if (pNeeded is not null)
                {
                    *pNeeded = 1;
                }
                if (name is not null && count > 0)
                {
                    name[0] = '\0';
                }
            }
            else
            {
                if (pNeeded is not null)
                {
                    *pNeeded = (uint)(friendlyName.Length + 1);
                }

                if (name is not null && count > 0)
                {
                    OutputBufferHelpers.CopyStringToBuffer(name, count, pNeeded, friendlyName);
                }
            }
        }
        catch (System.Exception ex)
        {
            hr = ex.HResult;
        }
#if DEBUG
        if (_legacyImpl is not null)
        {
            uint neededLocal;
            char[] nameLocal = new char[count];
            int hrLocal;
            fixed (char* ptr = nameLocal)
            {
                hrLocal = _legacyImpl.GetAppDomainName(addr, count, ptr, &neededLocal);
            }
            Debug.ValidateHResult(hr, hrLocal);
            if (hr == HResults.S_OK)
            {
                Debug.Assert(pNeeded == null || *pNeeded == neededLocal);
                Debug.Assert(name == null || new ReadOnlySpan<char>(nameLocal, 0, (int)neededLocal - 1).SequenceEqual(new string(name)));
            }
        }
#endif
        return hr;
    }
    int ISOSDacInterface.GetAppDomainStoreData(void* data)
    {
        DacpAppDomainStoreData* appDomainStoreData = (DacpAppDomainStoreData*)data;
        int hr = HResults.S_OK;
        try
        {
            appDomainStoreData->sharedDomain = 0;
            TargetPointer systemDomainPtr = _target.ReadGlobalPointer(Constants.Globals.SystemDomain);
            appDomainStoreData->systemDomain = _target.ReadPointer(systemDomainPtr).ToClrDataAddress(_target);
            TargetPointer appDomainPtr = _target.ReadGlobalPointer(Constants.Globals.AppDomain);
            appDomainStoreData->DomainCount = _target.ReadPointer(appDomainPtr) != 0 ? 1 : 0;
        }
        catch (System.Exception ex)
        {
            hr = ex.HResult;
        }
#if DEBUG
        {
            if (_legacyImpl is not null)
            {
                DacpAppDomainStoreData dataLocal = default;
                int hrLocal = _legacyImpl.GetAppDomainStoreData(&dataLocal);
                Debug.ValidateHResult(hr, hrLocal);
                Debug.Assert(appDomainStoreData->sharedDomain == dataLocal.sharedDomain, $"cDAC: {appDomainStoreData->sharedDomain:x}, DAC: {dataLocal.sharedDomain:x}");
                Debug.Assert(appDomainStoreData->systemDomain == dataLocal.systemDomain, $"cDAC: {appDomainStoreData->systemDomain:x}, DAC: {dataLocal.systemDomain:x}");
                Debug.Assert(appDomainStoreData->DomainCount == dataLocal.DomainCount, $"cDAC: {appDomainStoreData->DomainCount}, DAC: {dataLocal.DomainCount}");
            }
        }
#endif
        return hr;
    }
    int ISOSDacInterface.GetApplicationBase(ClrDataAddress appDomain, int count, char* appBase, uint* pNeeded)
    {
        // Method is not supported on CoreCLR
        int hr = HResults.E_FAIL;

#if DEBUG
        if (_legacyImpl is not null)
        {
            int hrLocal = _legacyImpl.GetApplicationBase(appDomain, count, appBase, pNeeded);
            Debug.ValidateHResult(hr, hrLocal);
        }
#endif

        return hr;
    }

    int ISOSDacInterface.GetAssemblyData(ClrDataAddress domain, ClrDataAddress assembly, DacpAssemblyData* data)
    {
        int hr = HResults.S_OK;

        try
        {
            if (assembly == 0 && domain == 0)
                throw new ArgumentException();

            // Zero out data structure
            *data = default;

            data->AssemblyPtr = assembly;
            data->DomainPtr = domain;

            TargetPointer ppAppDomain = _target.ReadGlobalPointer(Constants.Globals.AppDomain);
            TargetPointer pAppDomain = _target.ReadPointer(ppAppDomain);
            data->ParentDomain = pAppDomain.ToClrDataAddress(_target);

            ILoader loader = _target.Contracts.Loader;
            Contracts.ModuleHandle moduleHandle = loader.GetModuleHandleFromAssemblyPtr(assembly.ToTargetPointer(_target));
            data->isDynamic = loader.IsDynamic(moduleHandle) ? 1 : 0;

            // The DAC increments ModuleCount to 1 if assembly->GetModule() is valid,
            // the cDAC assumes that all assemblies will have a module and the above logic relies on that.
            // Therefore we always set ModuleCount to 1.
            data->ModuleCount = 1;
        }
        catch (System.Exception ex)
        {
            hr = ex.HResult;
        }

#if DEBUG
        if (_legacyImpl is not null)
        {
            DacpAssemblyData dataLocal = default;
            int hrLocal = _legacyImpl.GetAssemblyData(domain, assembly, &dataLocal);
            Debug.ValidateHResult(hr, hrLocal);
            if (hr == HResults.S_OK)
            {
                Debug.Assert(data->AssemblyPtr == dataLocal.AssemblyPtr, $"cDAC: {data->AssemblyPtr:x}, DAC: {dataLocal.AssemblyPtr:x}");
                Debug.Assert(data->ClassLoader == dataLocal.ClassLoader, $"cDAC: {data->ClassLoader:x}, DAC: {dataLocal.ClassLoader:x}");
                Debug.Assert(data->ParentDomain == dataLocal.ParentDomain, $"cDAC: {data->ParentDomain:x}, DAC: {dataLocal.ParentDomain:x}");
                Debug.Assert(data->DomainPtr == dataLocal.DomainPtr, $"cDAC: {data->DomainPtr:x}, DAC: {dataLocal.DomainPtr:x}");
                Debug.Assert(data->AssemblySecDesc == dataLocal.AssemblySecDesc, $"cDAC: {data->AssemblySecDesc:x}, DAC: {dataLocal.AssemblySecDesc:x}");
                Debug.Assert(data->isDynamic == dataLocal.isDynamic, $"cDAC: {data->isDynamic}, DAC: {dataLocal.isDynamic}");
                Debug.Assert(data->ModuleCount == dataLocal.ModuleCount, $"cDAC: {data->ModuleCount}, DAC: {dataLocal.ModuleCount}");
                Debug.Assert(data->LoadContext == dataLocal.LoadContext, $"cDAC: {data->LoadContext:x}, DAC: {dataLocal.LoadContext:x}");
                Debug.Assert(data->isDomainNeutral == dataLocal.isDomainNeutral, $"cDAC: {data->isDomainNeutral}, DAC: {dataLocal.isDomainNeutral}");
                Debug.Assert(data->dwLocationFlags == dataLocal.dwLocationFlags, $"cDAC: {data->dwLocationFlags:x}, DAC: {dataLocal.dwLocationFlags:x}");
            }
        }
#endif

        return hr;
    }

    int ISOSDacInterface.GetAssemblyList(ClrDataAddress addr, int count, [In, MarshalUsing(CountElementName = "count"), Out] ClrDataAddress[]? values, int* pNeeded)
    {
        int hr = HResults.S_OK;

        try
        {
            if (addr == 0)
                throw new ArgumentException();
            TargetPointer appDomain = addr.ToTargetPointer(_target);
            TargetPointer systemDomainPtr = _target.ReadGlobalPointer(Constants.Globals.SystemDomain);
            ClrDataAddress systemDomain = _target.ReadPointer(systemDomainPtr).ToClrDataAddress(_target);
            if (addr == systemDomain)
                // We shouldn't be asking for the assemblies in SystemDomain
                throw new ArgumentException();

            ILoader loader = _target.Contracts.Loader;
            List<Contracts.ModuleHandle> modules = loader.GetModuleHandles(
                appDomain,
                AssemblyIterationFlags.IncludeLoading |
                AssemblyIterationFlags.IncludeLoaded |
                AssemblyIterationFlags.IncludeExecution).ToList();

            int n = 0; // number of Assemblies that will be returned
            if (values is not null)
            {
                for (int i = 0; i < modules.Count && n < count; i++)
                {
                    Contracts.ModuleHandle module = modules[i];
                    if (loader.IsAssemblyLoaded(module))
                    {
                        values[n++] = loader.GetAssembly(module).ToClrDataAddress(_target);
                    }
                }
            }
            else
            {
                for (int i = 0; i < modules.Count; i++)
                {
                    Contracts.ModuleHandle module = modules[i];
                    if (loader.IsAssemblyLoaded(module))
                    {
                        n++;
                    }
                }
            }

            if (pNeeded is not null)
            {
                *pNeeded = n;
            }
        }
        catch (System.Exception ex)
        {
            hr = ex.HResult;
        }

#if DEBUG
        if (_legacyImpl is not null)
        {
            ClrDataAddress[]? valuesLocal = values != null ? new ClrDataAddress[count] : null;
            int neededLocal;
            int hrLocal = _legacyImpl.GetAssemblyList(addr, count, valuesLocal, &neededLocal);
            Debug.ValidateHResult(hr, hrLocal);
            if (hr == HResults.S_OK)
            {
                Debug.Assert(pNeeded == null || *pNeeded == neededLocal);
                if (values is not null)
                {
                    // in theory, these don't need to be in the same order, but for consistency it is
                    // easiest for consumers and verification if the DAC and cDAC return the same order
                    for (int i = 0; i < neededLocal; i++)
                    {
                        Debug.Assert(values[i] == valuesLocal![i], $"cDAC: {values[i]:x}, DAC: {valuesLocal[i]:x}");
                    }
                }
            }
        }
#endif

        return hr;
    }
    int ISOSDacInterface.GetAssemblyLocation(ClrDataAddress assembly, int count, char* location, uint* pNeeded)
        => LegacyFallbackHelper.CanFallback() && _legacyImpl is not null ? _legacyImpl.GetAssemblyLocation(assembly, count, location, pNeeded) : HResults.E_NOTIMPL;
    int ISOSDacInterface.GetAssemblyModuleList(ClrDataAddress assembly, uint count, [In, MarshalUsing(CountElementName = "count"), Out] ClrDataAddress[]? modules, uint* pNeeded)
    {
        int hr = HResults.S_OK;
        try
        {
            if (assembly == 0)
                throw new ArgumentException();
            if (modules is not null && modules.Length > 0 && count > 0)
            {
                TargetPointer addr = assembly.ToTargetPointer(_target);
                Contracts.ILoader loader = _target.Contracts.Loader;
                Contracts.ModuleHandle handle = loader.GetModuleHandleFromAssemblyPtr(addr);
                TargetPointer modulePointer = loader.GetModule(handle);
                modules[0] = modulePointer.ToClrDataAddress(_target);
            }

            if (pNeeded is not null)
            {
                *pNeeded = 1;
            }
        }
        catch (System.Exception ex)
        {
            hr = ex.HResult;
        }

#if DEBUG
        if (_legacyImpl is not null)
        {
            ClrDataAddress[] modulesLocal = new ClrDataAddress[count];
            uint neededLocal;
            int hrLocal = _legacyImpl.GetAssemblyModuleList(assembly, count, modulesLocal, &neededLocal);
            Debug.ValidateHResult(hr, hrLocal);
            if (hr == HResults.S_OK)
            {
                Debug.Assert(pNeeded == null || *pNeeded == neededLocal);
                if (modules is not null && modules.Length > 0)
                {
                    Debug.Assert(modules[0] == modulesLocal[0], $"cDAC: {modules[0]:x}, DAC: {modulesLocal[0]:x}");
                }
            }
        }
#endif
        return hr;

    }
    int ISOSDacInterface.GetAssemblyName(ClrDataAddress assembly, uint count, char* name, uint* pNeeded)
    {
        int hr = HResults.S_OK;
        try
        {
            if (name is not null)
                name[0] = '\0';
            Contracts.ILoader contract = _target.Contracts.Loader;
            Contracts.ModuleHandle handle = contract.GetModuleHandleFromAssemblyPtr(assembly.ToTargetPointer(_target));
            string path = contract.GetPath(handle);

            // Return not implemented for empty paths for non-reflection emit assemblies (for example, loaded from memory)
            if (string.IsNullOrEmpty(path))
            {
                Contracts.ModuleFlags flags = contract.GetFlags(handle);
                if (!flags.HasFlag(Contracts.ModuleFlags.ReflectionEmit))
                    hr = HResults.E_NOTIMPL;
                else
                    hr = HResults.E_FAIL;
            }

            OutputBufferHelpers.CopyStringToBuffer(name, count, pNeeded, path);
        }
        catch (System.Exception ex)
        {
            hr = ex.HResult;
        }

#if DEBUG
        if (_legacyImpl is not null)
        {
            char[] fileNameLocal = new char[count];
            uint neededLocal;
            int hrLocal;
            fixed (char* ptr = fileNameLocal)
            {
                hrLocal = _legacyImpl.GetAssemblyName(assembly, count, ptr, &neededLocal);
            }
            Debug.ValidateHResult(hr, hrLocal);
            if (hr == HResults.S_OK)
            {
                Debug.Assert(pNeeded == null || *pNeeded == neededLocal);
                Debug.Assert(name == null || new ReadOnlySpan<char>(fileNameLocal, 0, (int)neededLocal - 1).SequenceEqual(new string(name)));
            }
        }
#endif
        return hr;
    }
    int ISOSDacInterface.GetCCWData(ClrDataAddress ccw, DacpCCWData* data)
    {
        int hr = HResults.S_OK;
        try
        {
            if (ccw == 0 || data == null)
                throw new ArgumentException();

            *data = default;
            Contracts.IBuiltInCOM contract = _target.Contracts.BuiltInCOM;
            // Try to resolve as a COM interface pointer; if not recognised, treat as a direct CCW pointer.
            TargetPointer ccwPtr = contract.GetCCWFromInterfacePointer(ccw.ToTargetPointer(_target));
            if (ccwPtr == TargetPointer.Null)
                ccwPtr = ccw.ToTargetPointer(_target);

            // Navigate to the start wrapper, mirroring DACGetCCWFromAddress.
            ccwPtr = contract.GetStartWrapper(ccwPtr);

            SimpleComCallWrapperData sccwData = contract.GetSimpleComCallWrapperData(ccwPtr);
            int refCount = (int)sccwData.RefCount;

            data->outerIUnknown = sccwData.OuterIUnknown.ToClrDataAddress(_target);
            TargetPointer handle = contract.GetObjectHandle(ccwPtr);
            data->handle = handle.ToClrDataAddress(_target);
            if (handle != TargetPointer.Null)
            {
                data->managedObject = _target.ReadPointer(handle).ToClrDataAddress(_target);
            }
            data->ccwAddress = ccwPtr.ToClrDataAddress(_target);
            data->refCount = refCount;
            data->interfaceCount = contract.GetCCWInterfaces(ccwPtr).Count();
            data->isNeutered = sccwData.IsNeutered ? Interop.BOOL.TRUE : Interop.BOOL.FALSE;
            data->jupiterRefCount = 0;
            data->isPegged = Interop.BOOL.FALSE;
            data->isGlobalPegged = Interop.BOOL.FALSE;
            data->hasStrongRef = (refCount > 0) && !sccwData.IsHandleWeak ? Interop.BOOL.TRUE : Interop.BOOL.FALSE;
            data->isExtendsCOMObject = sccwData.IsExtendsCOMObject ? Interop.BOOL.TRUE : Interop.BOOL.FALSE;
            data->isAggregated = sccwData.IsAggregated ? Interop.BOOL.TRUE : Interop.BOOL.FALSE;
        }
        catch (System.Exception ex)
        {
            hr = ex.HResult;
        }
#if DEBUG
        if (_legacyImpl is not null)
        {
            DacpCCWData dataLocal = default;
            int hrLocal = _legacyImpl.GetCCWData(ccw, &dataLocal);
            Debug.ValidateHResult(hr, hrLocal);
            if (hr == HResults.S_OK)
            {
                Debug.Assert(data->outerIUnknown == dataLocal.outerIUnknown, $"cDAC outerIUnknown: {data->outerIUnknown:x}, DAC: {dataLocal.outerIUnknown:x}");
                Debug.Assert(data->managedObject == dataLocal.managedObject, $"cDAC managedObject: {data->managedObject:x}, DAC: {dataLocal.managedObject:x}");
                Debug.Assert(data->handle == dataLocal.handle, $"cDAC handle: {data->handle:x}, DAC: {dataLocal.handle:x}");
                Debug.Assert(data->ccwAddress == dataLocal.ccwAddress, $"cDAC ccwAddress: {data->ccwAddress:x}, DAC: {dataLocal.ccwAddress:x}");
                Debug.Assert(data->refCount == dataLocal.refCount, $"cDAC refCount: {data->refCount}, DAC: {dataLocal.refCount}");
                Debug.Assert(data->interfaceCount == dataLocal.interfaceCount, $"cDAC interfaceCount: {data->interfaceCount}, DAC: {dataLocal.interfaceCount}");
                Debug.Assert(data->isNeutered == dataLocal.isNeutered, $"cDAC isNeutered: {data->isNeutered}, DAC: {dataLocal.isNeutered}");
                Debug.Assert(data->jupiterRefCount == dataLocal.jupiterRefCount, $"cDAC jupiterRefCount: {data->jupiterRefCount}, DAC: {dataLocal.jupiterRefCount}");
                Debug.Assert(data->isPegged == dataLocal.isPegged, $"cDAC isPegged: {data->isPegged}, DAC: {dataLocal.isPegged}");
                Debug.Assert(data->isGlobalPegged == dataLocal.isGlobalPegged, $"cDAC isGlobalPegged: {data->isGlobalPegged}, DAC: {dataLocal.isGlobalPegged}");
                Debug.Assert(data->hasStrongRef == dataLocal.hasStrongRef, $"cDAC hasStrongRef: {data->hasStrongRef}, DAC: {dataLocal.hasStrongRef}");
                Debug.Assert(data->isExtendsCOMObject == dataLocal.isExtendsCOMObject, $"cDAC isExtendsCOMObject: {data->isExtendsCOMObject}, DAC: {dataLocal.isExtendsCOMObject}");
                Debug.Assert(data->isAggregated == dataLocal.isAggregated, $"cDAC isAggregated: {data->isAggregated}, DAC: {dataLocal.isAggregated}");
            }
        }
#endif
        return hr;
    }
    int ISOSDacInterface.GetCCWInterfaces(ClrDataAddress ccw, uint count, [In, MarshalUsing(CountElementName = nameof(count)), Out] DacpCOMInterfacePointerData[]? interfaces, uint* pNeeded)
    {
        int hr = HResults.S_OK;
#if DEBUG
        int numWritten = 0;
#endif
        try
        {
            if (ccw == 0 || (interfaces == null && pNeeded == null))
                throw new ArgumentException();

            Contracts.IBuiltInCOM builtInCOMContract = _target.Contracts.BuiltInCOM; // E_NOTIMPL if contract is not present
            // Try to resolve as a COM interface pointer; if not recognised, treat as a direct CCW pointer.
            // GetCCWInterfaces navigates to the start of the chain in both cases.
            TargetPointer startCCW = builtInCOMContract.GetCCWFromInterfacePointer(ccw.ToTargetPointer(_target));
            if (startCCW == TargetPointer.Null)
                startCCW = ccw.ToTargetPointer(_target);
            IEnumerable<Contracts.COMInterfacePointerData> result =
                builtInCOMContract.GetCCWInterfaces(startCCW);

            if (interfaces == null)
            {
                uint c = (uint)result.Count();
#if DEBUG
                numWritten = (int)c;
#endif
                if (pNeeded is not null)
                    *pNeeded = c;
            }
            else
            {
                uint itemIndex = 0;
                foreach (Contracts.COMInterfacePointerData item in result)
                {
                    if (itemIndex >= count)
                    {
#if DEBUG
                        numWritten = (int)itemIndex;
#endif
                        throw new ArgumentException();
                    }
                    interfaces[itemIndex].methodTable = item.MethodTable.ToClrDataAddress(_target);
                    interfaces[itemIndex].interfacePtr = item.InterfacePointerAddress.ToClrDataAddress(_target);
                    interfaces[itemIndex].comContext = 0;
                    itemIndex++;
                }
#if DEBUG
                numWritten = (int)itemIndex;
#endif
                if (pNeeded is not null)
                {
                    *pNeeded = itemIndex;
                }
            }
        }
        catch (System.Exception ex)
        {
            hr = ex.HResult;
        }
#if DEBUG
        if (_legacyImpl is not null)
        {
            DacpCOMInterfacePointerData[]? interfacesLocal = interfaces != null ? new DacpCOMInterfacePointerData[(int)count] : null;
            uint pNeededLocal = 0;
            int hrLocal = _legacyImpl.GetCCWInterfaces(ccw, count, interfacesLocal, pNeeded == null && interfacesLocal == null ? null : &pNeededLocal);
            Debug.ValidateHResult(hr, hrLocal);
            if (hr == HResults.S_OK)
            {
                Debug.Assert(pNeeded is null || *pNeeded == pNeededLocal, $"cDAC count: {(pNeeded is null ? "null" : (*pNeeded).ToString())}, DAC count: {pNeededLocal}");
                if (interfaces != null && interfacesLocal != null)
                {
                    for (uint i = 0; i < (int)pNeededLocal; i++)
                    {
                        Debug.Assert(interfaces[i].methodTable == interfacesLocal![i].methodTable, $"cDAC methodTable[{i}]: {interfaces[i].methodTable:x}, DAC: {interfacesLocal[i].methodTable:x}");
                        Debug.Assert(interfaces[i].interfacePtr == interfacesLocal![i].interfacePtr, $"cDAC interfacePtr[{i}]: {interfaces[i].interfacePtr:x}, DAC: {interfacesLocal[i].interfacePtr:x}");
                        Debug.Assert(interfaces[i].comContext == interfacesLocal![i].comContext, $"cDAC comContext[{i}]: {interfaces[i].comContext:x}, DAC: {interfacesLocal[i].comContext:x}");
                    }
                }
            }
        }
#endif
        return hr;
    }
    int ISOSDacInterface.GetClrWatsonBuckets(ClrDataAddress thread, void* pGenericModeBlock)
    {
        int hr = HResults.S_OK;
        Contracts.IThread threadContract = _target.Contracts.Thread;
        byte[] buckets = Array.Empty<byte>();
        try
        {
            if (_target.Contracts.RuntimeInfo.GetTargetOperatingSystem() != RuntimeInfoOperatingSystem.Windows)
                throw Marshal.GetExceptionForHR(HResults.E_FAIL)!;

            if (thread == 0 || pGenericModeBlock == null)
                throw new ArgumentException();

            buckets = threadContract.GetWatsonBuckets(thread.ToTargetPointer(_target));
            if (buckets.Length != 0)
            {
                var dest = new Span<byte>(pGenericModeBlock, buckets.Length);
                buckets.AsSpan().CopyTo(dest);
            }
            else
            {
                hr = HResults.S_FALSE; // No Watson buckets found
            }
        }
        catch (System.Exception ex)
        {
            hr = ex.HResult;
        }
#if DEBUG
        if (_legacyImpl is not null)
        {
            int hrLocal;
            int sizeOfGenericModeBlock = (int)_target.ReadGlobal<uint>(Constants.Globals.SizeOfGenericModeBlock);
            byte[] genericModeBlockLocal = new byte[sizeOfGenericModeBlock];
            fixed (byte* ptr = genericModeBlockLocal)
            {
                hrLocal = _legacyImpl.GetClrWatsonBuckets(thread, ptr);
            }

            Debug.ValidateHResult(hr, hrLocal);
            if (hr == HResults.S_OK)
            {
                Debug.Assert(new ReadOnlySpan<byte>(genericModeBlockLocal, 0, sizeOfGenericModeBlock).SequenceEqual(new Span<byte>(pGenericModeBlock, sizeOfGenericModeBlock)));
            }
        }
#endif
        return hr;
    }
    int ISOSDacInterface.GetCodeHeaderData(ClrDataAddress ip, DacpCodeHeaderData* data)
    {
        int hr = HResults.S_OK;
        try
        {
            if (ip == 0 || data == null)
                throw new ArgumentException();

            IExecutionManager eman = _target.Contracts.ExecutionManager;
            IGCInfo gcInfo = _target.Contracts.GCInfo;

            TargetCodePointer targetCodePointer = ip.ToTargetCodePointer(_target);
            if (eman.GetCodeBlockHandle(targetCodePointer) is not CodeBlockHandle cbh)
            {
                TargetPointer methodDesc = eman.NonVirtualEntry2MethodDesc(targetCodePointer);
                if (methodDesc == TargetPointer.Null)
                    throw new ArgumentException();
                data->MethodDescPtr = methodDesc.ToClrDataAddress(_target);
                data->JITType = JitTypes.TYPE_UNKNOWN;
                data->GCInfo = 0;
                data->MethodStart = 0;
                data->MethodSize = 0;
                data->HotRegionSize = 0;
                data->ColdRegionSize = 0;
                data->ColdRegionStart = 0;
            }
            else
            {
                data->MethodDescPtr = eman.GetMethodDesc(cbh).ToClrDataAddress(_target);

                Contracts.CodeKind codeKind = eman.GetCodeKind(targetCodePointer);
                data->JITType = codeKind switch
                {
                    Contracts.CodeKind.Jitted => JitTypes.TYPE_JIT,
                    Contracts.CodeKind.ReadyToRun => JitTypes.TYPE_PJIT,
                    Contracts.CodeKind.Interpreter => JitTypes.TYPE_INTERPRETER,
                    _ => JitTypes.TYPE_UNKNOWN,
                };

                eman.GetGCInfo(cbh, out TargetPointer pGcInfo, out uint gcVersion);
                data->GCInfo = pGcInfo.ToClrDataAddress(_target);

                data->MethodStart = eman.GetStartAddress(cbh).Value;

                // Mirrors native ClrDataAccess::GetCodeHeaderData which routes through
                // EECodeInfo::GetCodeManager()->GetFunctionSize: interpreter code uses the
                // interpreter-specific GC info encoding, all other code uses the platform
                // GC info encoding.
                IGCInfoHandle gcInfoHandle = codeKind == Contracts.CodeKind.Interpreter
                    ? gcInfo.DecodeInterpreterGCInfo(pGcInfo, gcVersion)
                    : gcInfo.DecodePlatformSpecificGCInfo(pGcInfo, gcVersion);
                data->MethodSize = gcInfo.GetCodeLength(gcInfoHandle);

                eman.GetMethodRegionInfo(cbh, out uint hotRegionSize, out TargetPointer coldRegionStart, out uint coldRegionSize);
                data->HotRegionSize = hotRegionSize;
                data->ColdRegionSize = coldRegionSize;
                data->ColdRegionStart = coldRegionStart.ToClrDataAddress(_target);
            }
        }
        catch (System.Exception ex)
        {
            hr = ex.HResult;
        }
#if DEBUG
        if (_legacyImpl is not null)
        {
            DacpCodeHeaderData dataLocal = default;
            int hrLocal = _legacyImpl.GetCodeHeaderData(ip, &dataLocal);
            Debug.ValidateHResult(hr, hrLocal);
            if (hr == HResults.S_OK)
            {
                Debug.Assert(data->MethodDescPtr == dataLocal.MethodDescPtr, $"cDAC: {data->MethodDescPtr:x}, DAC: {dataLocal.MethodDescPtr:x}");
                Debug.Assert(data->JITType == dataLocal.JITType, $"cDAC: {data->JITType}, DAC: {dataLocal.JITType}");
                Debug.Assert(data->GCInfo == dataLocal.GCInfo, $"cDAC: {data->GCInfo:x}, DAC: {dataLocal.GCInfo:x}");
                Debug.Assert(data->MethodStart == dataLocal.MethodStart, $"cDAC: {data->MethodStart:x}, DAC: {dataLocal.MethodStart:x}");
                Debug.Assert(data->MethodSize == dataLocal.MethodSize, $"cDAC: {data->MethodSize}, DAC: {dataLocal.MethodSize}");
                Debug.Assert(data->HotRegionSize == dataLocal.HotRegionSize, $"cDAC: {data->HotRegionSize}, DAC: {dataLocal.HotRegionSize}");
                Debug.Assert(data->ColdRegionStart == dataLocal.ColdRegionStart, $"cDAC: {data->ColdRegionStart:x}, DAC: {dataLocal.ColdRegionStart:x}");
                Debug.Assert(data->ColdRegionSize == dataLocal.ColdRegionSize, $"cDAC: {data->ColdRegionSize}, DAC: {dataLocal.ColdRegionSize}");
            }
        }
#endif
        return hr;
    }
    int ISOSDacInterface.GetCodeHeapList(ClrDataAddress jitManager, uint count, [In, MarshalUsing(CountElementName = nameof(count)), Out] DacpJitCodeHeapInfo[]? codeHeaps, uint* pNeeded)
    {
        // returns the code heap list from the single global EEJitManager.
        int hr = HResults.S_OK;
        try
        {
            if (jitManager == 0 || (codeHeaps == null && pNeeded == null))
                throw new ArgumentException();

            IExecutionManager em = _target.Contracts.ExecutionManager;
#if DEBUG
            Contracts.JitManagerInfo jitManagerInfo = em.GetEEJitManagerInfo();
            Debug.Assert(jitManager.ToTargetPointer(_target) == jitManagerInfo.ManagerAddress);
#endif

            List<ICodeHeapInfo> heapInfos = em.GetCodeHeapInfos().ToList();
            int i = 0;
            if (codeHeaps is not null)
            {
                for (; i < heapInfos.Count && i < count; i++)
                {
                    codeHeaps[i] = default;
                    switch (heapInfos[i])
                    {
                        case Contracts.LoaderCodeHeapInfo loader:
                            codeHeaps[i].codeHeapType = DacpJitCodeHeapInfo.CodeHeapType.CODEHEAP_LOADER;
                            codeHeaps[i].LoaderHeap = loader.LoaderHeapAddress.ToClrDataAddress(_target);
                            break;
                        case Contracts.HostCodeHeapInfo host:
                            codeHeaps[i].codeHeapType = DacpJitCodeHeapInfo.CodeHeapType.CODEHEAP_HOST;
                            codeHeaps[i].baseAddr    = host.BaseAddress.ToClrDataAddress(_target);
                            codeHeaps[i].currentAddr = host.CurrentAddress.ToClrDataAddress(_target);
                            break;
                        default:
                            codeHeaps[i].codeHeapType = DacpJitCodeHeapInfo.CodeHeapType.CODEHEAP_UNKNOWN;
                            break;
                    }
                }
            }

            if (pNeeded is not null)
                *pNeeded = codeHeaps is not null ? (uint)i : (uint)heapInfos.Count;
        }
        catch (System.Exception ex)
        {
            hr = ex.HResult;
        }
#if DEBUG
        if (_legacyImpl is not null)
        {
            uint neededLocal = 0;
            DacpJitCodeHeapInfo[]? legacyHeaps = codeHeaps is not null ? new DacpJitCodeHeapInfo[(int)count] : null;
            int hrLocal = _legacyImpl.GetCodeHeapList(jitManager, count, legacyHeaps, codeHeaps is null && pNeeded is null ? null : &neededLocal);
            Debug.ValidateHResult(hr, hrLocal);
            if (hr == HResults.S_OK)
            {
                if (pNeeded != null)
                {
                    Debug.Assert(*pNeeded == neededLocal, $"cDAC count: {(*pNeeded):x}, DAC count: {neededLocal:x}");
                }
                if (codeHeaps != null && legacyHeaps != null)
                {
                    for (uint i = 0; i < neededLocal && i < count; i++)
                    {
                        Debug.Assert(codeHeaps[i].codeHeapType == legacyHeaps![i].codeHeapType,
                            $"cDAC heap[{i}] type: {codeHeaps[i].codeHeapType}, DAC: {legacyHeaps[i].codeHeapType}");
                        if (codeHeaps[i].codeHeapType == DacpJitCodeHeapInfo.CodeHeapType.CODEHEAP_LOADER)
                            Debug.Assert(codeHeaps[i].LoaderHeap == legacyHeaps[i].LoaderHeap,
                                $"cDAC heap[{i}] LoaderHeap: {codeHeaps[i].LoaderHeap:x}, DAC: {legacyHeaps[i].LoaderHeap:x}");
                        else if (codeHeaps[i].codeHeapType == DacpJitCodeHeapInfo.CodeHeapType.CODEHEAP_HOST)
                        {
                            Debug.Assert(codeHeaps[i].baseAddr == legacyHeaps[i].baseAddr,
                                $"cDAC heap[{i}] baseAddr: {codeHeaps[i].baseAddr:x}, DAC: {legacyHeaps[i].baseAddr:x}");
                            Debug.Assert(codeHeaps[i].currentAddr == legacyHeaps[i].currentAddr,
                                $"cDAC heap[{i}] currentAddr: {codeHeaps[i].currentAddr:x}, DAC: {legacyHeaps[i].currentAddr:x}");
                        }
                    }
                }
            }
        }
#endif
        return hr;
    }
    int ISOSDacInterface.GetDacModuleHandle(void* phModule)
        => LegacyFallbackHelper.CanFallback() && _legacyImpl is not null ? _legacyImpl.GetDacModuleHandle(phModule) : HResults.E_NOTIMPL;
    int ISOSDacInterface.GetDomainFromContext(ClrDataAddress context, ClrDataAddress* domain)
    {
        int hr = HResults.S_OK;
        try
        {
            if (context == 0 || domain == null)
                throw new ArgumentException();
            *domain = context;
        }
        catch (System.Exception ex)
        {
            hr = ex.HResult;
        }
#if DEBUG
        if (_legacyImpl is not null)
        {
            ClrDataAddress domainLocal;
            int hrLocal = _legacyImpl.GetDomainFromContext(context, &domainLocal);
            Debug.ValidateHResult(hr, hrLocal);
            if (hr == HResults.S_OK)
            {
                Debug.Assert(domainLocal == context, $"cDAC: {context:x}, DAC: {domainLocal:x}");
            }
        }
#endif
        return hr;
    }
    int ISOSDacInterface.GetDomainLocalModuleData(ClrDataAddress addr, void* data)
    {
        // CoreCLR does not use domain local modules anymore
        int hr = HResults.E_NOTIMPL;

#if DEBUG
        if (_legacyImpl is not null)
        {
            int hrLocal = _legacyImpl.GetDomainLocalModuleData(addr, data);
            Debug.ValidateHResult(hr, hrLocal);
        }
#endif

        return hr;
    }

    int ISOSDacInterface.GetDomainLocalModuleDataFromAppDomain(ClrDataAddress appDomainAddr, int moduleID, void* data)
    {
        // CoreCLR does not support multi-appdomain shared assembly loading. Thus, a non-pointer sized moduleID cannot exist.
        int hr = HResults.E_INVALIDARG;

#if DEBUG
        if (_legacyImpl is not null)
        {
            int hrLocal = _legacyImpl.GetDomainLocalModuleDataFromAppDomain(appDomainAddr, moduleID, data);
            Debug.ValidateHResult(hr, hrLocal);
        }
#endif

        return hr;
    }
    int ISOSDacInterface.GetDomainLocalModuleDataFromModule(ClrDataAddress moduleAddr, void* data)
    {
        // CoreCLR does not use domain local modules anymore
        int hr = HResults.E_NOTIMPL;

#if DEBUG
        if (_legacyImpl is not null)
        {
            int hrLocal = _legacyImpl.GetDomainLocalModuleDataFromModule(moduleAddr, data);
            Debug.ValidateHResult(hr, hrLocal);
        }
#endif

        return hr;
    }
    int ISOSDacInterface.GetFailedAssemblyData(ClrDataAddress assembly, uint* pContext, int* pResult)
        => LegacyFallbackHelper.CanFallback() && _legacyImpl is not null ? _legacyImpl.GetFailedAssemblyData(assembly, pContext, pResult) : HResults.E_NOTIMPL;
    int ISOSDacInterface.GetFailedAssemblyDisplayName(ClrDataAddress assembly, uint count, char* name, uint* pNeeded)
        => LegacyFallbackHelper.CanFallback() && _legacyImpl is not null ? _legacyImpl.GetFailedAssemblyDisplayName(assembly, count, name, pNeeded) : HResults.E_NOTIMPL;
    int ISOSDacInterface.GetFailedAssemblyList(ClrDataAddress appDomain, int count, [In, MarshalUsing(CountElementName = "count"), Out] ClrDataAddress[] values, uint* pNeeded)
        => LegacyFallbackHelper.CanFallback() && _legacyImpl is not null ? _legacyImpl.GetFailedAssemblyList(appDomain, count, values, pNeeded) : HResults.E_NOTIMPL;
    int ISOSDacInterface.GetFailedAssemblyLocation(ClrDataAddress assembly, uint count, char* location, uint* pNeeded)
    {
        int hr = HResults.S_OK;
        try
        {
            if (assembly == 0 || (location == null && pNeeded == null) || (location != null && count == 0))
                throw new ArgumentException();

            if (pNeeded != null)
                *pNeeded = 1;
            if (location != null)
                location[0] = '\0';
        }
        catch (System.Exception ex)
        {
            hr = ex.HResult;
        }

#if DEBUG
        if (_legacyImpl is not null)
        {
            char[] locationLocal = new char[count];
            uint neededLocal;
            int hrLocal;
            fixed (char* ptr = locationLocal)
            {
                hrLocal = _legacyImpl.GetFailedAssemblyLocation(assembly, count, ptr, &neededLocal);
            }
            Debug.ValidateHResult(hr, hrLocal);
            if (hr == HResults.S_OK)
            {
                Debug.Assert(pNeeded == null || *pNeeded == neededLocal);
                Debug.Assert(location == null || new ReadOnlySpan<char>(locationLocal, 0, (int)neededLocal - 1).SequenceEqual(new string(location)));
            }
        }
#endif
        return hr;
    }
    int ISOSDacInterface.GetFieldDescData(ClrDataAddress fieldDesc, DacpFieldDescData* data)
    {
        int hr = HResults.S_OK;
        try
        {
            if (fieldDesc == 0 || data == null)
                throw new ArgumentException();

            IRuntimeTypeSystem rtsContract = _target.Contracts.RuntimeTypeSystem;
            IEcmaMetadata ecmaMetadataContract = _target.Contracts.EcmaMetadata;
            ISignature signatureContract = _target.Contracts.Signature;

            TargetPointer fieldDescTargetPtr = fieldDesc.ToTargetPointer(_target);
            CorElementType fieldDescType = rtsContract.GetFieldDescType(fieldDescTargetPtr);
            data->Type = fieldDescType;
            data->sigType = fieldDescType;

            uint token = rtsContract.GetFieldDescMemberDef(fieldDescTargetPtr);
            FieldDefinitionHandle fieldHandle = (FieldDefinitionHandle)MetadataTokens.Handle((int)token);

            TargetPointer enclosingMT = rtsContract.GetMTOfEnclosingClass(fieldDescTargetPtr);
            TypeHandle ctx = rtsContract.GetTypeHandle(enclosingMT);
            TargetPointer modulePtr = rtsContract.GetModule(ctx);
            Contracts.ModuleHandle moduleHandle = _target.Contracts.Loader.GetModuleHandleFromModulePtr(modulePtr);
            MetadataReader mdReader = ecmaMetadataContract.GetMetadata(moduleHandle)!;
            FieldDefinition fieldDef = mdReader.GetFieldDefinition(fieldHandle);
            try
            {
                // try to completely decode the signature
                TypeHandle foundTypeHandle = signatureContract.DecodeFieldSignature(fieldDef.Signature, moduleHandle, ctx);

                // get the MT of the type
                // This is an implementation detail of the DAC that we replicate here to get method tables for non-MT types
                // that we can return to SOS for pretty-printing.
                // In the future we may want to return a TypeHandle instead of a MethodTable, and modify SOS to do more complete pretty-printing.
                // DAC equivalent: src/coreclr/vm/typehandle.inl TypeHandle::GetMethodTable
                if (rtsContract.IsFunctionPointer(foundTypeHandle, out _, out _) || rtsContract.IsPointer(foundTypeHandle))
                    data->MTOfType = rtsContract.GetPrimitiveType(CorElementType.U).Address.ToClrDataAddress(_target);
                // array MTs
                else if (rtsContract.IsArray(foundTypeHandle, out _))
                    data->MTOfType = foundTypeHandle.Address.ToClrDataAddress(_target);
                else
                {
                    try
                    {
                        // value typedescs
                        TypeHandle paramTypeHandle = rtsContract.GetTypeParam(foundTypeHandle);
                        data->MTOfType = paramTypeHandle.Address.ToClrDataAddress(_target);
                    }
                    catch (ArgumentException)
                    {
                        // non-array MTs
                        data->MTOfType = foundTypeHandle.Address.ToClrDataAddress(_target);
                    }
                }
            }
            catch (VirtualReadException)
            {
                // if we can't find the MT (e.g in a minidump)
                data->MTOfType = 0;
            }

            // partial decoding of signature
            BlobReader blobReader = mdReader.GetBlobReader(fieldDef.Signature);
            SignatureHeader header = blobReader.ReadSignatureHeader();
            // read the header byte and check for correctness
            if (header.Kind != SignatureKind.Field)
                throw new BadImageFormatException();
            // read the top-level type
            CorElementType typeCode;
            EntityHandle entityHandle;
            // in a loop, read custom modifiers until we get to the underlying type
            do
            {
                typeCode = (CorElementType)blobReader.ReadByte();
                entityHandle = blobReader.ReadTypeHandle(); // consume the type
            } while (typeCode is CorElementType.CModReqd or CorElementType.CModOpt); // eat custom modifiers

            if (typeCode is CorElementType.Class or CorElementType.ValueType)
            {
                // if the typecode is class or value, we have been able to read the token that follows in the sig
                data->TokenOfType = (uint)MetadataTokens.GetToken(entityHandle);
            }
            else
            {
                // otherwise we have not found the token here, but we can encode the underlying type in sigType
                data->TokenOfType = (uint)EcmaMetadataUtils.TokenType.mdtTypeDef;
                if (data->MTOfType == 0)
                    data->sigType = typeCode;
            }

            data->ModuleOfType = modulePtr.ToClrDataAddress(_target);
            data->mb = token;
            data->MTOfEnclosingClass = ctx.Address.ToClrDataAddress(_target);
            data->dwOffset = rtsContract.GetFieldDescOffset(fieldDescTargetPtr, fieldDef);
            data->bIsThreadLocal = rtsContract.IsFieldDescThreadStatic(fieldDescTargetPtr) ? 1 : 0;
            data->bIsContextLocal = 0;
            data->bIsStatic = rtsContract.IsFieldDescStatic(fieldDescTargetPtr) ? 1 : 0;
            data->NextField = fieldDescTargetPtr + _target.GetTypeInfo(DataType.FieldDesc).Size!.Value;
        }
        catch (System.Exception ex)
        {
            hr = ex.HResult;
        }
#if DEBUG
        if (_legacyImpl is not null)
        {
            DacpFieldDescData dataLocal = default;
            int hrLocal = _legacyImpl.GetFieldDescData(fieldDesc, &dataLocal);
            Debug.ValidateHResult(hr, hrLocal);
            if (hr == HResults.S_OK)
            {
                Debug.Assert(data->Type == dataLocal.Type, $"cDAC: {data->Type}, DAC: {dataLocal.Type}");
                Debug.Assert(data->sigType == dataLocal.sigType, $"cDAC: {data->sigType}, DAC: {dataLocal.sigType}");
                Debug.Assert(data->TokenOfType == dataLocal.TokenOfType, $"cDAC: {data->TokenOfType:x}, DAC: {dataLocal.TokenOfType:x}");
                Debug.Assert(data->MTOfType == dataLocal.MTOfType, $"cDAC: {data->MTOfType:x}, DAC: {dataLocal.MTOfType:x}");
                Debug.Assert(data->ModuleOfType == dataLocal.ModuleOfType, $"cDAC: {data->ModuleOfType:x}, DAC: {dataLocal.ModuleOfType:x}");
                Debug.Assert(data->mb == dataLocal.mb, $"cDAC: {data->mb:x}, DAC: {dataLocal.mb:x}");
                Debug.Assert(data->MTOfEnclosingClass == dataLocal.MTOfEnclosingClass, $"cDAC: {data->MTOfEnclosingClass:x}, DAC: {dataLocal.MTOfEnclosingClass:x}");
                Debug.Assert(data->dwOffset == dataLocal.dwOffset, $"cDAC: {data->dwOffset:x}, DAC: {dataLocal.dwOffset:x}");
                Debug.Assert(data->bIsThreadLocal == dataLocal.bIsThreadLocal, $"cDAC: {data->bIsThreadLocal}, DAC: {dataLocal.bIsThreadLocal}");
                Debug.Assert(data->bIsContextLocal == dataLocal.bIsContextLocal, $"cDAC: {data->bIsContextLocal}, DAC: {dataLocal.bIsContextLocal}");
                Debug.Assert(data->bIsStatic == dataLocal.bIsStatic, $"cDAC: {data->bIsStatic}, DAC: {dataLocal.bIsStatic}");
                Debug.Assert(data->NextField == dataLocal.NextField, $"cDAC: {data->NextField:x}, DAC: {dataLocal.NextField:x}");
            }
        }
#endif
        return hr;
    }

    int ISOSDacInterface.GetFrameName(ClrDataAddress vtable, uint count, char* frameName, uint* pNeeded)
    {
        int hr = HResults.S_OK;
        try
        {
            if (vtable == 0)
                throw new ArgumentException();
            IStackWalk stackWalk = _target.Contracts.StackWalk;
            string name = stackWalk.GetFrameName(new(vtable));

            if (string.IsNullOrEmpty(name))
                throw new ArgumentException();

            OutputBufferHelpers.CopyStringToBuffer(frameName, count, pNeeded, name);

            if (frameName is not null && pNeeded is not null)
            {
                // the DAC version of this API does not count the trailing null terminator
                // if a buffer is provided
                (*pNeeded)--;
            }
        }
        catch (System.Exception ex)
        {
            hr = ex.HResult;
        }

#if DEBUG
        if (_legacyImpl is not null)
        {
            char[] nameLocal = new char[count];
            uint neededLocal;
            int hrLocal;
            fixed (char* ptr = nameLocal)
            {
                hrLocal = _legacyImpl.GetFrameName(vtable, count, ptr, &neededLocal);
            }
            Debug.ValidateHResult(hr, hrLocal);
            if (hr == HResults.S_OK)
            {
                Debug.Assert(pNeeded == null || *pNeeded == neededLocal);
                Debug.Assert(frameName == null || new ReadOnlySpan<char>(nameLocal, 0, (int)neededLocal).SequenceEqual(new string(frameName)),
                    $"cDAC: {new string(frameName)}, DAC: {new string(nameLocal, 0, (int)neededLocal)}");
            }
        }
#endif

        return hr;
    }
    int ISOSDacInterface.GetGCHeapData(DacpGcHeapData* data)
    {
        int hr = HResults.S_OK;

        try
        {
            if (data == null)
                throw new ArgumentException();
            IGC gc = _target.Contracts.GC;
            string[] heapType = gc.GetGCIdentifiers();
            if (!heapType.Contains(GCIdentifiers.Workstation) && !heapType.Contains(GCIdentifiers.Server))
            {
                // If the GC type is not recognized, we cannot provide heap data
                hr = HResults.E_FAIL;
            }
            else
            {
                data->g_max_generation = gc.GetMaxGeneration();
                data->bServerMode = heapType.Contains(GCIdentifiers.Server) ? 1 : 0;
                data->bGcStructuresValid = gc.GetGCStructuresValid() ? 1 : 0;
                data->HeapCount = gc.GetGCHeapCount();
            }
        }
        catch (System.Exception ex)
        {
            hr = ex.HResult;
        }

#if DEBUG
        if (_legacyImpl is not null)
        {
            DacpGcHeapData dataLocal = default;
            int hrLocal = _legacyImpl.GetGCHeapData(&dataLocal);
            Debug.ValidateHResult(hr, hrLocal);
            if (hr == HResults.S_OK)
            {
                Debug.Assert(data->bServerMode == dataLocal.bServerMode, $"cDAC: {data->bServerMode}, DAC: {dataLocal.bServerMode}");
                Debug.Assert(data->bGcStructuresValid == dataLocal.bGcStructuresValid, $"cDAC: {data->bGcStructuresValid}, DAC: {dataLocal.bGcStructuresValid}");
                Debug.Assert(data->HeapCount == dataLocal.HeapCount, $"cDAC: {data->HeapCount}, DAC: {dataLocal.HeapCount}");
                Debug.Assert(data->g_max_generation == dataLocal.g_max_generation, $"cDAC: {data->g_max_generation}, DAC: {dataLocal.g_max_generation}");
            }
        }
#endif

        return hr;
    }
    int ISOSDacInterface.GetGCHeapList(uint count, [In, MarshalUsing(CountElementName = "count"), Out] ClrDataAddress[] heaps, uint* pNeeded)
    {
        int hr = HResults.S_OK;

        try
        {
            IGC gc = _target.Contracts.GC;
            string[] heapType = gc.GetGCIdentifiers();
            if (!heapType.Contains(GCIdentifiers.Server))
            {
                // If GC type is not server, this API is not supported
                hr = HResults.E_FAIL;
            }
            else
            {
                uint heapCount = gc.GetGCHeapCount();
                if (pNeeded is not null)
                {
                    *pNeeded = heapCount;
                }

                if (heaps.Length == heapCount)
                {
                    List<TargetPointer> gcHeaps = gc.GetGCHeaps().ToList();
                    Debug.Assert(gcHeaps.Count == heapCount, "Expected the number of GC heaps to match the count returned by GetGCHeapCount");
                    for (uint i = 0; i < heapCount; i++)
                    {
                        heaps[i] = gcHeaps[(int)i].ToClrDataAddress(_target);
                    }
                }
                else if (heaps.Length != 0)
                {
                    hr = HResults.E_INVALIDARG;
                }
            }
        }
        catch (System.Exception ex)
        {
            hr = ex.HResult;
        }

#if DEBUG
        if (_legacyImpl is not null)
        {
            ClrDataAddress[] heapsLocal = new ClrDataAddress[count];
            uint neededLocal;
            int hrLocal = _legacyImpl.GetGCHeapList(count, heapsLocal, &neededLocal);
            Debug.ValidateHResult(hr, hrLocal);
            if (hr == HResults.S_OK)
            {
                Debug.Assert(pNeeded == null || *pNeeded == neededLocal);
                // in theory, these don't need to be in the same order, but for consistency it is
                // easiest for consumers and verification if the DAC and cDAC return the same order
                for (int i = 0; i < neededLocal; i++)
                {
                    Debug.Assert(heaps[i] == heapsLocal[i], $"cDAC: {heaps[i]:x}, DAC: {heapsLocal[i]:x}");
                }
            }
        }
#endif
        return hr;
    }
    int ISOSDacInterface.GetGCHeapDetails(ClrDataAddress heap, DacpGcHeapDetails* details)
    {
        int hr = HResults.S_OK;

        try
        {
            if (heap == 0 || details == null)
                throw new ArgumentException();

            IGC gc = _target.Contracts.GC;
            string[] gcIdentifiers = gc.GetGCIdentifiers();

            // doesn't make sense to call this on WKS mode
            if (!gcIdentifiers.Contains(GCIdentifiers.Server))
                throw new ArgumentException();

            GCHeapData heapData = gc.GetHeapData(heap.ToTargetPointer(_target));

            details->heapAddr = heap;

            gc.GetGCBounds(out TargetPointer minAddress, out TargetPointer maxAddress);
            details->lowest_address = minAddress.ToClrDataAddress(_target);
            details->highest_address = maxAddress.ToClrDataAddress(_target);

            if (gcIdentifiers.Contains(GCIdentifiers.Background))
            {
                details->current_c_gc_state = gc.GetCurrentGCState();
                details->mark_array = heapData.MarkArray.ToClrDataAddress(_target);
                details->next_sweep_obj = heapData.NextSweepObject.ToClrDataAddress(_target);
                details->background_saved_lowest_address = heapData.BackGroundSavedMinAddress.ToClrDataAddress(_target);
                details->background_saved_highest_address = heapData.BackGroundSavedMaxAddress.ToClrDataAddress(_target);
            }
            else
            {
                details->current_c_gc_state = 0;
                details->mark_array = unchecked((ulong)-1);
                details->next_sweep_obj = 0;
                details->background_saved_lowest_address = 0;
                details->background_saved_highest_address = 0;
            }

            // now get information specific to this heap (server mode gives us several heaps; we're getting
            // information about only one of them.
            details->alloc_allocated = heapData.AllocAllocated.ToClrDataAddress(_target);
            details->ephemeral_heap_segment = heapData.EphemeralHeapSegment.ToClrDataAddress(_target);
            details->card_table = heapData.CardTable.ToClrDataAddress(_target);

            if (gcIdentifiers.Contains(GCIdentifiers.Regions))
            {
                // with regions, we don't have these variables anymore
                // use special value -1 in saved_sweep_ephemeral_seg to signal the region case
                details->saved_sweep_ephemeral_seg = unchecked((ulong)-1);
                details->saved_sweep_ephemeral_start = 0;
            }
            else
            {
                if (gcIdentifiers.Contains(GCIdentifiers.Background))
                {
                    details->saved_sweep_ephemeral_seg = heapData.SavedSweepEphemeralSegment.ToClrDataAddress(_target);
                    details->saved_sweep_ephemeral_start = heapData.SavedSweepEphemeralStart.ToClrDataAddress(_target);
                }
                else
                {
                    details->saved_sweep_ephemeral_seg = 0;
                    details->saved_sweep_ephemeral_start = 0;
                }
            }

            // get bounds for the different generations
            for (int i = 0; i < GCConstants.DAC_NUMBERGENERATIONS && i < heapData.GenerationTable.Count; i++)
            {
                GCGenerationData genData = heapData.GenerationTable[i];
                details->generation_table[i].start_segment = genData.StartSegment.ToClrDataAddress(_target);
                details->generation_table[i].allocation_start = genData.AllocationStart.ToClrDataAddress(_target);
                details->generation_table[i].allocContextPtr = genData.AllocationContextPointer.ToClrDataAddress(_target);
                details->generation_table[i].allocContextLimit = genData.AllocationContextLimit.ToClrDataAddress(_target);
            }

            for (int i = 0; i < GCConstants.DAC_NUMBERGENERATIONS + 3 && i < heapData.FillPointers.Count; i++)
            {
                details->finalization_fill_pointers[i] = heapData.FillPointers[i].ToClrDataAddress(_target);
            }
        }
        catch (System.Exception ex)
        {
            hr = ex.HResult;
        }

#if DEBUG
        if (_legacyImpl is not null)
        {
            DacpGcHeapDetails detailsLocal = default;
            int hrLocal = _legacyImpl.GetGCHeapDetails(heap, &detailsLocal);
            Debug.ValidateHResult(hr, hrLocal);
            if (hr == HResults.S_OK)
            {
                Debug.Assert(details->heapAddr == detailsLocal.heapAddr, $"cDAC: {details->heapAddr:x}, DAC: {detailsLocal.heapAddr:x}");
                Debug.Assert(details->alloc_allocated == detailsLocal.alloc_allocated, $"cDAC: {details->alloc_allocated:x}, DAC: {detailsLocal.alloc_allocated:x}");
                Debug.Assert(details->mark_array == detailsLocal.mark_array, $"cDAC: {details->mark_array:x}, DAC: {detailsLocal.mark_array:x}");
                Debug.Assert(details->current_c_gc_state == detailsLocal.current_c_gc_state, $"cDAC: {details->current_c_gc_state:x}, DAC: {detailsLocal.current_c_gc_state:x}");
                Debug.Assert(details->next_sweep_obj == detailsLocal.next_sweep_obj, $"cDAC: {details->next_sweep_obj:x}, DAC: {detailsLocal.next_sweep_obj:x}");
                Debug.Assert(details->saved_sweep_ephemeral_seg == detailsLocal.saved_sweep_ephemeral_seg, $"cDAC: {details->saved_sweep_ephemeral_seg:x}, DAC: {detailsLocal.saved_sweep_ephemeral_seg:x}");
                Debug.Assert(details->saved_sweep_ephemeral_start == detailsLocal.saved_sweep_ephemeral_start, $"cDAC: {details->saved_sweep_ephemeral_start:x}, DAC: {detailsLocal.saved_sweep_ephemeral_start:x}");
                Debug.Assert(details->background_saved_lowest_address == detailsLocal.background_saved_lowest_address, $"cDAC: {details->background_saved_lowest_address:x}, DAC: {detailsLocal.background_saved_lowest_address:x}");
                Debug.Assert(details->background_saved_highest_address == detailsLocal.background_saved_highest_address, $"cDAC: {details->background_saved_highest_address:x}, DAC: {detailsLocal.background_saved_highest_address:x}");

                // Verify generation table data
                for (int i = 0; i < GCConstants.DAC_NUMBERGENERATIONS; i++)
                {
                    Debug.Assert(details->generation_table[i].start_segment == detailsLocal.generation_table[i].start_segment, $"cDAC gen[{i}].start_segment: {details->generation_table[i].start_segment:x}, DAC: {detailsLocal.generation_table[i].start_segment:x}");
                    Debug.Assert(details->generation_table[i].allocation_start == detailsLocal.generation_table[i].allocation_start, $"cDAC gen[{i}].allocation_start: {details->generation_table[i].allocation_start:x}, DAC: {detailsLocal.generation_table[i].allocation_start:x}");
                    Debug.Assert(details->generation_table[i].allocContextPtr == detailsLocal.generation_table[i].allocContextPtr, $"cDAC gen[{i}].allocContextPtr: {details->generation_table[i].allocContextPtr:x}, DAC: {detailsLocal.generation_table[i].allocContextPtr:x}");
                    Debug.Assert(details->generation_table[i].allocContextLimit == detailsLocal.generation_table[i].allocContextLimit, $"cDAC gen[{i}].allocContextLimit: {details->generation_table[i].allocContextLimit:x}, DAC: {detailsLocal.generation_table[i].allocContextLimit:x}");
                }

                Debug.Assert(details->ephemeral_heap_segment == detailsLocal.ephemeral_heap_segment, $"cDAC: {details->ephemeral_heap_segment:x}, DAC: {detailsLocal.ephemeral_heap_segment:x}");

                // Verify finalization fill pointers
                for (int i = 0; i < GCConstants.DAC_NUMBERGENERATIONS + 3; i++)
                {
                    Debug.Assert(details->finalization_fill_pointers[i] == detailsLocal.finalization_fill_pointers[i], $"cDAC finalization_fill_pointers[{i}]: {details->finalization_fill_pointers[i]:x}, DAC: {detailsLocal.finalization_fill_pointers[i]:x}");
                }

                Debug.Assert(details->lowest_address == detailsLocal.lowest_address, $"cDAC: {details->lowest_address:x}, DAC: {detailsLocal.lowest_address:x}");
                Debug.Assert(details->highest_address == detailsLocal.highest_address, $"cDAC: {details->highest_address:x}, DAC: {detailsLocal.highest_address:x}");
                Debug.Assert(details->card_table == detailsLocal.card_table, $"cDAC: {details->card_table:x}, DAC: {detailsLocal.card_table:x}");
            }
        }
#endif

        return hr;
    }

    int ISOSDacInterface.GetGCHeapStaticData(DacpGcHeapDetails* details)
    {
        int hr = HResults.S_OK;

        try
        {
            if (details == null)
                throw new ArgumentException();

            IGC gc = _target.Contracts.GC;
            string[] gcIdentifiers = gc.GetGCIdentifiers();

            // doesn't make sense to call this on SVR mode
            if (!gcIdentifiers.Contains(GCIdentifiers.Workstation))
                throw new ArgumentException();

            GCHeapData heapData = gc.GetHeapData();

            details->heapAddr = 0;

            gc.GetGCBounds(out TargetPointer minAddress, out TargetPointer maxAddress);
            details->lowest_address = minAddress.ToClrDataAddress(_target);
            details->highest_address = maxAddress.ToClrDataAddress(_target);

            if (gcIdentifiers.Contains(GCIdentifiers.Background))
            {
                details->current_c_gc_state = gc.GetCurrentGCState();
                details->mark_array = heapData.MarkArray.ToClrDataAddress(_target);
                details->next_sweep_obj = heapData.NextSweepObject.ToClrDataAddress(_target);
                details->background_saved_lowest_address = heapData.BackGroundSavedMinAddress.ToClrDataAddress(_target);
                details->background_saved_highest_address = heapData.BackGroundSavedMaxAddress.ToClrDataAddress(_target);
            }
            else
            {
                details->current_c_gc_state = 0;
                details->mark_array = unchecked((ulong)-1);
                details->next_sweep_obj = 0;
                details->background_saved_lowest_address = 0;
                details->background_saved_highest_address = 0;
            }

            // now get information specific to this heap (server mode gives us several heaps; we're getting
            // information about only one of them.
            details->alloc_allocated = heapData.AllocAllocated.ToClrDataAddress(_target);
            details->ephemeral_heap_segment = heapData.EphemeralHeapSegment.ToClrDataAddress(_target);
            details->card_table = heapData.CardTable.ToClrDataAddress(_target);

            if (gcIdentifiers.Contains(GCIdentifiers.Regions))
            {
                // with regions, we don't have these variables anymore
                // use special value -1 in saved_sweep_ephemeral_seg to signal the region case
                details->saved_sweep_ephemeral_seg = unchecked((ulong)-1);
                details->saved_sweep_ephemeral_start = 0;
            }
            else
            {
                if (gcIdentifiers.Contains(GCIdentifiers.Background))
                {
                    details->saved_sweep_ephemeral_seg = heapData.SavedSweepEphemeralSegment.ToClrDataAddress(_target);
                    details->saved_sweep_ephemeral_start = heapData.SavedSweepEphemeralStart.ToClrDataAddress(_target);
                }
                else
                {
                    details->saved_sweep_ephemeral_seg = 0;
                    details->saved_sweep_ephemeral_start = 0;
                }
            }

            // get bounds for the different generations
            for (int i = 0; i < GCConstants.DAC_NUMBERGENERATIONS && i < heapData.GenerationTable.Count; i++)
            {
                GCGenerationData genData = heapData.GenerationTable[i];
                details->generation_table[i].start_segment = genData.StartSegment.ToClrDataAddress(_target);
                details->generation_table[i].allocation_start = genData.AllocationStart.ToClrDataAddress(_target);
                details->generation_table[i].allocContextPtr = genData.AllocationContextPointer.ToClrDataAddress(_target);
                details->generation_table[i].allocContextLimit = genData.AllocationContextLimit.ToClrDataAddress(_target);
            }

            for (int i = 0; i < GCConstants.DAC_NUMBERGENERATIONS + 3 && i < heapData.FillPointers.Count; i++)
            {
                details->finalization_fill_pointers[i] = heapData.FillPointers[i].ToClrDataAddress(_target);
            }
        }
        catch (System.Exception ex)
        {
            hr = ex.HResult;
        }

#if DEBUG
        if (_legacyImpl is not null)
        {
            DacpGcHeapDetails detailsLocal = default;
            int hrLocal = _legacyImpl.GetGCHeapStaticData(&detailsLocal);
            Debug.ValidateHResult(hr, hrLocal);
            if (hr == HResults.S_OK)
            {
                Debug.Assert(details->heapAddr == detailsLocal.heapAddr, $"cDAC: {details->heapAddr:x}, DAC: {detailsLocal.heapAddr:x}");
                Debug.Assert(details->alloc_allocated == detailsLocal.alloc_allocated, $"cDAC: {details->alloc_allocated:x}, DAC: {detailsLocal.alloc_allocated:x}");
                Debug.Assert(details->mark_array == detailsLocal.mark_array, $"cDAC: {details->mark_array:x}, DAC: {detailsLocal.mark_array:x}");
                Debug.Assert(details->current_c_gc_state == detailsLocal.current_c_gc_state, $"cDAC: {details->current_c_gc_state:x}, DAC: {detailsLocal.current_c_gc_state:x}");
                Debug.Assert(details->next_sweep_obj == detailsLocal.next_sweep_obj, $"cDAC: {details->next_sweep_obj:x}, DAC: {detailsLocal.next_sweep_obj:x}");
                Debug.Assert(details->saved_sweep_ephemeral_seg == detailsLocal.saved_sweep_ephemeral_seg, $"cDAC: {details->saved_sweep_ephemeral_seg:x}, DAC: {detailsLocal.saved_sweep_ephemeral_seg:x}");
                Debug.Assert(details->saved_sweep_ephemeral_start == detailsLocal.saved_sweep_ephemeral_start, $"cDAC: {details->saved_sweep_ephemeral_start:x}, DAC: {detailsLocal.saved_sweep_ephemeral_start:x}");
                Debug.Assert(details->background_saved_lowest_address == detailsLocal.background_saved_lowest_address, $"cDAC: {details->background_saved_lowest_address:x}, DAC: {detailsLocal.background_saved_lowest_address:x}");
                Debug.Assert(details->background_saved_highest_address == detailsLocal.background_saved_highest_address, $"cDAC: {details->background_saved_highest_address:x}, DAC: {detailsLocal.background_saved_highest_address:x}");

                // Verify generation table data
                for (int i = 0; i < GCConstants.DAC_NUMBERGENERATIONS; i++)
                {
                    Debug.Assert(details->generation_table[i].start_segment == detailsLocal.generation_table[i].start_segment, $"cDAC gen[{i}].start_segment: {details->generation_table[i].start_segment:x}, DAC: {detailsLocal.generation_table[i].start_segment:x}");
                    Debug.Assert(details->generation_table[i].allocation_start == detailsLocal.generation_table[i].allocation_start, $"cDAC gen[{i}].allocation_start: {details->generation_table[i].allocation_start:x}, DAC: {detailsLocal.generation_table[i].allocation_start:x}");
                    Debug.Assert(details->generation_table[i].allocContextPtr == detailsLocal.generation_table[i].allocContextPtr, $"cDAC gen[{i}].allocContextPtr: {details->generation_table[i].allocContextPtr:x}, DAC: {detailsLocal.generation_table[i].allocContextPtr:x}");
                    Debug.Assert(details->generation_table[i].allocContextLimit == detailsLocal.generation_table[i].allocContextLimit, $"cDAC gen[{i}].allocContextLimit: {details->generation_table[i].allocContextLimit:x}, DAC: {detailsLocal.generation_table[i].allocContextLimit:x}");
                }

                Debug.Assert(details->ephemeral_heap_segment == detailsLocal.ephemeral_heap_segment, $"cDAC: {details->ephemeral_heap_segment:x}, DAC: {detailsLocal.ephemeral_heap_segment:x}");

                // Verify finalization fill pointers
                for (int i = 0; i < GCConstants.DAC_NUMBERGENERATIONS + 3; i++)
                {
                    Debug.Assert(details->finalization_fill_pointers[i] == detailsLocal.finalization_fill_pointers[i], $"cDAC finalization_fill_pointers[{i}]: {details->finalization_fill_pointers[i]:x}, DAC: {detailsLocal.finalization_fill_pointers[i]:x}");
                }

                Debug.Assert(details->lowest_address == detailsLocal.lowest_address, $"cDAC: {details->lowest_address:x}, DAC: {detailsLocal.lowest_address:x}");
                Debug.Assert(details->highest_address == detailsLocal.highest_address, $"cDAC: {details->highest_address:x}, DAC: {detailsLocal.highest_address:x}");
                Debug.Assert(details->card_table == detailsLocal.card_table, $"cDAC: {details->card_table:x}, DAC: {detailsLocal.card_table:x}");
            }
        }
#endif

        return hr;
    }

    [GeneratedComClass]
    internal sealed unsafe partial class SOSHandleEnum : ISOSHandleEnum
    {
        private readonly Target _target;
        private readonly SOSHandleData[] _handles;
        private readonly ISOSHandleEnum? _legacyHandleEnum;
        private uint _index;

        public SOSHandleEnum(Target target, HandleType[] types, ISOSHandleEnum? legacyHandleEnum)
        {
            _target = target;
            _legacyHandleEnum = legacyHandleEnum;
            _handles = GetHandles(types);
        }

        private SOSHandleData[] GetHandles(HandleType[] types)
        {
            IGC gc = _target.Contracts.GC;
            List<HandleData> handles = gc.GetHandles(types);

            TargetPointer appDomainPointer = _target.ReadGlobalPointer(Constants.Globals.AppDomain);
            TargetPointer appDomain = _target.ReadPointer(appDomainPointer);
            ClrDataAddress appDomainClrAddress = appDomain.ToClrDataAddress(_target);

            SOSHandleData[] sosHandles = new SOSHandleData[handles.Count];
            for (int i = 0; i < handles.Count; i++)
            {
                HandleData h = handles[i];
                sosHandles[i] = new SOSHandleData
                {
                    AppDomain = appDomainClrAddress,
                    Handle = h.Handle.ToClrDataAddress(_target),
                    Secondary = h.Secondary.ToClrDataAddress(_target),
                    Type = h.Type,
                    StrongReference = h.StrongReference ? 1 : 0,
                    RefCount = h.RefCount,
                    JupiterRefCount = h.JupiterRefCount,
                    IsPegged = h.IsPegged ? 1 : 0,
                };
            }

            return sosHandles;
        }

        int ISOSHandleEnum.Next(uint count, SOSHandleData[] handles, uint* pNeeded)
        {
            int hr = HResults.S_OK;
            try
            {
                if (pNeeded is null || handles is null)
                    throw new NullReferenceException();

                uint written = 0;
                while (written < count && _index < _handles.Length)
                    handles[written++] = _handles[(int)_index++];

                *pNeeded = written;
                hr = _index < _handles.Length ? HResults.S_FALSE : HResults.S_OK;
            }
            catch (System.Exception ex)
            {
                hr = ex.HResult;
            }
#if DEBUG
            if (_legacyHandleEnum is not null)
            {
                SOSHandleData[] handlesLocal = new SOSHandleData[count];
                uint neededLocal;
                int hrLocal = _legacyHandleEnum.Next(count, handlesLocal, &neededLocal);
                Debug.ValidateHResult(hr, hrLocal);
                if (hr == HResults.S_OK || hr == HResults.S_FALSE)
                {
                    Debug.Assert(*pNeeded == neededLocal, $"cDAC: {*pNeeded}, DAC: {neededLocal}");
                    for (int i = 0; i < neededLocal; i++)
                    {
                        Debug.Assert(handles[i].AppDomain == handlesLocal[i].AppDomain, $"cDAC: {handles[i].AppDomain:x}, DAC: {handlesLocal[i].AppDomain:x}");
                        Debug.Assert(handles[i].Handle == handlesLocal[i].Handle, $"cDAC: {handles[i].Handle:x}, DAC: {handlesLocal[i].Handle:x}");
                        Debug.Assert(handles[i].Secondary == handlesLocal[i].Secondary, $"cDAC: {handles[i].Secondary:x}, DAC: {handlesLocal[i].Secondary:x}");
                        Debug.Assert(handles[i].Type == handlesLocal[i].Type, $"cDAC: {handles[i].Type}, DAC: {handlesLocal[i].Type}");
                        Debug.Assert(handles[i].StrongReference == handlesLocal[i].StrongReference, $"cDAC: {handles[i].StrongReference}, DAC: {handlesLocal[i].StrongReference}");
                        Debug.Assert(handles[i].RefCount == handlesLocal[i].RefCount, $"cDAC: {handles[i].RefCount}, DAC: {handlesLocal[i].RefCount}");
                        Debug.Assert(handles[i].JupiterRefCount == handlesLocal[i].JupiterRefCount, $"cDAC: {handles[i].JupiterRefCount}, DAC: {handlesLocal[i].JupiterRefCount}");
                        Debug.Assert(handles[i].IsPegged == handlesLocal[i].IsPegged, $"cDAC: {handles[i].IsPegged}, DAC: {handlesLocal[i].IsPegged}");
                    }
                }
            }
#endif
            return hr;
        }

        int ISOSEnum.Skip(uint count)
        {
            _index += count;
#if DEBUG
            _legacyHandleEnum?.Skip(count);
#endif
            return HResults.S_OK;
        }
        int ISOSEnum.Reset()
        {
            _index = 0;
#if DEBUG
            _legacyHandleEnum?.Reset();
#endif
            return HResults.S_OK;
        }
        int ISOSEnum.GetCount(uint* pCount)
        {
            if (pCount is null) return HResults.E_POINTER;
            *pCount = (uint)_handles.Length;
#if DEBUG
            if (_legacyHandleEnum is not null)
            {
                uint countLocal;
                _legacyHandleEnum.GetCount(&countLocal);
                Debug.Assert(countLocal == (uint)_handles.Length);
            }
#endif
            return HResults.S_OK;
        }
    }

    [GeneratedComClass]
    internal sealed unsafe partial class SOSMemoryEnum : ISOSMemoryEnum
    {
        private readonly SOSMemoryRegion[] _regions;
        private readonly ISOSMemoryEnum? _legacyMemoryEnum;
        private uint _index;

        public SOSMemoryEnum(Target target, IReadOnlyList<GCMemoryRegionData> regions, ISOSMemoryEnum? legacyMemoryEnum = null)
        {
            _legacyMemoryEnum = legacyMemoryEnum;
            _regions = new SOSMemoryRegion[regions.Count];
            for (int i = 0; i < regions.Count; i++)
            {
                _regions[i] = new SOSMemoryRegion
                {
                    Start = regions[i].Start.ToClrDataAddress(target),
                    Size = (ClrDataAddress)regions[i].Size,
                    ExtraData = (ClrDataAddress)regions[i].ExtraData,
                    Heap = regions[i].Heap,
                };
            }
        }

        int ISOSMemoryEnum.Next(uint count, SOSMemoryRegion[] memRegions, uint* pNeeded)
        {
            int hr = HResults.S_OK;
            try
            {
                if (pNeeded is null || memRegions is null)
                    throw new NullReferenceException();

                uint written = 0;
                while (written < count && _index < _regions.Length)
                    memRegions[written++] = _regions[(int)_index++];

                *pNeeded = written;
                hr = written < count ? HResults.S_FALSE : HResults.S_OK;
            }
            catch (System.Exception ex)
            {
                hr = ex.HResult;
            }
#if DEBUG
            if (_legacyMemoryEnum is not null)
            {
                SOSMemoryRegion[] regionsLocal = new SOSMemoryRegion[count];
                uint neededLocal;
                int hrLocal = _legacyMemoryEnum.Next(count, regionsLocal, &neededLocal);
                Debug.ValidateHResult(hr, hrLocal);
                if (hr == HResults.S_OK || hr == HResults.S_FALSE)
                {
                    Debug.Assert(*pNeeded == neededLocal, $"cDAC: {*pNeeded}, DAC: {neededLocal}");
                    for (int i = 0; i < neededLocal; i++)
                    {
                        Debug.Assert(memRegions[i].Start == regionsLocal[i].Start, $"cDAC: {memRegions[i].Start:x}, DAC: {regionsLocal[i].Start:x}");
                        Debug.Assert(memRegions[i].Size == regionsLocal[i].Size, $"cDAC: {memRegions[i].Size:x}, DAC: {regionsLocal[i].Size:x}");
                        Debug.Assert(memRegions[i].ExtraData == regionsLocal[i].ExtraData, $"cDAC: {memRegions[i].ExtraData:x}, DAC: {regionsLocal[i].ExtraData:x}");
                        Debug.Assert(memRegions[i].Heap == regionsLocal[i].Heap, $"cDAC: {memRegions[i].Heap}, DAC: {regionsLocal[i].Heap}");
                    }
                }
            }
#endif
            return hr;
        }

        int ISOSEnum.Skip(uint count)
        {
            _index += count;
#if DEBUG
            _legacyMemoryEnum?.Skip(count);
#endif
            return HResults.S_OK;
        }

        int ISOSEnum.Reset()
        {
            _index = 0;
#if DEBUG
            _legacyMemoryEnum?.Reset();
#endif
            return HResults.S_OK;
        }

        int ISOSEnum.GetCount(uint* pCount)
        {
            if (pCount is null)
                return HResults.E_POINTER;
            *pCount = (uint)_regions.Length;
#if DEBUG
            if (_legacyMemoryEnum is not null)
            {
                uint countLocal;
                _legacyMemoryEnum.GetCount(&countLocal);
                Debug.Assert(countLocal == (uint)_regions.Length);
            }
#endif
            return HResults.S_OK;
        }
    }

    int ISOSDacInterface.GetHandleEnum(DacComNullableByRef<ISOSHandleEnum> ppHandleEnum)
    {
        int hr = HResults.S_OK;
        try
        {
            IGC gc = _target.Contracts.GC;
            HandleType[] supportedHandleTypes = gc.GetSupportedHandleTypes();
            ISOSHandleEnum? legacyHandleEnum = null;
#if DEBUG
            if (_legacyImpl is not null)
            {
                DacComNullableByRef<ISOSHandleEnum> legacyOut = new(isNullRef: false);
                int hrLocal = _legacyImpl.GetHandleEnum(legacyOut);
                Debug.ValidateHResult(hr, hrLocal);
                legacyHandleEnum = legacyOut.Interface;
            }
#endif
            ppHandleEnum.Interface = new SOSHandleEnum(_target, supportedHandleTypes, legacyHandleEnum);
        }
        catch (System.Exception ex)
        {
            hr = ex.HResult;
        }
        return hr;
    }
    int ISOSDacInterface.GetHandleEnumForGC(uint gen, DacComNullableByRef<ISOSHandleEnum> ppHandleEnum)
        => LegacyFallbackHelper.CanFallback() && _legacyImpl is not null ? _legacyImpl.GetHandleEnumForGC(gen, ppHandleEnum) : HResults.E_NOTIMPL;
    int ISOSDacInterface.GetHandleEnumForTypes([In, MarshalUsing(CountElementName = "count")] uint[] types, uint count, DacComNullableByRef<ISOSHandleEnum> ppHandleEnum)
    {
        int hr = HResults.S_OK;
        try
        {
            ISOSHandleEnum? legacyHandleEnum = null;
#if DEBUG
            if (_legacyImpl is not null)
            {
                DacComNullableByRef<ISOSHandleEnum> legacyOut = new(isNullRef: false);
                int hrLocal = _legacyImpl.GetHandleEnumForTypes(types, count, legacyOut);
                Debug.ValidateHResult(hr, hrLocal);
                legacyHandleEnum = legacyOut.Interface;
            }
#endif
            IGC gc = _target.Contracts.GC;
            HandleType[] handleTypes = gc.GetHandleTypes(types);
            ppHandleEnum.Interface = new SOSHandleEnum(_target, handleTypes, legacyHandleEnum);
        }
        catch (System.Exception ex)
        {
            hr = ex.HResult;
        }
        return hr;
    }
    int ISOSDacInterface.GetHeapAllocData(uint count, void* data, uint* pNeeded)
        => LegacyFallbackHelper.CanFallback() && _legacyImpl is not null ? _legacyImpl.GetHeapAllocData(count, data, pNeeded) : HResults.E_NOTIMPL;
    int ISOSDacInterface.GetHeapAnalyzeData(ClrDataAddress addr, DacpGcHeapAnalyzeData* data)
    {
        int hr = HResults.S_OK;
        try
        {
            if (addr == 0 || data == null)
                throw new ArgumentException();

            IGC gc = _target.Contracts.GC;
            string[] gcIdentifiers = gc.GetGCIdentifiers();

            // doesn't make sense to call this on WKS mode
            if (!gcIdentifiers.Contains(GCIdentifiers.Server))
                throw Marshal.GetExceptionForHR(HResults.E_FAIL)!;

            GCHeapData heapData = gc.GetHeapData(addr.ToTargetPointer(_target));

            data->heapAddr = addr;
            data->internal_root_array = heapData.InternalRootArray.ToClrDataAddress(_target);
            data->internal_root_array_index = heapData.InternalRootArrayIndex.Value;
            data->heap_analyze_success = heapData.HeapAnalyzeSuccess ? (int)Interop.BOOL.TRUE : (int)Interop.BOOL.FALSE;
        }
        catch (System.Exception ex)
        {
            hr = ex.HResult;
        }

#if DEBUG
        if (_legacyImpl is not null)
        {
            DacpGcHeapAnalyzeData dataLocal = default;
            int hrLocal = _legacyImpl.GetHeapAnalyzeData(addr, &dataLocal);
            Debug.ValidateHResult(hr, hrLocal);
            if (hr == HResults.S_OK)
            {
                Debug.Assert(data->heapAddr == dataLocal.heapAddr, $"cDAC: {data->heapAddr:x}, DAC: {dataLocal.heapAddr:x}");
                Debug.Assert(data->internal_root_array == dataLocal.internal_root_array, $"cDAC: {data->internal_root_array:x}, DAC: {dataLocal.internal_root_array:x}");
                Debug.Assert(data->internal_root_array_index == dataLocal.internal_root_array_index, $"cDAC: {data->internal_root_array_index}, DAC: {dataLocal.internal_root_array_index}");
                Debug.Assert(data->heap_analyze_success == dataLocal.heap_analyze_success, $"cDAC: {data->heap_analyze_success}, DAC: {dataLocal.heap_analyze_success}");
            }
        }
#endif

        return hr;
    }
    int ISOSDacInterface.GetHeapAnalyzeStaticData(DacpGcHeapAnalyzeData* data)
    {
        int hr = HResults.S_OK;
        try
        {
            if (data == null)
                throw new ArgumentException();

            IGC gc = _target.Contracts.GC;
            string[] gcIdentifiers = gc.GetGCIdentifiers();

            // doesn't make sense to call this on SVR mode
            if (!gcIdentifiers.Contains(GCIdentifiers.Workstation))
                throw Marshal.GetExceptionForHR(HResults.E_FAIL)!;

            // For workstation GC, use GetHeapData()
            GCHeapData heapData = gc.GetHeapData();

            data->heapAddr = 0; // Not applicable for static data
            data->internal_root_array = heapData.InternalRootArray.ToClrDataAddress(_target);
            data->internal_root_array_index = heapData.InternalRootArrayIndex.Value;
            data->heap_analyze_success = heapData.HeapAnalyzeSuccess ? (int)Interop.BOOL.TRUE : (int)Interop.BOOL.FALSE;
        }
        catch (System.Exception ex)
        {
            hr = ex.HResult;
        }

#if DEBUG
        if (_legacyImpl is not null)
        {
            DacpGcHeapAnalyzeData dataLocal = default;
            int hrLocal = _legacyImpl.GetHeapAnalyzeStaticData(&dataLocal);
            Debug.ValidateHResult(hr, hrLocal);
            if (hr == HResults.S_OK)
            {
                Debug.Assert(data->heapAddr == dataLocal.heapAddr, $"cDAC: {data->heapAddr:x}, DAC: {dataLocal.heapAddr:x}");
                Debug.Assert(data->internal_root_array == dataLocal.internal_root_array, $"cDAC: {data->internal_root_array:x}, DAC: {dataLocal.internal_root_array:x}");
                Debug.Assert(data->internal_root_array_index == dataLocal.internal_root_array_index, $"cDAC: {data->internal_root_array_index}, DAC: {dataLocal.internal_root_array_index}");
                Debug.Assert(data->heap_analyze_success == dataLocal.heap_analyze_success, $"cDAC: {data->heap_analyze_success}, DAC: {dataLocal.heap_analyze_success}");
            }
        }
#endif

        return hr;
    }
    int ISOSDacInterface.GetHeapSegmentData(ClrDataAddress seg, DacpHeapSegmentData* data)
    {
        int hr = HResults.S_OK;
        try
        {
            if (seg == 0 || data == null)
                throw new ArgumentException();

            IGC gc = _target.Contracts.GC;
            string[] gcIdentifiers = gc.GetGCIdentifiers();
            GCHeapSegmentData segmentData = gc.GetHeapSegmentData(seg.ToTargetPointer(_target));

            data->segmentAddr = seg;
            data->allocated = segmentData.Allocated.ToClrDataAddress(_target);
            data->committed = segmentData.Committed.ToClrDataAddress(_target);
            data->reserved = segmentData.Reserved.ToClrDataAddress(_target);
            data->used = segmentData.Used.ToClrDataAddress(_target);
            data->mem = segmentData.Mem.ToClrDataAddress(_target);
            data->next = segmentData.Next.ToClrDataAddress(_target);
            data->gc_heap = segmentData.Heap.ToClrDataAddress(_target);
            data->flags = (nuint)segmentData.Flags.Value;
            data->background_allocated = segmentData.BackgroundAllocated.ToClrDataAddress(_target);

            // TODO: Compute highAllocMark - need to determine if this is the ephemeral segment
            // and get the allocation mark from the appropriate heap data
            // For now, use allocated as a fallback (similar to non-ephemeral segments in legacy code)
            data->highAllocMark = data->allocated;

            GCHeapData heapData = gcIdentifiers.Contains(GCIdentifiers.Server) ? gc.GetHeapData(segmentData.Heap) : gc.GetHeapData();
            if (seg.ToTargetPointer(_target) == heapData.EphemeralHeapSegment)
            {
                data->highAllocMark = heapData.AllocAllocated.ToClrDataAddress(_target);
            }
            else
            {
                data->highAllocMark = data->allocated;
            }
        }
        catch (System.Exception ex)
        {
            hr = ex.HResult;
        }

#if DEBUG
        if (_legacyImpl is not null)
        {
            DacpHeapSegmentData dataLocal = default;
            int hrLocal = _legacyImpl.GetHeapSegmentData(seg, &dataLocal);
            Debug.ValidateHResult(hr, hrLocal);
            if (hr == HResults.S_OK)
            {
                Debug.Assert(data->segmentAddr == dataLocal.segmentAddr, $"segmentAddr - cDAC: {data->segmentAddr:x}, DAC: {dataLocal.segmentAddr:x}");
                Debug.Assert(data->allocated == dataLocal.allocated, $"allocated - cDAC: {data->allocated:x}, DAC: {dataLocal.allocated:x}");
                Debug.Assert(data->committed == dataLocal.committed, $"committed - cDAC: {data->committed:x}, DAC: {dataLocal.committed:x}");
                Debug.Assert(data->reserved == dataLocal.reserved, $"reserved - cDAC: {data->reserved:x}, DAC: {dataLocal.reserved:x}");
                Debug.Assert(data->used == dataLocal.used, $"used - cDAC: {data->used:x}, DAC: {dataLocal.used:x}");
                Debug.Assert(data->mem == dataLocal.mem, $"mem - cDAC: {data->mem:x}, DAC: {dataLocal.mem:x}");
                Debug.Assert(data->next == dataLocal.next, $"next - cDAC: {data->next:x}, DAC: {dataLocal.next:x}");
                Debug.Assert(data->gc_heap == dataLocal.gc_heap, $"gc_heap - cDAC: {data->gc_heap:x}, DAC: {dataLocal.gc_heap:x}");
                Debug.Assert(data->highAllocMark == dataLocal.highAllocMark, $"highAllocMark - cDAC: {data->highAllocMark:x}, DAC: {dataLocal.highAllocMark:x}");
                Debug.Assert(data->flags == dataLocal.flags, $"flags - cDAC: {data->flags:x}, DAC: {dataLocal.flags:x}");
                Debug.Assert(data->background_allocated == dataLocal.background_allocated, $"background_allocated - cDAC: {data->background_allocated:x}, DAC: {dataLocal.background_allocated:x}");
            }
        }
#endif

        return hr;
    }

    int ISOSDacInterface.GetHillClimbingLogEntry(ClrDataAddress addr, void* data)
    {
        // This API is not implemented by the legacy DAC
        int hr = HResults.E_NOTIMPL;

#if DEBUG
        if (_legacyImpl is not null)
        {
            int hrLocal = _legacyImpl.GetHillClimbingLogEntry(addr, data);
            Debug.ValidateHResult(hr, hrLocal);
        }
#endif

        return hr;
    }
    int ISOSDacInterface.GetILForModule(ClrDataAddress moduleAddr, int rva, ClrDataAddress* il)
    {
        int hr = HResults.S_OK;
        try
        {
            if (moduleAddr == 0 || il == null)
                throw new ArgumentException();

            Contracts.ILoader loader = _target.Contracts.Loader;
            TargetPointer module = moduleAddr.ToTargetPointer(_target);
            Contracts.ModuleHandle moduleHandle = loader.GetModuleHandleFromModulePtr(module);
            TargetPointer peAssemblyPtr = loader.GetPEAssembly(moduleHandle);
            *il = loader.GetILAddr(peAssemblyPtr, rva).ToClrDataAddress(_target);
        }
        catch (System.Exception ex)
        {
            hr = ex.HResult;
        }
#if DEBUG
        if (_legacyImpl is not null)
        {
            ClrDataAddress ilLocal;
            int hrLocal = _legacyImpl.GetILForModule(moduleAddr, rva, &ilLocal);
            Debug.ValidateHResult(hr, hrLocal);
            if (hr == HResults.S_OK)
            {
                Debug.Assert(*il == ilLocal, $"cDAC: {*il:x}, DAC: {ilLocal:x}");
            }
        }
#endif
        return hr;
    }
    int ISOSDacInterface.GetJitHelperFunctionName(ClrDataAddress ip, uint count, byte* name, uint* pNeeded)
    {
        int hr = HResults.S_OK;
        try
        {
            if (!_target.Contracts.AuxiliarySymbols.TryGetAuxiliarySymbolName(ip.ToTargetPointer(_target), out string? symbolName))
                throw new ArgumentException();

            uint needed = 0;
            OutputBufferHelpers.CopyUtf8StringToBuffer(name, count, &needed, symbolName);
            if (needed > count && name != null)
                throw Marshal.GetExceptionForHR(HResults.E_FAIL)!;
            if (pNeeded != null)
                *pNeeded = needed;
        }
        catch (System.Exception ex)
        {
            hr = ex.HResult;
        }
#if DEBUG
        if (_legacyImpl is not null)
        {
            byte[]? nameLocal = name != null && count > 0 ? new byte[count] : null;
            uint neededLocal;
            int hrLocal;
            fixed (byte* ptr = nameLocal)
            {
                hrLocal = _legacyImpl.GetJitHelperFunctionName(ip, count, ptr, &neededLocal);
            }
            Debug.ValidateHResult(hr, hrLocal);
            if (hr == HResults.S_OK)
            {
                Debug.Assert(pNeeded == null || *pNeeded == neededLocal);
                Debug.Assert(name == null || new ReadOnlySpan<byte>(name, (int)neededLocal).SequenceEqual(nameLocal!.AsSpan(0, (int)neededLocal)));
            }
        }
#endif

        return hr;
    }
    int ISOSDacInterface.GetJitManagerList(uint count, DacpJitManagerInfo* managers, uint* pNeeded)
    {
        int hr = HResults.S_OK;
        try
        {
            if (managers is not null)
            {
                if (count >= 1)
                {
                    *managers = default;
                    Contracts.JitManagerInfo jitManagerInfo = _target.Contracts.ExecutionManager.GetEEJitManagerInfo();
                    managers->managerAddr = jitManagerInfo.ManagerAddress.ToClrDataAddress(_target);
                    managers->codeType = jitManagerInfo.CodeType;
                    managers->ptrHeapList = jitManagerInfo.HeapListAddress.ToClrDataAddress(_target);
                }
            }
            else if (pNeeded is not null)
            {
                *pNeeded = 1;
            }
        }
        catch (System.Exception ex)
        {
            hr = ex.HResult;
        }
#if DEBUG
        if (_legacyImpl is not null)
        {
            if (managers is not null)
            {
                DacpJitManagerInfo managerLocal = default;
                int hrLocal = _legacyImpl.GetJitManagerList(count, &managerLocal, null);
                Debug.ValidateHResult(hr, hrLocal);
                if (hr == HResults.S_OK && count >= 1)
                {
                    Debug.Assert(managers->managerAddr == managerLocal.managerAddr);
                    Debug.Assert(managers->codeType == managerLocal.codeType);
                    Debug.Assert(managers->ptrHeapList == managerLocal.ptrHeapList);
                }
            }
            else
            {
                uint neededLocal;
                int hrLocal = _legacyImpl.GetJitManagerList(0, null, &neededLocal);
                Debug.ValidateHResult(hr, hrLocal);
                if (hr == HResults.S_OK && pNeeded is not null)
                {
                    Debug.Assert(*pNeeded == neededLocal);
                }
            }
        }
#endif
        return hr;
    }

    private bool IsJumpRel64(TargetPointer pThunk)
        => 0x48 == _target.Read<byte>(pThunk) &&
           0xB8 == _target.Read<byte>(pThunk + 1) &&
           0xFF == _target.Read<byte>(pThunk + 10) &&
           0xE0 == _target.Read<byte>(pThunk + 11);

    private TargetPointer DecodeJump64(TargetPointer pThunk)
    {
        Debug.Assert(IsJumpRel64(pThunk), "Expected a jump thunk");

        return _target.ReadPointer(pThunk + 2);
    }

    int ISOSDacInterface.GetJumpThunkTarget(void* ctx, ClrDataAddress* targetIP, ClrDataAddress* targetMD)
    {
        int hr = HResults.S_OK;
        try
        {
            if (ctx == null || targetIP == null || targetMD == null)
                throw new ArgumentException();
            // API is implemented for x64 only
            if (_target.Contracts.RuntimeInfo.GetTargetArchitecture() != RuntimeInfoArchitecture.X64)
                throw Marshal.GetExceptionForHR(HResults.E_FAIL)!;

            IPlatformAgnosticContext context = IPlatformAgnosticContext.GetContextForPlatform(_target);

            // Context is not stored in the target, but in our own process
            context.FillFromBuffer(new Span<byte>(ctx, (int)context.Size));
            TargetPointer pThunk = context.InstructionPointer;

            if (IsJumpRel64(pThunk))
            {
                *targetMD = 0;
                *targetIP = DecodeJump64(pThunk).ToClrDataAddress(_target);
            }
            else
            {
                hr = HResults.E_FAIL;
            }

        }
        catch (System.Exception ex)
        {
            hr = ex.HResult;
        }

#if DEBUG
        if (_legacyImpl is not null)
        {
            ClrDataAddress targetIPLocal;
            ClrDataAddress targetMDLocal;
            int hrLocal = _legacyImpl.GetJumpThunkTarget(ctx, &targetIPLocal, &targetMDLocal);
            Debug.ValidateHResult(hr, hrLocal);
            if (hr == HResults.S_OK)
            {
                Debug.Assert(*targetIP == targetIPLocal, $"cDAC: {*targetIP:x}, DAC: {targetIPLocal:x}");
                Debug.Assert(*targetMD == targetMDLocal, $"cDAC: {*targetMD:x}, DAC: {targetMDLocal:x}");
            }
        }
#endif

        return hr;
    }
    int ISOSDacInterface.GetMethodDescData(ClrDataAddress addr, ClrDataAddress ip, DacpMethodDescData* data, uint cRevertedRejitVersions, DacpReJitData* rgRevertedRejitData, uint* pcNeededRevertedRejitData)
    {
        int hr = HResults.S_OK;
        try
        {
            if (addr == 0)
                throw new ArgumentException();
            if (cRevertedRejitVersions != 0 && rgRevertedRejitData == null)
                throw new ArgumentException();
            if (rgRevertedRejitData != null && pcNeededRevertedRejitData == null)
                // If you're asking for reverted rejit data, you'd better ask for the number of
                // elements we return
                throw new ArgumentException();
            Contracts.IRuntimeTypeSystem rtsContract = _target.Contracts.RuntimeTypeSystem;

            TargetPointer methodDesc = addr.ToTargetPointer(_target);
            Contracts.MethodDescHandle methodDescHandle = rtsContract.GetMethodDescHandle(methodDesc);
            Contracts.ICodeVersions nativeCodeContract = _target.Contracts.CodeVersions;
            Contracts.IReJIT rejitContract = _target.Contracts.ReJIT;

            if (rgRevertedRejitData != null)
            {
                NativeMemory.Clear(rgRevertedRejitData, (nuint)(sizeof(DacpReJitData) * cRevertedRejitVersions));
            }
            if (pcNeededRevertedRejitData != null)
            {
                *pcNeededRevertedRejitData = 0;
            }

            NativeCodeVersionHandle requestedNativeCodeVersion;
            NativeCodeVersionHandle? activeNativeCodeVersion = null;
            if (ip != 0)
            {
                requestedNativeCodeVersion = nativeCodeContract.GetNativeCodeVersionForIP(ip.ToTargetCodePointer(_target));
            }
            else
            {
                requestedNativeCodeVersion = nativeCodeContract.GetActiveNativeCodeVersion(new TargetPointer(methodDesc));
                activeNativeCodeVersion = requestedNativeCodeVersion;
            }

            data->requestedIP = ip;
            data->bIsDynamic = rtsContract.IsDynamicMethod(methodDescHandle) ? 1 : 0;
            data->wSlotNumber = rtsContract.GetSlotNumber(methodDescHandle);
            TargetCodePointer nativeCodeAddr = TargetCodePointer.Null;
            if (requestedNativeCodeVersion.Valid)
            {
                nativeCodeAddr = nativeCodeContract.GetNativeCode(requestedNativeCodeVersion);
            }
            if (nativeCodeAddr != TargetCodePointer.Null)
            {
                data->bHasNativeCode = 1;
                data->NativeCodeAddr = _target.Contracts.PrecodeStubs.GetInterpreterCodeFromInterpreterPrecodeIfPresent(nativeCodeAddr).ToAddress(_target).ToClrDataAddress(_target);
            }
            else
            {
                data->bHasNativeCode = 0;
                data->NativeCodeAddr = 0xffffffff_fffffffful;
            }
            if (rtsContract.HasNativeCodeSlot(methodDescHandle))
            {
                data->AddressOfNativeCodeSlot = rtsContract.GetAddressOfNativeCodeSlot(methodDescHandle).ToClrDataAddress(_target);
            }
            else
            {
                data->AddressOfNativeCodeSlot = 0;
            }
            data->MDToken = rtsContract.GetMethodToken(methodDescHandle);
            data->MethodDescPtr = addr;
            TargetPointer methodTableAddr = rtsContract.GetMethodTable(methodDescHandle);
            data->MethodTablePtr = methodTableAddr.ToClrDataAddress(_target);
            TypeHandle typeHandle = rtsContract.GetTypeHandle(methodTableAddr);
            data->ModulePtr = rtsContract.GetModule(typeHandle).ToClrDataAddress(_target);

            // If rejit info is appropriate, get the following:
            //     * ReJitInfo for the current, active version of the method
            //     * ReJitInfo for the requested IP (for !ip2md and !u)
            //     * ReJitInfos for all reverted versions of the method (up to
            //         cRevertedRejitVersions)
            //
            // Minidumps will not have all this rejit info, and failure to get rejit info
            // should not be fatal.  So enclose all rejit stuff in a try.
            try
            {
                if (activeNativeCodeVersion is null || !activeNativeCodeVersion.Value.Valid)
                {
                    activeNativeCodeVersion = nativeCodeContract.GetActiveNativeCodeVersion(new TargetPointer(methodDesc));
                }

                if (activeNativeCodeVersion is null || !activeNativeCodeVersion.Value.Valid)
                {
                    throw new InvalidOperationException("No active native code version found");
                }

                // Active ReJitInfo
                CopyNativeCodeVersionToReJitData(
                    activeNativeCodeVersion.Value,
                    activeNativeCodeVersion.Value,
                    &data->rejitDataCurrent);

                // Requested ReJitInfo
                Debug.Assert(data->rejitDataRequested.rejitID == 0);
                if (ip != 0 && requestedNativeCodeVersion.Valid)
                {
                    CopyNativeCodeVersionToReJitData(
                        requestedNativeCodeVersion,
                        activeNativeCodeVersion.Value,
                        &data->rejitDataRequested);
                }

                // Total number of jitted rejit versions
                int cJittedRejitVersions = rejitContract.GetRejitIds(_target, methodDescHandle.Address).Count();
                data->cJittedRejitVersions = (uint)cJittedRejitVersions;

                // Reverted ReJitInfos
                if (rgRevertedRejitData == null)
                {
                    // No reverted rejit versions will be returned, but maybe caller wants a
                    // count of all versions
                    if (pcNeededRevertedRejitData != null)
                    {
                        *pcNeededRevertedRejitData = data->cJittedRejitVersions;
                    }
                }
                else
                {
                    // Caller wants some reverted rejit versions.  Gather reverted rejit version data to return

                    // Returns all available rejitids, including the rejitid for the one non-reverted
                    // current version.
                    List<TargetNUInt> reJitIds = rejitContract.GetRejitIds(_target, methodDescHandle.Address).ToList();

                    // Go through rejitids.  For each reverted one, populate a entry in rgRevertedRejitData
                    uint iRejitDataReverted = 0;
                    ILCodeVersionHandle activeVersion = nativeCodeContract.GetActiveILCodeVersion(methodDesc);
                    TargetNUInt activeVersionId = rejitContract.GetRejitId(activeVersion);
                    for (int i = 0; (i < reJitIds.Count) && (iRejitDataReverted < cRevertedRejitVersions); i++)
                    {
                        ILCodeVersionHandle ilCodeVersion = nativeCodeContract.GetILCodeVersions(methodDesc)
                            .FirstOrDefault(ilcode => rejitContract.GetRejitId(ilcode) == reJitIds[i],
                                ILCodeVersionHandle.Invalid);

                        if (!ilCodeVersion.IsValid || rejitContract.GetRejitId(ilCodeVersion) == activeVersionId)
                        {
                            continue;
                        }

                        NativeCodeVersionHandle activeRejitChild = nativeCodeContract.GetActiveNativeCodeVersionForILCodeVersion(methodDesc, ilCodeVersion);
                        CopyNativeCodeVersionToReJitData(
                            activeRejitChild,
                            activeNativeCodeVersion.Value,
                            &rgRevertedRejitData[iRejitDataReverted]);

                        iRejitDataReverted++;
                    }
                    // We already checked that pcNeededRevertedRejitData != NULL because rgRevertedRejitData != NULL
                    *pcNeededRevertedRejitData = iRejitDataReverted;
                }
            }
            catch (global::System.Exception)
            {
                if (pcNeededRevertedRejitData != null)
                {
                    *pcNeededRevertedRejitData = 0;
                }
            }

            // HAVE_GCCOVER
            if (requestedNativeCodeVersion.Valid)
            {
                // TargetPointer.Null if GCCover information is not available.
                // In certain minidumps, we won't save the GCCover information.
                // (it would be unwise to do so, it is heavy and not a customer scenario).
                data->GCStressCodeCopy = nativeCodeContract.GetGCStressCodeCopy(requestedNativeCodeVersion).ToClrDataAddress(_target);
            }

            // Unlike the legacy implementation, the cDAC does not currently populate
            // data->managedDynamicMethodObject. This field is unused in both SOS and CLRMD
            // and would require accessing CorLib bound managed fields which the cDAC does not
            // currently support. However, it must remain in the return type for compatibility.
            data->managedDynamicMethodObject = 0;
        }
        catch (global::System.Exception ex)
        {
            hr = ex.HResult;
        }

#if DEBUG
        if (_legacyImpl is not null)
        {
            DacpMethodDescData dataLocal = default;
            DacpReJitData[]? rgRevertedRejitDataLocal = null;
            if (rgRevertedRejitData != null)
            {
                rgRevertedRejitDataLocal = new DacpReJitData[cRevertedRejitVersions];
            }
            uint cNeededRevertedRejitDataLocal = 0;
            uint* pcNeededRevertedRejitDataLocal = null;
            if (pcNeededRevertedRejitData != null)
            {
                pcNeededRevertedRejitDataLocal = &cNeededRevertedRejitDataLocal;
            }
            int hrLocal;
            fixed (DacpReJitData* rgRevertedRejitDataLocalPtr = rgRevertedRejitDataLocal)
            {
                hrLocal = _legacyImpl.GetMethodDescData(addr, ip, &dataLocal, cRevertedRejitVersions, rgRevertedRejitDataLocalPtr, pcNeededRevertedRejitDataLocal);
            }
            Debug.ValidateHResult(hr, hrLocal);
            if (hr == HResults.S_OK)
            {
                Debug.Assert(data->bHasNativeCode == dataLocal.bHasNativeCode, $"cDAC: {data->bHasNativeCode}, DAC: {dataLocal.bHasNativeCode}");
                Debug.Assert(data->bIsDynamic == dataLocal.bIsDynamic, $"cDAC: {data->bIsDynamic}, DAC: {dataLocal.bIsDynamic}");
                Debug.Assert(data->wSlotNumber == dataLocal.wSlotNumber, $"cDAC: {data->wSlotNumber}, DAC: {dataLocal.wSlotNumber}");
                Debug.Assert(data->NativeCodeAddr == dataLocal.NativeCodeAddr, $"cDAC: {data->NativeCodeAddr:x}, DAC: {dataLocal.NativeCodeAddr:x}");
                Debug.Assert(data->AddressOfNativeCodeSlot == dataLocal.AddressOfNativeCodeSlot, $"cDAC: {data->AddressOfNativeCodeSlot:x}, DAC: {dataLocal.AddressOfNativeCodeSlot:x}");
                Debug.Assert(data->MethodDescPtr == dataLocal.MethodDescPtr, $"cDAC: {data->MethodDescPtr:x}, DAC: {dataLocal.MethodDescPtr:x}");
                Debug.Assert(data->MethodTablePtr == dataLocal.MethodTablePtr, $"cDAC: {data->MethodTablePtr:x}, DAC: {dataLocal.MethodTablePtr:x}");
                Debug.Assert(data->ModulePtr == dataLocal.ModulePtr, $"cDAC: {data->ModulePtr:x}, DAC: {dataLocal.ModulePtr:x}");
                Debug.Assert(data->MDToken == dataLocal.MDToken, $"cDAC: {data->MDToken:x}, DAC: {dataLocal.MDToken:x}");
                Debug.Assert(data->GCInfo == dataLocal.GCInfo, $"cDAC: {data->GCInfo:x}, DAC: {dataLocal.GCInfo:x}");
                Debug.Assert(data->GCStressCodeCopy == dataLocal.GCStressCodeCopy, $"cDAC: {data->GCStressCodeCopy:x}, DAC: {dataLocal.GCStressCodeCopy:x}");
                // managedDynamicMethodObject is not currently populated by the cDAC API and may differ from legacyImpl.
                Debug.Assert(data->managedDynamicMethodObject == 0);
                Debug.Assert(data->requestedIP == dataLocal.requestedIP, $"cDAC: {data->requestedIP:x}, DAC: {dataLocal.requestedIP:x}");
                Debug.Assert(data->cJittedRejitVersions == dataLocal.cJittedRejitVersions, $"cDAC: {data->cJittedRejitVersions}, DAC: {dataLocal.cJittedRejitVersions}");

                // rejitDataCurrent
                Debug.Assert(data->rejitDataCurrent.rejitID == dataLocal.rejitDataCurrent.rejitID, $"cDAC: {data->rejitDataCurrent.rejitID}, DAC: {dataLocal.rejitDataCurrent.rejitID}");
                Debug.Assert(data->rejitDataCurrent.NativeCodeAddr == dataLocal.rejitDataCurrent.NativeCodeAddr, $"cDAC: {data->rejitDataCurrent.NativeCodeAddr:x}, DAC: {dataLocal.rejitDataCurrent.NativeCodeAddr:x}");
                Debug.Assert(data->rejitDataCurrent.flags == dataLocal.rejitDataCurrent.flags, $"cDAC: {data->rejitDataCurrent.flags}, DAC: {dataLocal.rejitDataCurrent.flags}");

                // rejitDataRequested
                Debug.Assert(data->rejitDataRequested.rejitID == dataLocal.rejitDataRequested.rejitID, $"cDAC: {data->rejitDataRequested.rejitID}, DAC: {dataLocal.rejitDataRequested.rejitID}");
                Debug.Assert(data->rejitDataRequested.NativeCodeAddr == dataLocal.rejitDataRequested.NativeCodeAddr, $"cDAC: {data->rejitDataRequested.NativeCodeAddr:x}, DAC: {dataLocal.rejitDataRequested.NativeCodeAddr:x}");
                Debug.Assert(data->rejitDataRequested.flags == dataLocal.rejitDataRequested.flags, $"cDAC: {data->rejitDataRequested.flags}, DAC: {dataLocal.rejitDataRequested.flags}");

                // rgRevertedRejitData
                if (rgRevertedRejitData != null && rgRevertedRejitDataLocal != null)
                {
                    Debug.Assert(cNeededRevertedRejitDataLocal == *pcNeededRevertedRejitData, $"cDAC: {*pcNeededRevertedRejitData}, DAC: {cNeededRevertedRejitDataLocal}");
                    for (ulong i = 0; i < cNeededRevertedRejitDataLocal; i++)
                    {
                        Debug.Assert(rgRevertedRejitData[i].rejitID == rgRevertedRejitDataLocal[i].rejitID, $"cDAC: {rgRevertedRejitData[i].rejitID}, DAC: {rgRevertedRejitDataLocal[i].rejitID}");
                        Debug.Assert(rgRevertedRejitData[i].NativeCodeAddr == rgRevertedRejitDataLocal[i].NativeCodeAddr, $"cDAC: {rgRevertedRejitData[i].NativeCodeAddr:x}, DAC: {rgRevertedRejitDataLocal[i].NativeCodeAddr:x}");
                        Debug.Assert(rgRevertedRejitData[i].flags == rgRevertedRejitDataLocal[i].flags, $"cDAC: {rgRevertedRejitData[i].flags}, DAC: {rgRevertedRejitDataLocal[i].flags}");
                    }
                }
            }
        }
#endif
        return hr;
    }

    private void CopyNativeCodeVersionToReJitData(
        NativeCodeVersionHandle nativeCodeVersion,
        NativeCodeVersionHandle activeNativeCodeVersion,
        DacpReJitData* pReJitData)
    {
        ICodeVersions cv = _target.Contracts.CodeVersions;
        IReJIT rejit = _target.Contracts.ReJIT;

        ILCodeVersionHandle ilCodeVersion = cv.GetILCodeVersion(nativeCodeVersion);

        pReJitData->rejitID = rejit.GetRejitId(ilCodeVersion).Value;
        TargetCodePointer nativeCode = cv.GetNativeCode(nativeCodeVersion);
        pReJitData->NativeCodeAddr = _target.Contracts.PrecodeStubs.GetInterpreterCodeFromInterpreterPrecodeIfPresent(nativeCode).Value;

        if (nativeCodeVersion.CodeVersionNodeAddress != activeNativeCodeVersion.CodeVersionNodeAddress ||
            nativeCodeVersion.MethodDescAddress != activeNativeCodeVersion.MethodDescAddress)
        {
            pReJitData->flags = DacpReJitData.Flags.kReverted;
        }
        else
        {
            DacpReJitData.Flags flags = DacpReJitData.Flags.kUnknown;
            switch (rejit.GetRejitState(ilCodeVersion))
            {
                // kStateRequested
                case RejitState.Requested:
                    flags = DacpReJitData.Flags.kRequested;
                    break;
                // kStateActive
                case RejitState.Active:
                    flags = DacpReJitData.Flags.kActive;
                    break;
                default:
                    Debug.Fail("Unknown RejitState. cDAC should be updated to understand this new state.");
                    break;
            }
            pReJitData->flags = flags;
        }
    }

    int ISOSDacInterface.GetMethodDescFromToken(ClrDataAddress moduleAddr, uint token, ClrDataAddress* methodDesc)
    {
        int hr = HResults.S_OK;
        try
        {
            if (moduleAddr == 0 || methodDesc == null)
                throw new ArgumentException();

            Contracts.ILoader loader = _target.Contracts.Loader;
            TargetPointer module = moduleAddr.ToTargetPointer(_target);
            Contracts.ModuleHandle moduleHandle = loader.GetModuleHandleFromModulePtr(module);
            Contracts.ModuleLookupTables lookupTables = loader.GetLookupTables(moduleHandle);
            switch ((EcmaMetadataUtils.TokenType)(token & EcmaMetadataUtils.TokenTypeMask))
            {
                case EcmaMetadataUtils.TokenType.mdtFieldDef:
                    *methodDesc = loader.GetModuleLookupMapElement(lookupTables.FieldDefToDesc, token, out var _).ToClrDataAddress(_target);
                    break;
                case EcmaMetadataUtils.TokenType.mdtMethodDef:
                    *methodDesc = loader.GetModuleLookupMapElement(lookupTables.MethodDefToDesc, token, out var _).ToClrDataAddress(_target);
                    break;
                case EcmaMetadataUtils.TokenType.mdtTypeDef:
                    *methodDesc = loader.GetModuleLookupMapElement(lookupTables.TypeDefToMethodTable, token, out var _).ToClrDataAddress(_target);
                    break;
                case EcmaMetadataUtils.TokenType.mdtTypeRef:
                    *methodDesc = loader.GetModuleLookupMapElement(lookupTables.TypeRefToMethodTable, token, out var _).ToClrDataAddress(_target);
                    break;
                default:
                    throw new ArgumentException();
            }
        }
        catch (System.Exception ex)
        {
            hr = ex.HResult;
        }
#if DEBUG
        if (_legacyImpl is not null)
        {
            ClrDataAddress methodDescLocal;
            int hrLocal = _legacyImpl.GetMethodDescFromToken(moduleAddr, token, &methodDescLocal);
            Debug.ValidateHResult(hr, hrLocal);
            if (hr == HResults.S_OK)
            {
                Debug.Assert(*methodDesc == methodDescLocal, $"cDAC: {*methodDesc:x}, DAC: {methodDescLocal:x}");
            }
        }
#endif
        return hr;
    }
    int ISOSDacInterface.GetMethodDescName(ClrDataAddress addr, uint count, char* name, uint* pNeeded)
    {
        int hr = HResults.S_OK;
        if (pNeeded != null)
            *pNeeded = 0;
        try
        {
            if (addr == 0)
                throw new ArgumentException();
            TargetPointer methodDesc = addr.ToTargetPointer(_target);
            StringBuilder stringBuilder = new StringBuilder();
            Contracts.IRuntimeTypeSystem rtsContract = _target.Contracts.RuntimeTypeSystem;
            Contracts.MethodDescHandle methodDescHandle = rtsContract.GetMethodDescHandle(methodDesc);
            try
            {
                TypeNameBuilder.AppendMethodInternal(_target, stringBuilder, methodDescHandle, TypeNameFormat.FormatSignature | TypeNameFormat.FormatNamespace | TypeNameFormat.FormatFullInst);
            }
            catch
            {
                hr = HResults.E_FAIL;
                if (rtsContract.IsNoMetadataMethod(methodDescHandle, out _))
                {
                    // In heap dumps, trying to format the signature can fail
                    // in certain cases.
                    stringBuilder.Clear();
                    TypeNameBuilder.AppendMethodInternal(_target, stringBuilder, methodDescHandle, TypeNameFormat.FormatNamespace | TypeNameFormat.FormatFullInst);
                    hr = HResults.S_OK;
                }
                else
                {
                    string? fallbackNameString = _target.Contracts.DacStreams.StringFromEEAddress(methodDesc);
                    if (!string.IsNullOrEmpty(fallbackNameString))
                    {
                        stringBuilder.Clear();
                        stringBuilder.Append(fallbackNameString);
                        hr = HResults.S_OK;
                    }
                    else
                    {
                        TargetPointer modulePtr = rtsContract.GetModule(rtsContract.GetTypeHandle(rtsContract.GetMethodTable(methodDescHandle)));
                        Contracts.ModuleHandle module = _target.Contracts.Loader.GetModuleHandleFromModulePtr(modulePtr);
                        string modulePath = _target.Contracts.Loader.GetPath(module);
                        ReadOnlySpan<char> moduleSpan = modulePath.AsSpan();
                        char directorySeparator = (char)_target.ReadGlobal<byte>(Constants.Globals.DirectorySeparator);

                        int pathNameSpanIndex = moduleSpan.LastIndexOf(directorySeparator);
                        if (pathNameSpanIndex != -1)
                        {
                            moduleSpan = moduleSpan.Slice(pathNameSpanIndex + 1);
                        }
                        stringBuilder.Clear();
                        stringBuilder.Append(moduleSpan);
                        stringBuilder.Append("!Unknown");
                        hr = HResults.S_OK;
                    }
                }
            }

            if (hr == HResults.S_OK)
            {
                OutputBufferHelpers.CopyStringToBuffer(name, count, pNeeded, stringBuilder.ToString());
            }
        }
        catch (System.Exception ex)
        {
            hr = ex.HResult;
        }

#if DEBUG
        if (_legacyImpl is not null)
        {
            char[] nameLocal = new char[count];
            uint neededLocal;
            int hrLocal;
            fixed (char* ptr = nameLocal)
            {
                hrLocal = _legacyImpl.GetMethodDescName(addr, count, ptr, &neededLocal);
            }
            Debug.ValidateHResult(hr, hrLocal);
            if (hr == HResults.S_OK)
            {
                Debug.Assert(pNeeded == null || *pNeeded == neededLocal);
                Debug.Assert(name == null || new ReadOnlySpan<char>(nameLocal, 0, (int)neededLocal - 1).SequenceEqual(new string(name)), $"cDAC: {new string(name)}, DAC: {new string(nameLocal, 0, (int)neededLocal - 1)}");
            }
        }
#endif
        return hr;
    }

    int ISOSDacInterface.GetMethodDescPtrFromFrame(ClrDataAddress frameAddr, ClrDataAddress* ppMD)
    {
        int hr = HResults.S_OK;
        try
        {
            if (frameAddr == 0 || ppMD is null)
                throw new ArgumentException();

            Contracts.IStackWalk stackWalkContract = _target.Contracts.StackWalk;
            TargetPointer methodDescPtr = stackWalkContract.GetMethodDescPtr(frameAddr.ToTargetPointer(_target));
            if (methodDescPtr == TargetPointer.Null)
                throw new ArgumentException();

            _target.Contracts.RuntimeTypeSystem.GetMethodDescHandle(methodDescPtr); // validation
            *ppMD = methodDescPtr.ToClrDataAddress(_target);
        }
        catch (System.Exception ex)
        {
            hr = ex.HResult;
        }
#if DEBUG
        if (_legacyImpl is not null)
        {
            ClrDataAddress ppMDLocal;
            int hrLocal = _legacyImpl.GetMethodDescPtrFromFrame(frameAddr, &ppMDLocal);

            Debug.ValidateHResult(hr, hrLocal);
            if (hr == HResults.S_OK)
            {
                Debug.Assert(*ppMD == ppMDLocal);
            }
        }
#endif
        return hr;
    }
    int ISOSDacInterface.GetMethodDescPtrFromIP(ClrDataAddress ip, ClrDataAddress* ppMD)
    {
        int hr = HResults.E_NOTIMPL;

        try
        {
            if (ip == 0 || ppMD == null)
                throw new ArgumentException();
            IExecutionManager executionManager = _target.Contracts.ExecutionManager;
            IRuntimeTypeSystem rts = _target.Contracts.RuntimeTypeSystem;

            CodeBlockHandle? handle = executionManager.GetCodeBlockHandle(ip.ToTargetCodePointer(_target));
            if (handle is not CodeBlockHandle codeHandle)
                throw Marshal.GetExceptionForHR(HResults.E_FAIL)!;

            TargetPointer methodDescAddr = executionManager.GetMethodDesc(codeHandle);

            try
            {
                // Runs validation of MethodDesc
                // if validation fails, should return E_INVALIDARG
                rts.GetMethodDescHandle(methodDescAddr);

                *ppMD = methodDescAddr.ToClrDataAddress(_target);
                hr = HResults.S_OK;
            }
            catch (System.Exception)
            {
                hr = HResults.E_INVALIDARG;
            }
        }
        catch (System.Exception ex)
        {
            hr = ex.HResult;
        }

#if DEBUG
        if (_legacyImpl is not null)
        {
            ClrDataAddress ppMDLocal;
            int hrLocal = _legacyImpl.GetMethodDescPtrFromIP(ip, &ppMDLocal);

            Debug.ValidateHResult(hr, hrLocal);
            if (hr == HResults.S_OK)
            {
                Debug.Assert(*ppMD == ppMDLocal);
            }
        }
#endif
        return hr;
    }

    int ISOSDacInterface.GetMethodDescTransparencyData(ClrDataAddress methodDesc, DacpMethodDescTransparencyData* data)
    {
        int hr = HResults.S_OK;
        try
        {
            if (methodDesc == 0 || data is null)
                throw new ArgumentException();

            // Called for validation
            _target.Contracts.RuntimeTypeSystem.GetMethodDescHandle(methodDesc.ToTargetPointer(_target));

            // Zero memory
            *data = default;
        }
        catch (System.Exception ex)
        {
            hr = ex.HResult;
        }

        return hr;
    }

    int ISOSDacInterface.GetMethodTableData(ClrDataAddress mt, DacpMethodTableData* data)
    {
        int hr = HResults.S_OK;
        try
        {
            if (mt == 0 || data == null)
                throw new ArgumentException();
            Contracts.IRuntimeTypeSystem contract = _target.Contracts.RuntimeTypeSystem;
            Contracts.TypeHandle methodTable = contract.GetTypeHandle(mt.ToTargetPointer(_target));

            DacpMethodTableData result = default;
            result.baseSize = contract.GetBaseSize(methodTable);
            // [compat] SOS DAC APIs added this base size adjustment for strings
            // due to: "2008/09/25 Title: New implementation of StringBuilder and improvements in String class"
            // which changed StringBuilder not to use a String as an internal buffer and in the process
            // changed the String internals so that StringObject::GetBaseSize() now includes the nul terminator character,
            // which is apparently not expected by SOS.
            if (contract.IsString(methodTable))
                result.baseSize -= sizeof(char);

            result.componentSize = contract.GetComponentSize(methodTable);
            bool isFreeObjectMT = contract.IsFreeObjectMethodTable(methodTable);
            result.bIsFree = isFreeObjectMT ? 1 : 0;
            if (!isFreeObjectMT)
            {
                result.module = contract.GetModule(methodTable).ToClrDataAddress(_target);
                // Note: really the canonical method table, not the EEClass, which we don't expose
                result.klass = contract.GetCanonicalMethodTable(methodTable).ToClrDataAddress(_target);
                result.parentMethodTable = contract.GetParentMethodTable(methodTable).ToClrDataAddress(_target);
                result.wNumInterfaces = contract.GetNumInterfaces(methodTable);
                result.wNumMethods = contract.GetNumMethods(methodTable);
                result.wNumVtableSlots = 0; // always return 0 since .NET 9
                result.wNumVirtuals = 0; // always return 0 since .NET 9
                result.cl = contract.GetTypeDefToken(methodTable);
                result.dwAttrClass = contract.GetTypeDefTypeAttributes(methodTable);
                result.bContainsGCPointers = contract.ContainsGCPointers(methodTable) ? 1 : 0;
                result.bIsShared = 0;
                result.bIsDynamic = contract.IsDynamicStatics(methodTable) ? 1 : 0;
            }
            *data = result;
        }
        catch (System.Exception ex)
        {
            hr = ex.HResult;
        }

#if DEBUG
        if (_legacyImpl is not null)
        {
            DacpMethodTableData dataLocal;
            int hrLocal = _legacyImpl.GetMethodTableData(mt, &dataLocal);
            Debug.ValidateHResult(hr, hrLocal);
            if (hr == HResults.S_OK)
            {
                Debug.Assert(data->module == dataLocal.module);
                Debug.Assert(data->klass == dataLocal.klass);
                Debug.Assert(data->parentMethodTable == dataLocal.parentMethodTable);
                Debug.Assert(data->wNumInterfaces == dataLocal.wNumInterfaces);
                Debug.Assert(data->wNumMethods == dataLocal.wNumMethods);
                Debug.Assert(data->wNumVtableSlots == dataLocal.wNumVtableSlots);
                Debug.Assert(data->wNumVirtuals == dataLocal.wNumVirtuals);
                Debug.Assert(data->cl == dataLocal.cl);
                Debug.Assert(data->dwAttrClass == dataLocal.dwAttrClass);
                Debug.Assert(data->bContainsGCPointers == dataLocal.bContainsGCPointers);
                Debug.Assert(data->bIsShared == dataLocal.bIsShared);
                Debug.Assert(data->bIsDynamic == dataLocal.bIsDynamic);
            }
        }
#endif
        return hr;
    }
    int ISOSDacInterface.GetMethodTableFieldData(ClrDataAddress mt, DacpMethodTableFieldData* data)
    {
        int hr = HResults.S_OK;
        try
        {
            if (mt == 0 || data == null)
                throw new ArgumentException();

            TargetPointer mtAddress = mt.ToTargetPointer(_target);
            Contracts.IRuntimeTypeSystem rtsContract = _target.Contracts.RuntimeTypeSystem;
            TypeHandle typeHandle = rtsContract.GetTypeHandle(mtAddress);
            data->FirstField = rtsContract.GetFieldDescList(typeHandle).ToClrDataAddress(_target);
            data->wNumInstanceFields = rtsContract.GetNumInstanceFields(typeHandle);
            data->wNumStaticFields = rtsContract.GetNumStaticFields(typeHandle);
            data->wNumThreadStaticFields = rtsContract.GetNumThreadStaticFields(typeHandle);
            data->wContextStaticsSize = 0;
            data->wContextStaticOffset = 0;
        }
        catch (System.Exception ex)
        {
            hr = ex.HResult;
        }
#if DEBUG
        {
            if (_legacyImpl is not null)
            {
                DacpMethodTableFieldData mtFieldDataLocal = default;
                int hrLocal = _legacyImpl.GetMethodTableFieldData(mt, &mtFieldDataLocal);
                Debug.ValidateHResult(hr, hrLocal);
                if (hr == HResults.S_OK)
                {
                    Debug.Assert(data->wNumInstanceFields == mtFieldDataLocal.wNumInstanceFields);
                    Debug.Assert(data->wNumStaticFields == mtFieldDataLocal.wNumStaticFields);
                    Debug.Assert(data->wNumThreadStaticFields == mtFieldDataLocal.wNumThreadStaticFields);
                    Debug.Assert(data->wContextStaticOffset == mtFieldDataLocal.wContextStaticOffset);
                    Debug.Assert(data->wContextStaticsSize == mtFieldDataLocal.wContextStaticsSize);
                }
            }
        }
#endif
        return hr;
    }
    int ISOSDacInterface.GetMethodTableForEEClass(ClrDataAddress eeClassReallyCanonMT, ClrDataAddress* value)
    {
        int hr = HResults.S_OK;
        try
        {
            if (eeClassReallyCanonMT == 0 || value == null)
                throw new ArgumentException();
            Contracts.IRuntimeTypeSystem contract = _target.Contracts.RuntimeTypeSystem;
            Contracts.TypeHandle methodTableHandle = contract.GetTypeHandle(eeClassReallyCanonMT.ToTargetPointer(_target));
            *value = methodTableHandle.Address.ToClrDataAddress(_target);
        }
        catch (global::System.Exception ex)
        {
            hr = ex.HResult;
        }

#if DEBUG
        if (_legacyImpl is not null)
        {
            ClrDataAddress valueLocal;
            int hrLocal = _legacyImpl.GetMethodTableForEEClass(eeClassReallyCanonMT, &valueLocal);
            Debug.ValidateHResult(hr, hrLocal);
            if (hr == HResults.S_OK)
                Debug.Assert(*value == valueLocal);
        }
#endif
        return hr;
    }

    int ISOSDacInterface.GetMethodTableName(ClrDataAddress mt, uint count, char* mtName, uint* pNeeded)
    {
        int hr = HResults.S_OK;
        try
        {
            if (mt == 0)
                throw new ArgumentException();
            Contracts.IRuntimeTypeSystem typeSystemContract = _target.Contracts.RuntimeTypeSystem;
            Contracts.ILoader loader = _target.Contracts.Loader;
            Contracts.TypeHandle methodTableHandle = typeSystemContract.GetTypeHandle(mt.ToTargetPointer(_target, overrideCheck: true));
            if (typeSystemContract.IsFreeObjectMethodTable(methodTableHandle))
            {
                OutputBufferHelpers.CopyStringToBuffer(mtName, count, pNeeded, "Free");
            }
            else
            {
                TargetPointer modulePointer = typeSystemContract.GetModule(methodTableHandle);
                Contracts.ModuleHandle moduleHandle = loader.GetModuleHandleFromModulePtr(modulePointer);
                if (!loader.TryGetLoadedImageContents(moduleHandle, out _, out _, out _))
                {
                    OutputBufferHelpers.CopyStringToBuffer(mtName, count, pNeeded, "<Unloaded Type>");
                }
                else
                {
                    System.Text.StringBuilder methodTableName = new();
                    try
                    {
                        TypeNameBuilder.AppendType(_target, methodTableName, methodTableHandle, TypeNameFormat.FormatNamespace | TypeNameFormat.FormatFullInst);
                    }
                    catch
                    {
                        string? fallbackName = _target.Contracts.DacStreams.StringFromEEAddress(mt.ToTargetPointer(_target));
                        if (fallbackName != null)
                        {
                            methodTableName.Clear();
                            methodTableName.Append(fallbackName);
                        }
                    }
                    OutputBufferHelpers.CopyStringToBuffer(mtName, count, pNeeded, methodTableName.ToString());
                }
            }

        }
        catch (System.Exception ex)
        {
            hr = ex.HResult;
        }

#if DEBUG
        if (_legacyImpl is not null)
        {
            char[] mtNameLocal = new char[count];
            uint neededLocal;
            int hrLocal;
            fixed (char* ptr = mtNameLocal)
            {
                hrLocal = _legacyImpl.GetMethodTableName(mt, count, ptr, &neededLocal);
            }
            Debug.ValidateHResult(hr, hrLocal);
            if (hr == HResults.S_OK)
            {
                Debug.Assert(pNeeded == null || *pNeeded == neededLocal);
                Debug.Assert(mtName == null || new ReadOnlySpan<char>(mtNameLocal, 0, (int)neededLocal - 1).SequenceEqual(new string(mtName)));
            }
        }
#endif
        return hr;
    }

    int ISOSDacInterface.GetMethodTableSlot(ClrDataAddress mt, uint slot, ClrDataAddress* value)
    {
        int hr = HResults.S_OK;

        IRuntimeTypeSystem rts = _target.Contracts.RuntimeTypeSystem;

        try
        {
            if (mt == 0 || value == null)
                throw new ArgumentException();

            TargetPointer methodTable = mt.ToTargetPointer(_target);
            TypeHandle methodTableHandle = rts.GetTypeHandle(methodTable); // validate MT

            ushort vtableSlots = rts.GetNumVtableSlots(methodTableHandle);

            if (slot < vtableSlots)
            {
                *value = rts.GetSlot(methodTableHandle, slot).ToClrDataAddress(_target);
                if (*value == 0)
                {
                    hr = HResults.S_FALSE;
                }
            }
            else
            {
                hr = HResults.E_INVALIDARG;
                foreach (TargetPointer mdAddr in rts.GetIntroducedMethodDescs(methodTableHandle))
                {
                    MethodDescHandle mdHandle = rts.GetMethodDescHandle(mdAddr);
                    if (rts.GetSlotNumber(mdHandle) == slot)
                    {
                        *value = rts.GetMethodEntryPointIfExists(mdHandle).ToClrDataAddress(_target);
                        if (*value == 0)
                        {
                            hr = HResults.S_FALSE;
                        }
                        else
                        {
                            hr = HResults.S_OK;
                        }
                        break;
                    }
                }
            }
        }
        catch (System.Exception ex)
        {
            hr = ex.HResult;
        }

#if DEBUG
        if (_legacyImpl is not null)
        {
            int hrLocal;
            ClrDataAddress valueLocal;

            hrLocal = _legacyImpl.GetMethodTableSlot(mt, slot, &valueLocal);

            Debug.ValidateHResult(hr, hrLocal);
            if (hr == HResults.S_OK || hr == HResults.S_FALSE)
            {
                Debug.Assert(*value == valueLocal, $"cDAC: {*value:x}, DAC: {valueLocal:x}");
            }
        }
#endif

        return hr;
    }

    int ISOSDacInterface.GetMethodTableTransparencyData(ClrDataAddress mt, DacpMethodTableTransparencyData* data)
    {
        int hr = HResults.S_OK;
        try
        {
            if (mt == 0 || data is null)
                throw new ArgumentException();

            // Called for validation
            _target.Contracts.RuntimeTypeSystem.GetTypeHandle(mt.ToTargetPointer(_target));

            // Zero memory
            *data = default;
        }
        catch (System.Exception ex)
        {
            hr = ex.HResult;
        }

        return hr;
    }

    int ISOSDacInterface.GetModule(ClrDataAddress addr, DacComNullableByRef<IXCLRDataModule> mod)
    {
        IXCLRDataModule? legacyModule = null;
        if (_legacyImpl is not null)
        {
            DacComNullableByRef<IXCLRDataModule> legacyOut = new(isNullRef: false);
            int hr = _legacyImpl.GetModule(addr, legacyOut);
            if (hr < 0)
                return hr;
            legacyModule = legacyOut.Interface;
        }

        mod.Interface = new ClrDataModule(addr.ToTargetPointer(_target), _target, legacyModule);

        return HResults.S_OK;
    }

    int ISOSDacInterface.GetModuleData(ClrDataAddress moduleAddr, DacpModuleData* data)
    {
        int hr = HResults.S_OK;
        try
        {
            if (moduleAddr == 0 || data == null)
                throw new ArgumentException();
            Contracts.ILoader contract = _target.Contracts.Loader;
            Contracts.ModuleHandle handle = contract.GetModuleHandleFromModulePtr(moduleAddr.ToTargetPointer(_target));

            data->Address = moduleAddr;
            data->PEAssembly = moduleAddr; // Module address in .NET 9+ - correspondingly, SOS-DAC APIs for PE assemblies expect a module address
            data->Assembly = contract.GetAssembly(handle).ToClrDataAddress(_target);

            Contracts.ModuleFlags flags = contract.GetFlags(handle);
            bool isReflectionEmit = flags.HasFlag(Contracts.ModuleFlags.ReflectionEmit);
            data->isReflection = (uint)(isReflectionEmit ? 1 : 0);
            data->isPEFile = (uint)(isReflectionEmit ? 0 : 1);      // ReflectionEmit module means it is not a PE file
            data->dwTransientFlags = (uint)(flags & Contracts.ModuleFlags.EditAndContinue) != 0 ? (uint)DacpModuleData.TransientFlags.IsEditAndContinue : 0;

            data->ilBase = contract.GetILBase(handle).ToClrDataAddress(_target);

            try
            {
                TargetSpan readOnlyMetadata = _target.Contracts.EcmaMetadata.GetReadOnlyMetadataAddress(handle);
                data->metadataStart = readOnlyMetadata.Address.Value;
                data->metadataSize = readOnlyMetadata.Size;
            }
            catch (System.Exception)
            {
                // if we are unable to read the metadata, to match the DAC behavior
                // set metadataStart and metadataSize to 0
                data->metadataStart = 0;
                data->metadataSize = 0;
            }

            data->LoaderAllocator = contract.GetLoaderAllocator(handle).ToClrDataAddress(_target);

            Target.TypeInfo lookupMapTypeInfo = _target.GetTypeInfo(DataType.ModuleLookupMap);
            ulong tableDataOffset = (ulong)lookupMapTypeInfo.Fields[Constants.FieldNames.ModuleLookupMap.TableData].Offset;

            Contracts.ModuleLookupTables tables = contract.GetLookupTables(handle);
            data->FieldDefToDescMap = _target.ReadPointer(tables.FieldDefToDesc + tableDataOffset).ToClrDataAddress(_target);
            data->ManifestModuleReferencesMap = _target.ReadPointer(tables.ManifestModuleReferences + tableDataOffset).ToClrDataAddress(_target);
            data->MemberRefToDescMap = _target.ReadPointer(tables.MemberRefToDesc + tableDataOffset).ToClrDataAddress(_target);
            data->MethodDefToDescMap = _target.ReadPointer(tables.MethodDefToDesc + tableDataOffset).ToClrDataAddress(_target);
            data->TypeDefToMethodTableMap = _target.ReadPointer(tables.TypeDefToMethodTable + tableDataOffset).ToClrDataAddress(_target);
            data->TypeRefToMethodTableMap = _target.ReadPointer(tables.TypeRefToMethodTable + tableDataOffset).ToClrDataAddress(_target);

            // Always 0 - .NET no longer has these concepts
            data->dwModuleID = 0;
            data->dwBaseClassIndex = 0;
            data->dwModuleIndex = 0;
            data->ThunkHeap = 0;
        }
        catch (global::System.Exception e)
        {
            hr = e.HResult;
        }

#if DEBUG
        if (_legacyImpl is not null)
        {
            DacpModuleData dataLocal;
            int hrLocal = _legacyImpl.GetModuleData(moduleAddr, &dataLocal);
            Debug.ValidateHResult(hr, hrLocal);
            if (hr == HResults.S_OK)
            {
                Debug.Assert(data->Address == dataLocal.Address);
                Debug.Assert(data->PEAssembly == dataLocal.PEAssembly);
                Debug.Assert(data->Assembly == dataLocal.Assembly);
                Debug.Assert(data->isReflection == dataLocal.isReflection);
                Debug.Assert(data->isPEFile == dataLocal.isPEFile);
                Debug.Assert(data->dwTransientFlags == dataLocal.dwTransientFlags);
                Debug.Assert(data->ilBase == dataLocal.ilBase);
                Debug.Assert(data->metadataStart == dataLocal.metadataStart);
                Debug.Assert(data->metadataSize == dataLocal.metadataSize);
                Debug.Assert(data->LoaderAllocator == dataLocal.LoaderAllocator);
                Debug.Assert(data->ThunkHeap == dataLocal.ThunkHeap);
                Debug.Assert(data->FieldDefToDescMap == dataLocal.FieldDefToDescMap);
                Debug.Assert(data->ManifestModuleReferencesMap == dataLocal.ManifestModuleReferencesMap);
                Debug.Assert(data->MemberRefToDescMap == dataLocal.MemberRefToDescMap);
                Debug.Assert(data->MethodDefToDescMap == dataLocal.MethodDefToDescMap);
                Debug.Assert(data->TypeDefToMethodTableMap == dataLocal.TypeDefToMethodTableMap);
                Debug.Assert(data->TypeRefToMethodTableMap == dataLocal.TypeRefToMethodTableMap);
                Debug.Assert(data->dwModuleID == dataLocal.dwModuleID);
                Debug.Assert(data->dwBaseClassIndex == dataLocal.dwBaseClassIndex);
                Debug.Assert(data->dwModuleIndex == dataLocal.dwModuleIndex);
            }
        }
#endif
        return hr;
    }

    int ISOSDacInterface.GetNestedExceptionData(ClrDataAddress exception, ClrDataAddress* exceptionObject, ClrDataAddress* nextNestedException)
    {
        int hr = HResults.S_OK;
        try
        {
            if (exception == 0 || exceptionObject == null || nextNestedException == null)
                throw new ArgumentException();
            Contracts.IException contract = _target.Contracts.Exception;
            TargetPointer exceptionObjectLocal = contract.GetNestedExceptionInfo(
                exception.ToTargetPointer(_target),
                out TargetPointer nextNestedExceptionLocal,
                out _);
            *exceptionObject = exceptionObjectLocal.ToClrDataAddress(_target);
            *nextNestedException = nextNestedExceptionLocal.Value;
        }
        catch (global::System.Exception ex)
        {
            hr = ex.HResult;
        }

#if DEBUG
        if (_legacyImpl is not null)
        {
            ClrDataAddress exceptionObjectLocal;
            ClrDataAddress nextNestedExceptionLocal;
            int hrLocal = _legacyImpl.GetNestedExceptionData(exception, &exceptionObjectLocal, &nextNestedExceptionLocal);
            Debug.ValidateHResult(hr, hrLocal);
            if (hr == HResults.S_OK)
            {
                Debug.Assert(*exceptionObject == exceptionObjectLocal);
                Debug.Assert(*nextNestedException == nextNestedExceptionLocal);
            }
        }
#endif
        return hr;
    }

    int ISOSDacInterface.GetObjectClassName(ClrDataAddress obj, uint count, char* className, uint* pNeeded)
    {
        int hr = HResults.S_OK;
        try
        {
            if (obj == 0)
                throw new ArgumentException();

            Contracts.IObject objectContract = _target.Contracts.Object;
            Contracts.IRuntimeTypeSystem rts = _target.Contracts.RuntimeTypeSystem;
            Contracts.ILoader loader = _target.Contracts.Loader;

            TargetPointer mt = objectContract.GetMethodTableAddress(obj.ToTargetPointer(_target));
            Contracts.TypeHandle typeHandle = rts.GetTypeHandle(mt);

            TargetPointer modulePointer = rts.GetModule(typeHandle);
            if (modulePointer == TargetPointer.Null)
            {
                OutputBufferHelpers.CopyStringToBuffer(className, count, pNeeded, "<Unloaded Type>");
            }
            else
            {
                Contracts.ModuleHandle moduleHandle = loader.GetModuleHandleFromModulePtr(modulePointer);
                if (!loader.TryGetLoadedImageContents(moduleHandle, out _, out _, out _))
                {
                    OutputBufferHelpers.CopyStringToBuffer(className, count, pNeeded, "<Unloaded Type>");
                }
                else
                {
                    StringBuilder classNameBuilder = new();
                    try
                    {
                        TypeNameBuilder.AppendType(_target, classNameBuilder, typeHandle, TypeNameFormat.FormatNamespace | TypeNameFormat.FormatFullInst);
                    }
                    catch
                    {
                        string? fallbackName = _target.Contracts.DacStreams.StringFromEEAddress(mt);
                        if (fallbackName != null)
                        {
                            classNameBuilder.Clear();
                            classNameBuilder.Append(fallbackName);
                        }
                    }
                    OutputBufferHelpers.CopyStringToBuffer(className, count, pNeeded, classNameBuilder.ToString());
                }
            }
        }
        catch (System.Exception ex)
        {
            hr = ex.HResult;
        }

#if DEBUG
        if (_legacyImpl is not null)
        {
            char[] classNameLocal = new char[count];
            uint neededLocal;
            int hrLocal;
            fixed (char* ptr = classNameLocal)
            {
                hrLocal = _legacyImpl.GetObjectClassName(obj, count, ptr, &neededLocal);
            }
            Debug.ValidateHResult(hr, hrLocal);
            if (hr == HResults.S_OK)
            {
                Debug.Assert(pNeeded == null || *pNeeded == neededLocal);
                Debug.Assert(className == null || new ReadOnlySpan<char>(classNameLocal, 0, (int)neededLocal - 1).SequenceEqual(new string(className)));
            }
        }
#endif
        return hr;
    }

    int ISOSDacInterface.GetObjectData(ClrDataAddress objAddr, DacpObjectData* data)
    {
        int hr = HResults.S_OK;
        try
        {
            if (objAddr == 0 || data == null)
                throw new ArgumentException();
            Contracts.IObject objectContract = _target.Contracts.Object;
            Contracts.IRuntimeTypeSystem runtimeTypeSystemContract = _target.Contracts.RuntimeTypeSystem;

            TargetPointer objPtr = objAddr.ToTargetPointer(_target);
            TargetPointer mt = objectContract.GetMethodTableAddress(objPtr);
            TypeHandle handle = runtimeTypeSystemContract.GetTypeHandle(mt);

            data->MethodTable = mt.ToClrDataAddress(_target);
            data->Size = runtimeTypeSystemContract.GetBaseSize(handle);
            data->dwComponentSize = runtimeTypeSystemContract.GetComponentSize(handle);

            if (runtimeTypeSystemContract.IsFreeObjectMethodTable(handle))
            {
                data->ObjectType = DacpObjectType.OBJ_FREE;

                // Free objects have their component count explicitly set at the same offset as that for arrays
                // Update the size to include those components
                Target.TypeInfo arrayTypeInfo = _target.GetTypeInfo(DataType.Array);
                ulong numComponentsOffset = (ulong)_target.GetTypeInfo(DataType.Array).Fields[Constants.FieldNames.Array.NumComponents].Offset;
                data->Size += _target.Read<uint>(objAddr + numComponentsOffset) * data->dwComponentSize;
            }
            else if (mt == _stringMethodTable.Value)
            {
                data->ObjectType = DacpObjectType.OBJ_STRING;

                // Update the size to include the string character components
                data->Size += (uint)objectContract.GetStringValue(objPtr).Length * data->dwComponentSize;
            }
            else if (mt == _objectMethodTable.Value)
            {
                data->ObjectType = DacpObjectType.OBJ_OBJECT;
            }
            else if (runtimeTypeSystemContract.IsArray(handle, out uint rank))
            {
                data->ObjectType = DacpObjectType.OBJ_ARRAY;
                data->dwRank = rank;

                TargetPointer arrayData = objectContract.GetArrayData(objPtr, out uint numComponents, out TargetPointer boundsStart, out TargetPointer lowerBounds);
                data->ArrayDataPtr = arrayData.ToClrDataAddress(_target);
                data->dwNumComponents = numComponents;
                data->ArrayBoundsPtr = boundsStart.ToClrDataAddress(_target);
                data->ArrayLowerBoundsPtr = lowerBounds.ToClrDataAddress(_target);

                // Update the size to include the array components
                data->Size += numComponents * data->dwComponentSize;

                // Get the type of the array elements
                TypeHandle element = runtimeTypeSystemContract.GetTypeParam(handle);
                data->ElementTypeHandle = element.Address.Value;
                data->ElementType = (uint)runtimeTypeSystemContract.GetSignatureCorElementType(element);

                // Validate the element type handles for arrays of arrays
                while (runtimeTypeSystemContract.IsArray(element, out _))
                {
                    element = runtimeTypeSystemContract.GetTypeParam(element);
                }
            }
            else
            {
                data->ObjectType = DacpObjectType.OBJ_OTHER;
            }

            // Populate COM data if this is a COM object
            if (_target.ReadGlobal<byte>(Constants.Globals.FeatureCOMInterop) != 0
                && objectContract.GetBuiltInComData(objPtr, out TargetPointer rcw, out TargetPointer ccw, out _))
            {
                data->RCW = rcw & ~(_rcwMask);
                data->CCW = ccw;
            }

        }
        catch (System.Exception ex)
        {
            hr = ex.HResult;
        }

#if DEBUG
        if (_legacyImpl is not null)
        {
            DacpObjectData dataLocal;
            int hrLocal = _legacyImpl.GetObjectData(objAddr, &dataLocal);
            Debug.ValidateHResult(hr, hrLocal);
            if (hr == HResults.S_OK)
            {
                Debug.Assert(data->MethodTable == dataLocal.MethodTable);
                Debug.Assert(data->ObjectType == dataLocal.ObjectType);
                Debug.Assert(data->Size == dataLocal.Size);
                Debug.Assert(data->ElementTypeHandle == dataLocal.ElementTypeHandle);
                Debug.Assert(data->ElementType == dataLocal.ElementType);
                Debug.Assert(data->dwRank == dataLocal.dwRank);
                Debug.Assert(data->dwNumComponents == dataLocal.dwNumComponents);
                Debug.Assert(data->dwComponentSize == dataLocal.dwComponentSize);
                Debug.Assert(data->ArrayDataPtr == dataLocal.ArrayDataPtr);
                Debug.Assert(data->ArrayBoundsPtr == dataLocal.ArrayBoundsPtr);
                Debug.Assert(data->ArrayLowerBoundsPtr == dataLocal.ArrayLowerBoundsPtr);
                Debug.Assert(data->RCW == dataLocal.RCW);
                Debug.Assert(data->CCW == dataLocal.CCW);
            }
        }
#endif
        return hr;
    }

    int ISOSDacInterface.GetObjectStringData(ClrDataAddress obj, uint count, char* stringData, uint* pNeeded)
    {
        int hr = HResults.S_OK;
        try
        {
            if (obj == 0 || (stringData == null && pNeeded == null) || (stringData is not null && count <= 0))
                throw new ArgumentException();
            Contracts.IObject contract = _target.Contracts.Object;
            string str = contract.GetStringValue(obj.ToTargetPointer(_target));
            OutputBufferHelpers.CopyStringToBuffer(stringData, count, pNeeded, str);
        }
        catch (System.Exception ex)
        {
            hr = ex.HResult;
        }

#if DEBUG
        if (_legacyImpl is not null)
        {
            char[] stringDataLocal = new char[count];
            uint neededLocal;
            int hrLocal;
            fixed (char* ptr = stringDataLocal)
            {
                hrLocal = _legacyImpl.GetObjectStringData(obj, count, ptr, &neededLocal);
            }
            Debug.ValidateHResult(hr, hrLocal);
            if (hr == HResults.S_OK)
            {
                Debug.Assert(pNeeded == null || *pNeeded == neededLocal);
                Debug.Assert(stringData == null || new ReadOnlySpan<char>(stringDataLocal, 0, (int)neededLocal - 1).SequenceEqual(new string(stringData)));
            }
        }
#endif

        return hr;
    }

    int ISOSDacInterface.GetOOMData(ClrDataAddress oomAddr, DacpOomData* data)
    {
        int hr = HResults.S_OK;
        try
        {
            if (oomAddr == 0 || data == null)
                throw new ArgumentException();

            IGC gc = _target.Contracts.GC;
            string[] gcIdentifiers = gc.GetGCIdentifiers();

            // This method is only valid for server GC mode
            if (!gcIdentifiers.Contains(GCIdentifiers.Server))
                throw Marshal.GetExceptionForHR(HResults.E_FAIL)!;

            GCOomData oomData = gc.GetOomData(oomAddr.ToTargetPointer(_target));

            data->reason = oomData.Reason;
            data->alloc_size = oomData.AllocSize.Value;
            data->available_pagefile_mb = oomData.AvailablePagefileMB.Value;
            data->gc_index = oomData.GCIndex.Value;
            data->fgm = oomData.Fgm;
            data->size = oomData.Size.Value;
            data->loh_p = oomData.LohP ? (int)Interop.BOOL.TRUE : (int)Interop.BOOL.FALSE;
        }
        catch (System.Exception ex)
        {
            hr = ex.HResult;
        }

#if DEBUG
        if (_legacyImpl is not null)
        {
            DacpOomData dataLocal;
            int hrLocal = _legacyImpl.GetOOMData(oomAddr, &dataLocal);
            Debug.ValidateHResult(hr, hrLocal);
            if (hr == HResults.S_OK)
            {
                Debug.Assert(data->reason == dataLocal.reason, $"cDAC: {data->reason}, DAC: {dataLocal.reason}");
                Debug.Assert(data->alloc_size == dataLocal.alloc_size, $"cDAC: {data->alloc_size}, DAC: {dataLocal.alloc_size}");
                Debug.Assert(data->available_pagefile_mb == dataLocal.available_pagefile_mb, $"cDAC: {data->available_pagefile_mb}, DAC: {dataLocal.available_pagefile_mb}");
                Debug.Assert(data->gc_index == dataLocal.gc_index, $"cDAC: {data->gc_index}, DAC: {dataLocal.gc_index}");
                Debug.Assert(data->fgm == dataLocal.fgm, $"cDAC: {data->fgm}, DAC: {dataLocal.fgm}");
                Debug.Assert(data->size == dataLocal.size, $"cDAC: {data->size}, DAC: {dataLocal.size}");
                Debug.Assert(data->loh_p == dataLocal.loh_p, $"cDAC: {data->loh_p}, DAC: {dataLocal.loh_p}");
            }
        }
#endif
        return hr;
    }

    int ISOSDacInterface.GetOOMStaticData(DacpOomData* data)
    {
        int hr = HResults.S_OK;
        try
        {
            if (data == null)
                throw new ArgumentException();

            IGC gc = _target.Contracts.GC;
            string[] gcIdentifiers = gc.GetGCIdentifiers();

            // This method is only valid for workstation GC mode
            if (!gcIdentifiers.Contains(GCIdentifiers.Workstation))
                throw Marshal.GetExceptionForHR(HResults.E_FAIL)!;
            GCOomData oomData = gc.GetOomData();

            data->reason = oomData.Reason;
            data->alloc_size = oomData.AllocSize.Value;
            data->available_pagefile_mb = oomData.AvailablePagefileMB.Value;
            data->gc_index = oomData.GCIndex.Value;
            data->fgm = oomData.Fgm;
            data->size = oomData.Size.Value;
            data->loh_p = oomData.LohP ? (int)Interop.BOOL.TRUE : (int)Interop.BOOL.FALSE;
        }
        catch (System.Exception ex)
        {
            hr = ex.HResult;
        }

#if DEBUG
        if (_legacyImpl is not null)
        {
            DacpOomData dataLocal;
            int hrLocal = _legacyImpl.GetOOMStaticData(&dataLocal);
            Debug.ValidateHResult(hr, hrLocal);
            if (hr == HResults.S_OK)
            {
                Debug.Assert(data->reason == dataLocal.reason, $"cDAC: {data->reason}, DAC: {dataLocal.reason}");
                Debug.Assert(data->alloc_size == dataLocal.alloc_size, $"cDAC: {data->alloc_size}, DAC: {dataLocal.alloc_size}");
                Debug.Assert(data->available_pagefile_mb == dataLocal.available_pagefile_mb, $"cDAC: {data->available_pagefile_mb}, DAC: {dataLocal.available_pagefile_mb}");
                Debug.Assert(data->gc_index == dataLocal.gc_index, $"cDAC: {data->gc_index}, DAC: {dataLocal.gc_index}");
                Debug.Assert(data->fgm == dataLocal.fgm, $"cDAC: {data->fgm}, DAC: {dataLocal.fgm}");
                Debug.Assert(data->size == dataLocal.size, $"cDAC: {data->size}, DAC: {dataLocal.size}");
                Debug.Assert(data->loh_p == dataLocal.loh_p, $"cDAC: {data->loh_p}, DAC: {dataLocal.loh_p}");
            }
        }
#endif
        return hr;
    }

    int ISOSDacInterface.GetPEFileBase(ClrDataAddress addr, ClrDataAddress* peBase)
    {
        int hr = HResults.S_OK;
        try
        {
            if (addr == 0 || peBase == null)
                throw new ArgumentException();
            Contracts.ILoader contract = _target.Contracts.Loader;
            Contracts.ModuleHandle handle = contract.GetModuleHandleFromModulePtr(addr.ToTargetPointer(_target));
            Contracts.ModuleFlags flags = contract.GetFlags(handle);

            if (!flags.HasFlag(Contracts.ModuleFlags.ReflectionEmit))
            {
                *peBase = contract.GetILBase(handle).ToClrDataAddress(_target);
            }
            else
            {
                *peBase = 0;
            }
        }
        catch (System.Exception ex)
        {
            hr = ex.HResult;
        }

#if DEBUG
        if (_legacyImpl is not null)
        {
            ClrDataAddress peBaseLocal;
            int hrLocal = _legacyImpl.GetPEFileBase(addr, &peBaseLocal);
            Debug.ValidateHResult(hr, hrLocal);
            if (hr == HResults.S_OK)
                Debug.Assert(*peBase == peBaseLocal);
        }
#endif
        return hr;
    }

    int ISOSDacInterface.GetPEFileName(ClrDataAddress addr, uint count, char* fileName, uint* pNeeded)
    {
        int hr = HResults.S_OK;
        try
        {
            if (addr == 0 || (fileName == null && pNeeded == null) || (fileName is not null && count <= 0))
                throw new ArgumentException();

            Contracts.ILoader contract = _target.Contracts.Loader;
            Contracts.ModuleHandle handle = contract.GetModuleHandleFromModulePtr(addr.ToTargetPointer(_target));
            string path = contract.GetPath(handle);

            // Return not implemented for empty paths for non-reflection emit assemblies (for example, loaded from memory)
            if (string.IsNullOrEmpty(path))
            {
                Contracts.ModuleFlags flags = contract.GetFlags(handle);
                if (!flags.HasFlag(Contracts.ModuleFlags.ReflectionEmit))
                    throw new NotImplementedException();
            }

            OutputBufferHelpers.CopyStringToBuffer(fileName, count, pNeeded, path);
        }
        catch (System.Exception ex)
        {
            hr = ex.HResult;
        }

#if DEBUG
        if (_legacyImpl is not null)
        {
            char[] fileNameLocal = new char[count];
            uint neededLocal;
            int hrLocal;
            fixed (char* ptr = fileNameLocal)
            {
                hrLocal = _legacyImpl.GetPEFileName(addr, count, ptr, &neededLocal);
            }
            Debug.ValidateHResult(hr, hrLocal);
            if (hr == HResults.S_OK)
            {
                Debug.Assert(pNeeded == null || *pNeeded == neededLocal);
                Debug.Assert(fileName == null || new ReadOnlySpan<char>(fileNameLocal, 0, (int)neededLocal - 1).SequenceEqual(new string(fileName)));
            }
        }
#endif
        return hr;
    }

    int ISOSDacInterface.GetPrivateBinPaths(ClrDataAddress appDomain, int count, char* paths, uint* pNeeded)
    {
        // Method is not supported on CoreCLR
        int hr = HResults.E_NOTIMPL;

#if DEBUG
        if (_legacyImpl is not null)
        {
            int hrLocal = _legacyImpl.GetPrivateBinPaths(appDomain, count, paths, pNeeded);
            Debug.ValidateHResult(hr, hrLocal);
        }
#endif

        return hr;
    }
    int ISOSDacInterface.GetRCWData(ClrDataAddress addr, DacpRCWData* data)
    {
        int hr = HResults.S_OK;
        try
        {
            if (addr == 0 || data is null)
                throw new ArgumentException();

            IBuiltInCOM builtInCom = _target.Contracts.BuiltInCOM; // E_NOTIMPL if not defined (non-Windows)
            *data = default;
            TargetPointer rcwPtr = addr.ToTargetPointer(_target);
            Contracts.RCWData rcwData = builtInCom.GetRCWData(rcwPtr);

            data->identityPointer = rcwData.IdentityPointer.ToClrDataAddress(_target);
            data->unknownPointer = rcwData.UnknownPointer.ToClrDataAddress(_target);
            data->managedObject = rcwData.ManagedObject.ToClrDataAddress(_target);
            data->vtablePtr = rcwData.VTablePtr.ToClrDataAddress(_target);
            data->creatorThread = rcwData.CreatorThread.ToClrDataAddress(_target);
            data->ctxCookie = rcwData.CtxCookie.ToClrDataAddress(_target);
            data->refCount = (int)rcwData.RefCount;
            data->interfaceCount = builtInCom.GetRCWInterfaces(rcwPtr).Count();
            data->isAggregated = rcwData.IsAggregated ? Interop.BOOL.TRUE : Interop.BOOL.FALSE;
            data->isContained = rcwData.IsContained ? Interop.BOOL.TRUE : Interop.BOOL.FALSE;
            data->isFreeThreaded = rcwData.IsFreeThreaded ? Interop.BOOL.TRUE : Interop.BOOL.FALSE;
            data->isDisconnected = rcwData.IsDisconnected ? Interop.BOOL.TRUE : Interop.BOOL.FALSE;
        }
        catch (System.Exception ex)
        {
            hr = ex.HResult;
        }

#if DEBUG
        if (_legacyImpl is not null)
        {
            DacpRCWData dataLocal;
            int hrLocal = _legacyImpl.GetRCWData(addr, &dataLocal);
            Debug.ValidateHResult(hr, hrLocal);
            if (hr == HResults.S_OK)
            {
                Debug.Assert(data->identityPointer == dataLocal.identityPointer, $"cDAC: {data->identityPointer:x}, DAC: {dataLocal.identityPointer:x}");
                Debug.Assert(data->unknownPointer == dataLocal.unknownPointer, $"cDAC: {data->unknownPointer:x}, DAC: {dataLocal.unknownPointer:x}");
                Debug.Assert(data->managedObject == dataLocal.managedObject, $"cDAC: {data->managedObject:x}, DAC: {dataLocal.managedObject:x}");
                Debug.Assert(data->vtablePtr == dataLocal.vtablePtr, $"cDAC: {data->vtablePtr:x}, DAC: {dataLocal.vtablePtr:x}");
                Debug.Assert(data->creatorThread == dataLocal.creatorThread, $"cDAC: {data->creatorThread:x}, DAC: {dataLocal.creatorThread:x}");
                Debug.Assert(data->ctxCookie == dataLocal.ctxCookie, $"cDAC: {data->ctxCookie:x}, DAC: {dataLocal.ctxCookie:x}");
                Debug.Assert(data->refCount == dataLocal.refCount, $"cDAC: {data->refCount}, DAC: {dataLocal.refCount}");
                Debug.Assert(data->interfaceCount == dataLocal.interfaceCount, $"cDAC: {data->interfaceCount}, DAC: {dataLocal.interfaceCount}");
                Debug.Assert(data->isAggregated == dataLocal.isAggregated, $"cDAC: {data->isAggregated}, DAC: {dataLocal.isAggregated}");
                Debug.Assert(data->isContained == dataLocal.isContained, $"cDAC: {data->isContained}, DAC: {dataLocal.isContained}");
                Debug.Assert(data->isFreeThreaded == dataLocal.isFreeThreaded, $"cDAC: {data->isFreeThreaded}, DAC: {dataLocal.isFreeThreaded}");
                Debug.Assert(data->isDisconnected == dataLocal.isDisconnected, $"cDAC: {data->isDisconnected}, DAC: {dataLocal.isDisconnected}");
            }
        }
#endif

        return hr;
    }
    int ISOSDacInterface.GetRCWInterfaces(ClrDataAddress rcw, uint count, [In, MarshalUsing(CountElementName = nameof(count)), Out] DacpCOMInterfacePointerData[]? interfaces, uint* pNeeded)
    {
        int hr = HResults.S_OK;
#if DEBUG
        int numWritten = 0;
#endif
        try
        {
            if (rcw == 0)
                throw new ArgumentException();

            TargetPointer rcwPtr = rcw.ToTargetPointer(_target);
            IBuiltInCOM builtInCom = _target.Contracts.BuiltInCOM; // E_NOTIMPL if not defined (non-Windows)
            IEnumerable<(TargetPointer MethodTable, TargetPointer Unknown)> entries = builtInCom.GetRCWInterfaces(rcwPtr);

            if (interfaces == null)
            {
                if (pNeeded == null)
                {
                    throw new ArgumentException();
                }
                else
                {
                    *pNeeded = (uint)entries.Count();
#if DEBUG
                    numWritten = (int)*pNeeded;
#endif
                }
            }
            else
            {
                TargetPointer ctxCookie = builtInCom.GetRCWContext(rcwPtr);
                uint itemIndex = 0;
                foreach (var (methodTable, unknown) in entries)
                {
                    if (itemIndex >= count)
                    {
#if DEBUG
                        numWritten = (int)itemIndex;
#endif
                        throw new ArgumentException();
                    }

                    interfaces[itemIndex].methodTable = methodTable.ToClrDataAddress(_target);
                    interfaces[itemIndex].interfacePtr = unknown.ToClrDataAddress(_target);
                    interfaces[itemIndex].comContext = ctxCookie.ToClrDataAddress(_target);
                    itemIndex++;
                }

                if (pNeeded != null)
                    *pNeeded = itemIndex;
#if DEBUG
                numWritten = (int)itemIndex;
#endif
            }
        }
        catch (System.Exception ex)
        {
            hr = ex.HResult;
        }

#if DEBUG
        if (_legacyImpl is not null)
        {
            uint pNeededLocal = 0;
            int hrLocal;
            DacpCOMInterfacePointerData[]? interfacesLocal = interfaces != null ? new DacpCOMInterfacePointerData[count] : null;
            hrLocal = _legacyImpl.GetRCWInterfaces(rcw, count, interfacesLocal, pNeeded == null && interfacesLocal == null ? null : &pNeededLocal);
            Debug.ValidateHResult(hr, hrLocal);
            if (hr == HResults.S_OK)
            {
                Debug.Assert(numWritten == pNeededLocal, $"cDAC: {numWritten}, DAC: {pNeededLocal}");
                if (interfacesLocal is not null && interfaces is not null)
                {
                    for (int i = 0; i < (int)pNeededLocal; i++)
                    {
                        Debug.Assert(interfaces[i].methodTable == interfacesLocal![i].methodTable, $"cDAC: {interfaces[i].methodTable:x}, DAC: {interfacesLocal[i].methodTable:x}");
                        Debug.Assert(interfaces[i].interfacePtr == interfacesLocal![i].interfacePtr, $"cDAC: {interfaces[i].interfacePtr:x}, DAC: {interfacesLocal[i].interfacePtr:x}");
                        Debug.Assert(interfaces[i].comContext == interfacesLocal![i].comContext, $"cDAC: {interfaces[i].comContext:x}, DAC: {interfacesLocal[i].comContext:x}");
                    }
                }
            }
        }
#endif

        return hr;
    }
    int ISOSDacInterface.GetRegisterName(int regName, uint count, char* buffer, uint* pNeeded)
    {
        int hr = HResults.S_OK;
        try
        {
            if (buffer is null && pNeeded is null)
                throw new NullReferenceException();

            string[] regs = _target.Contracts.RuntimeInfo.GetTargetArchitecture() switch
            {
                RuntimeInfoArchitecture.X64 => s_amd64Registers,
                RuntimeInfoArchitecture.Arm => s_armRegisters,
                RuntimeInfoArchitecture.Arm64 => s_arm64Registers,
                RuntimeInfoArchitecture.X86 => s_x86Registers,
                RuntimeInfoArchitecture.LoongArch64 => s_loongArch64Registers,
                RuntimeInfoArchitecture.RiscV64 => s_riscV64Registers,
                _ => throw new InvalidOperationException(),
            };

            // Caller frame registers are encoded as "-(reg+1)".
            bool callerFrame = regName < 0;
            int regIndex = callerFrame ? -regName - 1 : regName;

            if ((uint)regIndex >= (uint)regs.Length)
                return unchecked((int)0x8000FFFF); // E_UNEXPECTED

            string name = callerFrame ? $"caller.{regs[regIndex]}" : regs[regIndex];

            uint needed = (uint)(name.Length + 1);
            if (pNeeded is not null)
                *pNeeded = needed;

            if (buffer is not null)
            {
                OutputBufferHelpers.CopyStringToBuffer(buffer, count, neededBufferSize: null, name);

                if (count < needed)
                    hr = HResults.S_FALSE;
            }
        }
        catch (System.Exception ex)
        {
            hr = ex.HResult;
        }

#if DEBUG
        if (_legacyImpl is not null)
        {
            char[] bufferLocal = new char[count];
            uint neededLocal;
            int hrLocal;
            fixed (char* ptr = bufferLocal)
            {
                hrLocal = _legacyImpl.GetRegisterName(regName, count, ptr, &neededLocal);
            }
            Debug.ValidateHResult(hr, hrLocal);
            if (hr == HResults.S_OK || hr == HResults.S_FALSE)
            {
                Debug.Assert(pNeeded is null || *pNeeded == neededLocal);
                Debug.Assert(buffer is null || new ReadOnlySpan<char>(bufferLocal, 0, (int)Math.Min(count, neededLocal)).SequenceEqual(new ReadOnlySpan<char>(buffer, (int)Math.Min(count, neededLocal))));
            }
        }
#endif

        return hr;
    }

    private static readonly string[] s_amd64Registers =
    [
        "rax", "rcx", "rdx", "rbx", "rsp", "rbp", "rsi", "rdi",
        "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15",
    ];

    private static readonly string[] s_armRegisters =
    [
        "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7",
        "r8", "r9", "r10", "r11", "r12", "sp", "lr",
    ];

    private static readonly string[] s_arm64Registers =
    [
        "X0", "X1", "X2", "X3", "X4", "X5", "X6", "X7",
        "X8", "X9", "X10", "X11", "X12", "X13", "X14", "X15", "X16", "X17",
        "X18", "X19", "X20", "X21", "X22", "X23", "X24", "X25", "X26", "X27",
        "X28", "Fp", "Lr", "Sp",
    ];

    private static readonly string[] s_x86Registers =
    [
        "eax", "ecx", "edx", "ebx", "esp", "ebp", "esi", "edi",
    ];

    private static readonly string[] s_loongArch64Registers =
    [
        "R0", "RA", "TP", "SP",
        "A0", "A1", "A2", "A3",
        "A4", "A5", "A6", "A7",
        "T0", "T1", "T2", "T3",
        "T4", "T5", "T6", "T7",
        "T8", "R21", "FP", "S0",
        "S1", "S2", "S3", "S4",
        "S5", "S6", "S7", "S8",
    ];

    private static readonly string[] s_riscV64Registers =
    [
        "zero", "RA", "SP", "GP",
        "TP", "T0", "T1", "T2",
        "FP", "S1", "A0", "A1",
        "A2", "A3", "A4", "A5",
        "A6", "A7", "S2", "S3",
        "S4", "S5", "S6", "S7",
        "S8", "S9", "S10", "S11",
        "T3", "T4", "T5", "T6",
    ];

    int ISOSDacInterface.GetStackLimits(ClrDataAddress threadPtr, ClrDataAddress* lower, ClrDataAddress* upper, ClrDataAddress* fp)
    {
        int hr = HResults.S_OK;
        try
        {
            if (threadPtr == 0 || (lower == null && upper == null && fp == null))
                throw new ArgumentException();

            Contracts.IThread contract = _target.Contracts.Thread;
            TargetPointer stackBase, stackLimit, frameAddress;
            contract.GetStackLimitData(threadPtr.ToTargetPointer(_target), out stackBase, out stackLimit, out frameAddress);

            if (lower != null)
                *lower = stackBase.ToClrDataAddress(_target);

            if (upper != null)
                *upper = stackLimit.ToClrDataAddress(_target);

            if (fp != null)
                *fp = frameAddress.ToClrDataAddress(_target);
        }
        catch (global::System.Exception ex)
        {
            hr = ex.HResult;
        }

#if DEBUG
        if (_legacyImpl is not null)
        {
            ClrDataAddress lowerLocal, upperLocal, fpLocal;
            int hrLocal = _legacyImpl.GetStackLimits(threadPtr, &lowerLocal, &upperLocal, &fpLocal);
            Debug.ValidateHResult(hr, hrLocal);
            if (hr == HResults.S_OK)
            {
                Debug.Assert(lower == null || *lower == lowerLocal, $"cDAC: {*lower:x}, DAC: {lowerLocal:x}");
                Debug.Assert(upper == null || *upper == upperLocal, $"cDAC: {*upper:x}, DAC: {upperLocal:x}");
                Debug.Assert(fp == null || *fp == fpLocal, $"cDAC: {*fp:x}, DAC: {fpLocal:x}");
            }
        }
#endif
        return hr;
    }

    [GeneratedComClass]
    internal sealed unsafe partial class SOSStackRefEnum : ISOSStackRefEnum
    {
        private readonly SOSStackRefData[] _refs;
        private uint _index;

        public SOSStackRefEnum(SOSStackRefData[] refs)
        {
            _refs = refs;
        }

        int ISOSStackRefEnum.Next(uint count, SOSStackRefData[] refs, uint* pFetched)
        {
            int hr = HResults.S_OK;
            try
            {
                if (pFetched is null || refs is null)
                    throw new NullReferenceException();

                count = Math.Min(count, (uint)refs.Length);
                uint written = 0;
                while (written < count && _index < _refs.Length)
                    refs[written++] = _refs[(int)_index++];

                *pFetched = written;
                // COMPAT: S_FALSE means more items remain, S_OK means enumeration is complete.
                // This is the inverse of the standard COM IEnumXxx convention, but matches
                // the legacy DAC behavior (see SOSHandleEnum.Next).
                hr = _index < _refs.Length ? HResults.S_FALSE : HResults.S_OK;
            }
            catch (System.Exception ex)
            {
                hr = ex.HResult;
            }

            return hr;
        }

        int ISOSStackRefEnum.EnumerateErrors(DacComNullableByRef<ISOSStackRefErrorEnum> ppEnum)
        {
            return HResults.E_NOTIMPL;
        }

        int ISOSEnum.Skip(uint count)
        {
            _index = Math.Min(_index + count, (uint)_refs.Length);
            return HResults.S_OK;
        }

        int ISOSEnum.Reset()
        {
            _index = 0;
            return HResults.S_OK;
        }

        int ISOSEnum.GetCount(uint* pCount)
        {
            if (pCount is null) return HResults.E_POINTER;
            *pCount = (uint)_refs.Length;
            return HResults.S_OK;
        }
    }

    int ISOSDacInterface.GetStackReferences(int osThreadID, DacComNullableByRef<ISOSStackRefEnum> ppEnum)
    {
        int hr = HResults.S_OK;
        try
        {
            IThread threadContract = _target.Contracts.Thread;
            IStackWalk stackWalkContract = _target.Contracts.StackWalk;
            ThreadData? matchingThread = null;

            ThreadStoreData threadStore = threadContract.GetThreadStoreData();
            TargetPointer threadAddr = threadStore.FirstThread;
            while (threadAddr != TargetPointer.Null)
            {
                ThreadData td = threadContract.GetThreadData(threadAddr);
                if (td.OSId.Value == (ulong)osThreadID)
                {
                    matchingThread = td;
                    break;
                }
                threadAddr = td.NextThread;
            }

            if (matchingThread is null)
            {
                throw new ArgumentException($"No thread with OS ID {osThreadID} was found.");
            }

            IReadOnlyList<StackReferenceData> refs = stackWalkContract.WalkStackReferences(matchingThread.Value);

            SOSStackRefData[] sosRefs = new SOSStackRefData[refs.Count];
            for (int i = 0; i < refs.Count; i++)
            {
                sosRefs[i] = new SOSStackRefData
                {
                    HasRegisterInformation = refs[i].HasRegisterInformation ? 1 : 0,
                    Register = refs[i].Register,
                    Offset = refs[i].Offset,
                    Address = refs[i].Address.Value,
                    Object = refs[i].Object.Value,
                    Flags = refs[i].Flags,
                    Source = refs[i].Source.Value,
                    SourceType = refs[i].IsStackSourceFrame
                        ? SOSStackSourceType.SOS_StackSourceFrame
                        : SOSStackSourceType.SOS_StackSourceIP,
                    StackPointer = refs[i].StackPointer.Value,
                };
            }

            ppEnum.Interface = new SOSStackRefEnum(sosRefs);
        }
        catch (System.Exception ex)
        {
            hr = ex.HResult;
        }
#if DEBUG
        if (_legacyImpl is not null)
        {
            // Validate that the legacy DAC produces the same HResult.
            // We pass isNullRef: false to request actual enumeration, but we don't
            // compare individual refs — that's done by cdacstress.cpp at runtime.
            int hrLocal = _legacyImpl.GetStackReferences(osThreadID, new DacComNullableByRef<ISOSStackRefEnum>(isNullRef: false));
            Debug.ValidateHResult(hr, hrLocal);
        }
#endif
        return hr;
    }

    int ISOSDacInterface.GetStressLogAddress(ClrDataAddress* stressLog)
    {
        ulong stressLogAddress = _target.ReadGlobalPointer(Constants.Globals.StressLog);

#if DEBUG
        if (_legacyImpl is not null)
        {
            ClrDataAddress legacyStressLog;
            Debug.Assert(HResults.S_OK == _legacyImpl.GetStressLogAddress(&legacyStressLog));
            Debug.Assert(legacyStressLog == stressLogAddress);
        }
#endif
        *stressLog = stressLogAddress;
        return HResults.S_OK;
    }

    int ISOSDacInterface.GetSyncBlockCleanupData(ClrDataAddress addr, DacpSyncBlockCleanupData* data)
    {
        int hr = HResults.S_OK;
        try
        {
            if (data == null)
                throw new ArgumentException();
            *data = default;

            ISyncBlock syncBlockContract = _target.Contracts.SyncBlock;
            TargetPointer syncBlockPtr;
            if (addr == 0)
            {
                syncBlockPtr = syncBlockContract.GetSyncBlockFromCleanupList();
            }
            else
            {
                syncBlockPtr = addr.ToTargetPointer(_target);
            }

            if (syncBlockPtr != TargetPointer.Null)
            {
                data->SyncBlockPointer = syncBlockPtr.ToClrDataAddress(_target);
                data->nextSyncBlock = syncBlockContract.GetNextSyncBlock(syncBlockPtr).ToClrDataAddress(_target);
                if (syncBlockContract.GetBuiltInComData(syncBlockPtr, out TargetPointer rcw, out TargetPointer ccw, out TargetPointer ccf))
                {
                    data->blockRCW = rcw.ToClrDataAddress(_target);
                    data->blockClassFactory = ccf.ToClrDataAddress(_target);
                    data->blockCCW = ccw.ToClrDataAddress(_target);
                }
            }

            // Maintain backwards compatibility with old versions of CLRMD. They will not properly iterate, but at least it will not infinite loop.
            if (addr == 0)
                throw new ArgumentException();

        }
        catch (System.Exception ex)
        {
            hr = ex.HResult;
        }
#if DEBUG
        if (_legacyImpl is not null)
        {
            DacpSyncBlockCleanupData dataLocal;
            int hrLocal = _legacyImpl.GetSyncBlockCleanupData(addr, &dataLocal);
            Debug.ValidateHResult(hr, hrLocal);
            if (hr == HResults.S_OK)
            {
                Debug.Assert(data->SyncBlockPointer == dataLocal.SyncBlockPointer, $"cDAC: {data->SyncBlockPointer:x}, DAC: {dataLocal.SyncBlockPointer:x}");
                Debug.Assert(data->nextSyncBlock == dataLocal.nextSyncBlock, $"cDAC: {data->nextSyncBlock:x}, DAC: {dataLocal.nextSyncBlock:x}");
                Debug.Assert(data->blockRCW == dataLocal.blockRCW, $"cDAC: {data->blockRCW:x}, DAC: {dataLocal.blockRCW:x}");
                Debug.Assert(data->blockClassFactory == dataLocal.blockClassFactory, $"cDAC: {data->blockClassFactory:x}, DAC: {dataLocal.blockClassFactory:x}");
                Debug.Assert(data->blockCCW == dataLocal.blockCCW, $"cDAC: {data->blockCCW:x}, DAC: {dataLocal.blockCCW:x}");
            }
        }
#endif
        return hr;
    }

    int ISOSDacInterface.GetSyncBlockData(uint number, DacpSyncBlockData* data)
    {
        int hr = HResults.S_OK;
        try
        {
            if (data == null)
                throw new ArgumentException();
            *data = default;
            data->bFree = Interop.BOOL.TRUE;

            ISyncBlock syncBlock = _target.Contracts.SyncBlock;
            uint syncBlockCount = syncBlock.GetSyncBlockCount();
            data->SyncBlockCount = syncBlockCount;
            if (syncBlockCount > 0 && number <= syncBlockCount)
            {
                data->bFree = syncBlock.IsSyncBlockFree(number) ? Interop.BOOL.TRUE : Interop.BOOL.FALSE;
                if (data->bFree == Interop.BOOL.FALSE)
                {
                    TargetPointer obj = syncBlock.GetSyncBlockObject(number);
                    data->Object = obj.ToClrDataAddress(_target);
                    if (syncBlock.GetSyncBlock(number) is TargetPointer syncBlockAddr && syncBlockAddr != TargetPointer.Null)
                    {
                        data->SyncBlockPointer = syncBlockAddr.ToClrDataAddress(_target);
                        if (syncBlock.GetBuiltInComData(syncBlockAddr, out TargetPointer rcw, out TargetPointer ccw, out TargetPointer ccf))
                        {
                            data->COMFlags = (rcw & ~(_rcwMask)) != TargetPointer.Null ? (uint)DacpSyncBlockData.COMFlagsEnum.HasRCW : 0;
                            data->COMFlags |= ccw != TargetPointer.Null ? (uint)DacpSyncBlockData.COMFlagsEnum.HasCCW : 0;
                            data->COMFlags |= ccf != TargetPointer.Null ? (uint)DacpSyncBlockData.COMFlagsEnum.HasCCF : 0;
                        }
                        bool monitorHeld = syncBlock.TryGetLockInfo(syncBlockAddr, out uint owningThreadId, out uint recursion);
                        data->MonitorHeld = monitorHeld ? (uint)1 : 0;
                        if (monitorHeld)
                        {
                            data->Recursion = recursion + 1;
                            IThread thread = _target.Contracts.Thread;
                            TargetPointer threadPtr = thread.IdToThread(owningThreadId);
                            data->HoldingThread = threadPtr.ToClrDataAddress(_target);
                        }

                        TargetPointer appDomainPointer = _target.ReadGlobalPointer(Constants.Globals.AppDomain);
                        TargetPointer appDomain = _target.ReadPointer(appDomainPointer);
                        data->appDomainPtr = appDomain.ToClrDataAddress(_target);

                        data->AdditionalThreadCount = syncBlock.GetAdditionalThreadCount(syncBlockAddr);

                    }
                }
            }
        }
        catch (System.Exception ex)
        {
            hr = ex.HResult;
        }
#if DEBUG
        if (_legacyImpl is not null)
        {
            DacpSyncBlockData dataLocal;
            int hrLocal = _legacyImpl.GetSyncBlockData(number, &dataLocal);
            Debug.ValidateHResult(hr, hrLocal);
            if (hr == HResults.S_OK)
            {
                Debug.Assert(data->Object == dataLocal.Object, $"cDAC: {data->Object:x}, DAC: {dataLocal.Object:x}");
                Debug.Assert(data->bFree == dataLocal.bFree, $"cDAC: {data->bFree}, DAC: {dataLocal.bFree}");
                Debug.Assert(data->SyncBlockPointer == dataLocal.SyncBlockPointer, $"cDAC: {data->SyncBlockPointer:x}, DAC: {dataLocal.SyncBlockPointer:x}");
                Debug.Assert(data->COMFlags == dataLocal.COMFlags, $"cDAC: {data->COMFlags}, DAC: {dataLocal.COMFlags}");
                Debug.Assert(data->MonitorHeld == dataLocal.MonitorHeld, $"cDAC: {data->MonitorHeld}, DAC: {dataLocal.MonitorHeld}");
                if (data->MonitorHeld != 0)
                {
                    Debug.Assert(data->Recursion == dataLocal.Recursion, $"cDAC: {data->Recursion}, DAC: {dataLocal.Recursion}");
                    Debug.Assert(data->HoldingThread == dataLocal.HoldingThread, $"cDAC: {data->HoldingThread:x}, DAC: {dataLocal.HoldingThread:x}");
                }
                Debug.Assert(data->AdditionalThreadCount == dataLocal.AdditionalThreadCount, $"cDAC: {data->AdditionalThreadCount}, DAC: {dataLocal.AdditionalThreadCount}");
                Debug.Assert(data->appDomainPtr == dataLocal.appDomainPtr, $"cDAC: {data->appDomainPtr:x}, DAC: {dataLocal.appDomainPtr:x}");
                Debug.Assert(data->SyncBlockCount == dataLocal.SyncBlockCount, $"cDAC: {data->SyncBlockCount}, DAC: {dataLocal.SyncBlockCount}");
            }
        }
#endif
        return hr;
    }

    int ISOSDacInterface.GetThreadAllocData(ClrDataAddress thread, DacpAllocData* data)
    {
        int hr = HResults.S_OK;
        try
        {
            if (thread == 0)
                throw new ArgumentException();
            if (data is null)
                throw new NullReferenceException();

            Contracts.IThread contract = _target.Contracts.Thread;
            contract.GetThreadAllocContext(thread.ToTargetPointer(_target), out long allocBytes, out long allocBytesLoh);
            data->allocBytes = (ClrDataAddress)(ulong)allocBytes;
            data->allocBytesLoh = (ClrDataAddress)(ulong)allocBytesLoh;
        }
        catch (global::System.Exception ex)
        {
            hr = ex.HResult;
        }

#if DEBUG
        if (_legacyImpl is not null)
        {
            DacpAllocData dataLocal = default;
            int hrLocal = _legacyImpl.GetThreadAllocData(thread, &dataLocal);
            Debug.ValidateHResult(hr, hrLocal);
            if (hr == HResults.S_OK)
            {
                Debug.Assert(data->allocBytes == dataLocal.allocBytes, $"cDAC: {data->allocBytes:x}, DAC: {dataLocal.allocBytes:x}");
                Debug.Assert(data->allocBytesLoh == dataLocal.allocBytesLoh, $"cDAC: {data->allocBytesLoh:x}, DAC: {dataLocal.allocBytesLoh:x}");
            }
        }
#endif
        return hr;
    }

    int ISOSDacInterface.GetThreadData(ClrDataAddress thread, DacpThreadData* data)
    {
        int hr = HResults.S_OK;
        try
        {
            if (thread == 0 || data == null)
                throw new ArgumentException();
            Contracts.IThread contract = _target.Contracts.Thread;
            Contracts.ThreadData threadData = contract.GetThreadData(thread.ToTargetPointer(_target));
            data->corThreadId = (int)threadData.Id;
            data->osThreadId = (int)threadData.OSId.Value;
            data->state = 0; // Set to 0, nobody uses this
            data->preemptiveGCDisabled = (uint)(threadData.PreemptiveGCDisabled ? 1 : 0);
            data->allocContextPtr = threadData.AllocContextPointer.ToClrDataAddress(_target);
            data->allocContextLimit = threadData.AllocContextLimit.ToClrDataAddress(_target);
            data->fiberData = 0;    // Always set to 0 - fibers are no longer supported

            TargetPointer appDomainPointer = _target.ReadGlobalPointer(Constants.Globals.AppDomain);
            TargetPointer appDomain = _target.ReadPointer(appDomainPointer);
            data->context = appDomain.ToClrDataAddress(_target);
            data->domain = appDomain.ToClrDataAddress(_target);

            data->lockCount = -1;   // Always set to -1 - lock count was .NET Framework and no longer needed
            data->pFrame = threadData.Frame.ToClrDataAddress(_target);
            data->firstNestedException = threadData.FirstNestedException.ToClrDataAddress(_target);
            data->teb = 0;    // TEB is no longer provided by the DAC - consumers should look it up from the OS thread ID
            data->lastThrownObjectHandle = threadData.LastThrownObjectHandle.ToClrDataAddress(_target);
            data->nextThread = threadData.NextThread.ToClrDataAddress(_target);
        }
        catch (global::System.Exception ex)
        {
            hr = ex.HResult;
        }

#if DEBUG
        if (_legacyImpl is not null)
        {
            DacpThreadData dataLocal;
            int hrLocal = _legacyImpl.GetThreadData(thread, &dataLocal);
            Debug.ValidateHResult(hr, hrLocal);
            if (hr == HResults.S_OK)
            {
                Debug.Assert(data->corThreadId == dataLocal.corThreadId, $"cDAC: {data->corThreadId}, DAC: {dataLocal.corThreadId}");
                Debug.Assert(data->osThreadId == dataLocal.osThreadId, $"cDAC: {data->osThreadId}, DAC: {dataLocal.osThreadId}");
                Debug.Assert(data->state == dataLocal.state, $"cDAC: {data->state}, DAC: {dataLocal.state}");
                Debug.Assert(data->preemptiveGCDisabled == dataLocal.preemptiveGCDisabled, $"cDAC: {data->preemptiveGCDisabled}, DAC: {dataLocal.preemptiveGCDisabled}");
                Debug.Assert(data->allocContextPtr == dataLocal.allocContextPtr, $"cDAC: {data->allocContextPtr:x}, DAC: {dataLocal.allocContextPtr:x}");
                Debug.Assert(data->allocContextLimit == dataLocal.allocContextLimit, $"cDAC: {data->allocContextLimit:x}, DAC: {dataLocal.allocContextLimit:x}");
                Debug.Assert(data->fiberData == dataLocal.fiberData, $"cDAC: {data->fiberData:x}, DAC: {dataLocal.fiberData:x}");
                Debug.Assert(data->context == dataLocal.context, $"cDAC: {data->context:x}, DAC: {dataLocal.context:x}");
                Debug.Assert(data->domain == dataLocal.domain, $"cDAC: {data->domain:x}, DAC: {dataLocal.domain:x}");
                Debug.Assert(data->lockCount == dataLocal.lockCount, $"cDAC: {data->lockCount}, DAC: {dataLocal.lockCount}");
                Debug.Assert(data->pFrame == dataLocal.pFrame, $"cDAC: {data->pFrame:x}, DAC: {dataLocal.pFrame:x}");
                Debug.Assert(data->firstNestedException == dataLocal.firstNestedException, $"cDAC: {data->firstNestedException:x}, DAC: {dataLocal.firstNestedException:x}");
                Debug.Assert(data->lastThrownObjectHandle == dataLocal.lastThrownObjectHandle, $"cDAC: {data->lastThrownObjectHandle:x}, DAC: {dataLocal.lastThrownObjectHandle:x}");
                Debug.Assert(data->nextThread == dataLocal.nextThread, $"cDAC: {data->nextThread:x}, DAC: {dataLocal.nextThread:x}");
            }
        }
#endif
        return hr;
    }
    int ISOSDacInterface.GetThreadFromThinlockID(uint thinLockId, ClrDataAddress* pThread)
    {
        int hr = HResults.S_OK;
        try
        {
            if (pThread == null)
                throw new ArgumentException();
            TargetPointer threadPtr = _target.Contracts.Thread.IdToThread(thinLockId);
            *pThread = threadPtr.ToClrDataAddress(_target);
        }
        catch (System.Exception ex)
        {
            hr = ex.HResult;
        }
#if DEBUG
        if (_legacyImpl is not null)
        {
            ClrDataAddress pThreadLocal;
            int hrLocal = _legacyImpl.GetThreadFromThinlockID(thinLockId, &pThreadLocal);
            Debug.ValidateHResult(hr, hrLocal);
            if (hr == HResults.S_OK)
            {
                Debug.Assert(*pThread == pThreadLocal);
            }
        }
#endif
        return hr;
    }
    int ISOSDacInterface.GetThreadLocalModuleData(ClrDataAddress thread, uint index, void* data)
    {
        // CoreCLR does not use thread local modules anymore
        int hr = HResults.E_NOTIMPL;

#if DEBUG
        if (_legacyImpl is not null)
        {
            int hrLocal = _legacyImpl.GetThreadLocalModuleData(thread, index, data);
            Debug.ValidateHResult(hr, hrLocal);
        }
#endif

        return hr;
    }

    int ISOSDacInterface.GetThreadpoolData(void* data)
    {
        // This API is not implemented by the legacy DAC
        int hr = HResults.E_NOTIMPL;

#if DEBUG
        if (_legacyImpl is not null)
        {
            int hrLocal = _legacyImpl.GetThreadpoolData(data);
            Debug.ValidateHResult(hr, hrLocal);
        }
#endif

        return hr;
    }

    int ISOSDacInterface.GetThreadStoreData(DacpThreadStoreData* data)
    {
        int hr = HResults.S_OK;
        try
        {
            if (data == null)
                throw new ArgumentException();
            Contracts.IThread thread = _target.Contracts.Thread;
            Contracts.ThreadStoreData threadStoreData = thread.GetThreadStoreData();
            data->threadCount = threadStoreData.ThreadCount;
            data->firstThread = threadStoreData.FirstThread.ToClrDataAddress(_target);
            data->finalizerThread = threadStoreData.FinalizerThread.ToClrDataAddress(_target);
            data->gcThread = threadStoreData.GCThread.ToClrDataAddress(_target);

            Contracts.ThreadStoreCounts threadCounts = thread.GetThreadCounts();
            data->unstartedThreadCount = threadCounts.UnstartedThreadCount;
            data->backgroundThreadCount = threadCounts.BackgroundThreadCount;
            data->pendingThreadCount = threadCounts.PendingThreadCount;
            data->deadThreadCount = threadCounts.DeadThreadCount;

            data->fHostConfig = 0; // Always 0 for non-Framework
        }
        catch (global::System.Exception ex)
        {
            hr = ex.HResult;
        }

#if DEBUG
        if (_legacyImpl is not null)
        {
            DacpThreadStoreData dataLocal;
            int hrLocal = _legacyImpl.GetThreadStoreData(&dataLocal);
            Debug.ValidateHResult(hr, hrLocal);
            if (hr == HResults.S_OK)
            {
                Debug.Assert(data->threadCount == dataLocal.threadCount);
                Debug.Assert(data->firstThread == dataLocal.firstThread);
                Debug.Assert(data->finalizerThread == dataLocal.finalizerThread);
                Debug.Assert(data->gcThread == dataLocal.gcThread);
                Debug.Assert(data->unstartedThreadCount == dataLocal.unstartedThreadCount);
                Debug.Assert(data->backgroundThreadCount == dataLocal.backgroundThreadCount);
                Debug.Assert(data->pendingThreadCount == dataLocal.pendingThreadCount);
                Debug.Assert(data->deadThreadCount == dataLocal.deadThreadCount);
                Debug.Assert(data->fHostConfig == dataLocal.fHostConfig);
            }
        }
#endif
        return hr;
    }

    int ISOSDacInterface.GetTLSIndex(uint* pIndex)
    {
        int hr = HResults.S_OK;
        try
        {
            if (pIndex == null)
                throw new ArgumentException();
            uint TlsIndexBase = _target.Read<uint>(_target.ReadGlobalPointer(Constants.Globals.TlsIndexBase));
            uint OffsetOfCurrentThreadInfo = _target.Read<uint>(_target.ReadGlobalPointer(Constants.Globals.OffsetOfCurrentThreadInfo));
            uint CombinedTlsIndex = TlsIndexBase + (OffsetOfCurrentThreadInfo << 16) + 0x80000000;
            *pIndex = CombinedTlsIndex;
        }
        catch (System.Exception ex)
        {
            hr = ex.HResult;
        }
#if DEBUG
        if (_legacyImpl is not null)
        {
            uint indexLocal;
            int hrLocal = _legacyImpl.GetTLSIndex(&indexLocal);
            Debug.ValidateHResult(hr, hrLocal);
            if (hr == HResults.S_OK || hr == HResults.S_FALSE)
            {
                Debug.Assert(*pIndex == indexLocal);
            }
        }
#endif
        return hr;
    }
    int ISOSDacInterface.GetUsefulGlobals(DacpUsefulGlobalsData* data)
    {
        int hr = HResults.S_OK;
        try
        {
            if (data == null)
                throw new ArgumentException();
            data->ArrayMethodTable = _target.ReadPointer(
                _target.ReadGlobalPointer(Constants.Globals.ObjectArrayMethodTable))
                .ToClrDataAddress(_target);
            data->StringMethodTable = _target.ReadPointer(
                _target.ReadGlobalPointer(Constants.Globals.StringMethodTable))
                .ToClrDataAddress(_target);
            data->ObjectMethodTable = _target.ReadPointer(
                _target.ReadGlobalPointer(Constants.Globals.ObjectMethodTable))
                .ToClrDataAddress(_target);
            data->ExceptionMethodTable = _target.ReadPointer(
                _target.ReadGlobalPointer(Constants.Globals.ExceptionMethodTable))
                .ToClrDataAddress(_target);
            data->FreeMethodTable = _target.ReadPointer(
                _target.ReadGlobalPointer(Constants.Globals.FreeObjectMethodTable))
                .ToClrDataAddress(_target);
        }
        catch (System.Exception ex)
        {
            hr = ex.HResult;

            // There are some scenarios where SOS can call GetUsefulGlobals before the globals are initialized,
            // in these cases set the method table pointers to 0 and assert that the legacy DAC returns the same
            // uninitialized values.
            data->ArrayMethodTable = 0;
            data->StringMethodTable = 0;
            data->ObjectMethodTable = 0;
            data->ExceptionMethodTable = 0;
            data->FreeMethodTable = 0;
        }

#if DEBUG
        if (_legacyImpl is not null)
        {
            DacpUsefulGlobalsData dataLocal;
            int hrLocal = _legacyImpl.GetUsefulGlobals(&dataLocal);
            // SOS can call GetUsefulGlobals before the global pointers are initialized.
            // In the DAC, this behavior depends on the compiler.
            // MSVC builds: the DAC global table is a compile time constant and the DAC will return successfully.
            // Clang builds: the DAC global table is constructed at runtime and the DAC will fail.
            // Because of this variation, we cannot match the DAC behavior exactly.
            // As long as the returned data matches, it should be fine.
            if (hr == HResults.S_OK || hrLocal == HResults.S_OK)
            {
                Debug.Assert(data->ArrayMethodTable == dataLocal.ArrayMethodTable);
                Debug.Assert(data->StringMethodTable == dataLocal.StringMethodTable);
                Debug.Assert(data->ObjectMethodTable == dataLocal.ObjectMethodTable);
                Debug.Assert(data->ExceptionMethodTable == dataLocal.ExceptionMethodTable);
                Debug.Assert(data->FreeMethodTable == dataLocal.FreeMethodTable);
            }
        }
#endif
        return hr;
    }

    int ISOSDacInterface.GetWorkRequestData(ClrDataAddress addrWorkRequest, void* data)
    {
        // This API is not implemented by the legacy DAC
        int hr = HResults.E_NOTIMPL;

#if DEBUG
        if (_legacyImpl is not null)
        {
            int hrLocal = _legacyImpl.GetWorkRequestData(addrWorkRequest, data);
            Debug.ValidateHResult(hr, hrLocal);
        }
#endif

        return hr;
    }

#if DEBUG
    internal sealed class TraverseEhInfoExpected
    {
        public TraverseEhInfoExpected(List<DACEHInfo> elements, bool expectAbort, uint? abortIndex = null)
        {
            Elements = elements;
            ExpectAbort = expectAbort;
            AbortIndex = abortIndex;
        }

        public List<DACEHInfo> Elements { get; }
        public bool ExpectAbort { get; }
        public uint? AbortIndex { get; }
        public int CallbackCount { get; set; }
    }

    [UnmanagedCallersOnly]
    private static int TraverseEHInfoCallback(uint clauseIndex, uint totalClauses, DACEHInfo* pEHInfo, void* expectedEhInfo)
    {
        var expected = (TraverseEhInfoExpected)GCHandle.FromIntPtr((nint)expectedEhInfo).Target!;
        Debug.Assert(clauseIndex < totalClauses, $"Invalid clause index {clauseIndex} of {totalClauses}");
        if (clauseIndex < expected.Elements.Count)
        {
            DACEHInfo expectedEhClause = expected.Elements[(int)clauseIndex];
            Debug.Assert(pEHInfo->clauseType == expectedEhClause.clauseType, $"cDAC: {expectedEhClause.clauseType}, DAC: {pEHInfo->clauseType}");
            Debug.Assert(pEHInfo->tryStartOffset == expectedEhClause.tryStartOffset, $"cDAC: {expectedEhClause.tryStartOffset:x}, DAC: {pEHInfo->tryStartOffset:x}");
            Debug.Assert(pEHInfo->tryEndOffset == expectedEhClause.tryEndOffset, $"cDAC: {expectedEhClause.tryEndOffset:x}, DAC: {pEHInfo->tryEndOffset:x}");
            Debug.Assert(pEHInfo->handlerStartOffset == expectedEhClause.handlerStartOffset, $"cDAC: {expectedEhClause.handlerStartOffset:x}, DAC: {pEHInfo->handlerStartOffset:x}");
            Debug.Assert(pEHInfo->handlerEndOffset == expectedEhClause.handlerEndOffset, $"cDAC: {expectedEhClause.handlerEndOffset:x}, DAC: {pEHInfo->handlerEndOffset:x}");
            Debug.Assert(pEHInfo->isDuplicateClause == expectedEhClause.isDuplicateClause, $"cDAC: {expectedEhClause.isDuplicateClause}, DAC: {pEHInfo->isDuplicateClause}");
            Debug.Assert(pEHInfo->filterOffset == expectedEhClause.filterOffset, $"cDAC: {expectedEhClause.filterOffset:x}, DAC: {pEHInfo->filterOffset:x}");
            Debug.Assert(pEHInfo->isCatchAllHandler == expectedEhClause.isCatchAllHandler, $"cDAC: {expectedEhClause.isCatchAllHandler}, DAC: {pEHInfo->isCatchAllHandler}");
            Debug.Assert(pEHInfo->moduleAddr == expectedEhClause.moduleAddr, $"cDAC: {expectedEhClause.moduleAddr:x}, DAC: {pEHInfo->moduleAddr:x}");
            Debug.Assert(pEHInfo->mtCatch == expectedEhClause.mtCatch, $"cDAC: {expectedEhClause.mtCatch:x}, DAC: {pEHInfo->mtCatch:x}");
            Debug.Assert(pEHInfo->tokCatch == expectedEhClause.tokCatch, $"cDAC: {expectedEhClause.tokCatch:x}, DAC: {pEHInfo->tokCatch:x}");
        }
        else
        {
            Debug.Fail($"Received unexpected clause index {clauseIndex} of {totalClauses}");
        }

        expected.CallbackCount++;

        if (expected.ExpectAbort && expected.AbortIndex == clauseIndex)
        {
            return 0; // Return 0 to trigger E_ABORT and stop enumeration
        }
        return 1; // Return non-zero to continue enumeration
    }
#endif
    int ISOSDacInterface.TraverseEHInfo(ClrDataAddress ip, delegate* unmanaged<uint, uint, DACEHInfo*, void*, int> pCallback, void* token)
    {
        int hr = HResults.S_OK;
#if DEBUG
        List<DACEHInfo> clausesLocal = new();
#endif
        int E_ABORT = unchecked((int)0x80004004);
        uint lastIndex = 0;

        try
        {
            if (ip == 0 || pCallback == null)
            {
                throw new ArgumentException();
            }

            IExecutionManager executionManager = _target.Contracts.ExecutionManager;

            CodeBlockHandle? handle = executionManager.GetCodeBlockHandle(ip.ToTargetCodePointer(_target));
            if (handle is not CodeBlockHandle codeBlockHandle)
            {
                throw new ArgumentException();
            }

            List<ExceptionClauseInfo> exceptionClauses = executionManager.GetExceptionClauses(codeBlockHandle);
            uint numClauses = (uint)exceptionClauses.Count;
            for (uint i = 0; i < numClauses; i++)
            {
                ExceptionClauseInfo clause = exceptionClauses[(int)i];
                DACEHInfo ehInfo = default;
                ehInfo.clauseType = clause.ClauseType switch
                {
                    ExceptionClauseInfo.ExceptionClauseFlags.Fault => DACEHInfo.EHClauseType.EHFault,
                    ExceptionClauseInfo.ExceptionClauseFlags.Finally => DACEHInfo.EHClauseType.EHFinally,
                    ExceptionClauseInfo.ExceptionClauseFlags.Filter => DACEHInfo.EHClauseType.EHFilter,
                    ExceptionClauseInfo.ExceptionClauseFlags.Typed => DACEHInfo.EHClauseType.EHTyped,
                    _ => DACEHInfo.EHClauseType.EHUnknown,
                };

                ehInfo.filterOffset = clause.FilterOffset is uint filterOffset ? (ulong)filterOffset : 0;
                ehInfo.isCatchAllHandler = clause.IsCatchAllHandler is true ? Interop.BOOL.TRUE : Interop.BOOL.FALSE;
                ehInfo.tokCatch = clause.ClassToken is uint classToken ? classToken : 0;
                ehInfo.moduleAddr = clause.ModuleAddr is TargetPointer moduleAddr ? moduleAddr.ToClrDataAddress(_target) : 0;
                ehInfo.mtCatch = clause.TypeHandle is TargetNUInt th ? new TargetPointer(th.Value).ToClrDataAddress(_target) : 0;

                ehInfo.tryStartOffset = (ulong)clause.TryStartPC;
                ehInfo.tryEndOffset = (ulong)clause.TryEndPC;
                ehInfo.handlerStartOffset = (ulong)clause.HandlerStartPC;
                ehInfo.handlerEndOffset = (ulong)clause.HandlerEndPC;
#if DEBUG
                clausesLocal.Add(ehInfo);
#endif
                if (pCallback(i, numClauses, &ehInfo, token) == 0)
                {
                    lastIndex = i;
                    throw Marshal.GetExceptionForHR(E_ABORT)!;
                }
            }
        }
        catch (System.Exception ex)
        {
            hr = ex.HResult;
        }

#if DEBUG
        if (_legacyImpl is not null)
        {
            TraverseEhInfoExpected expected = new(clausesLocal, hr == E_ABORT, hr == E_ABORT ? lastIndex : null);
            GCHandle expectedHandle = GCHandle.Alloc(expected);
            try
            {
                void* tokenDebug = GCHandle.ToIntPtr(expectedHandle).ToPointer();
                delegate* unmanaged<uint, uint, DACEHInfo*, void*, int> callbackDebugPtr = &TraverseEHInfoCallback;

                int hrLocal = _legacyImpl.TraverseEHInfo(ip, callbackDebugPtr, tokenDebug);
                Debug.ValidateHResult(hr, hrLocal, HResultValidationMode.Exact);
            }
            finally
            {
                expectedHandle.Free();
            }
        }
#endif
        return hr;
    }

#if DEBUG
    [ThreadStatic]
    private static List<(ulong VirtualAddress, nuint VirtualSize)>? _debugTraverseLoaderHeapBlocks;
    [ThreadStatic]
    private static uint _debugTraverseLoaderDebugCount;

    private static List<(ulong VirtualAddress, nuint VirtualSize)> DebugTraverseLoaderHeapBlocks
        => _debugTraverseLoaderHeapBlocks ??= new();

    [UnmanagedCallersOnly]
    private static void TraverseLoaderHeapDebugCallback(ulong virtualAddress, nuint virtualSize, Interop.BOOL _)
    {
        List<(ulong VirtualAddress, nuint VirtualSize)> expected = DebugTraverseLoaderHeapBlocks;
        bool found = expected.Remove((virtualAddress, virtualSize));
        _debugTraverseLoaderDebugCount++;
        Debug.Assert(found, $"Unexpected loader heap block: address={virtualAddress:x}, size={virtualSize:x}");
    }
#endif

    private int TraverseLoaderHeapCore(TargetPointer loaderHeapAddr, delegate* unmanaged<ulong, nuint, Interop.BOOL, void> pCallback)
    {
        int hr = HResults.S_OK;
#if DEBUG
        DebugTraverseLoaderHeapBlocks.Clear();
        _debugTraverseLoaderDebugCount = 0;
#endif
        try
        {
            if (loaderHeapAddr == TargetPointer.Null || pCallback is null)
                throw new ArgumentException();
            int iterationMax = 8192;

            Contracts.ILoader loader = _target.Contracts.Loader;
            TargetPointer block = loader.GetFirstLoaderHeapBlock(loaderHeapAddr);
            TargetPointer firstBlock = block;
            int i = 0;
            while (block != TargetPointer.Null && i++ < iterationMax)
            {
                Contracts.LoaderHeapBlockData blockData;
                try
                {
                    blockData = loader.GetLoaderHeapBlockData(block);
                }
                catch (VirtualReadException)
                {
                    throw new NullReferenceException();
                }
                pCallback(blockData.Address.Value, (nuint)blockData.Size.Value, block == firstBlock ? Interop.BOOL.TRUE : Interop.BOOL.FALSE);
#if DEBUG
                DebugTraverseLoaderHeapBlocks.Add((blockData.Address.Value, (nuint)blockData.Size.Value));
#endif
                block = blockData.NextBlock;
                if (block == firstBlock)
                    throw new NullReferenceException();
            }
            if (i >= iterationMax)
                hr = HResults.S_FALSE;
        }
        catch (System.Exception ex)
        {
            hr = ex.HResult;
        }
        return hr;
    }

    int ISOSDacInterface.TraverseLoaderHeap(ClrDataAddress loaderHeapAddr, delegate* unmanaged<ulong, nuint, Interop.BOOL, void> pCallback)
    {
        int hr = TraverseLoaderHeapCore(loaderHeapAddr.ToTargetPointer(_target), pCallback);
#if DEBUG
        if (_legacyImpl is not null)
        {
            int cdacCount = DebugTraverseLoaderHeapBlocks.Count;
            delegate* unmanaged<ulong, nuint, Interop.BOOL, void> debugCallbackPtr = &TraverseLoaderHeapDebugCallback;
            int hrLocal = _legacyImpl.TraverseLoaderHeap(loaderHeapAddr, debugCallbackPtr);
            Debug.ValidateHResult(hr, hrLocal);
            if (hr == HResults.S_OK || hr == HResults.S_FALSE)
            {
                Debug.Assert(DebugTraverseLoaderHeapBlocks.Count == 0,
                    $"cDAC found {cdacCount} blocks, DAC matched {_debugTraverseLoaderDebugCount}, {DebugTraverseLoaderHeapBlocks.Count} unmatched");
                Debug.Assert(_debugTraverseLoaderDebugCount == (uint)cdacCount,
                    $"cDAC: {cdacCount} blocks, DAC: {_debugTraverseLoaderDebugCount} blocks");
            }
        }
#endif
        return hr;
    }

#if DEBUG
    [UnmanagedCallersOnly]
    private static void TraverseModuleMapCallback(uint index, ulong moduleAddr, void* expectedElements)
    {
        var expectedElementsDict = (Dictionary<ulong, uint>)GCHandle.FromIntPtr((nint)expectedElements).Target!;
        if (expectedElementsDict.TryGetValue(moduleAddr, out uint expectedIndex) && expectedIndex == index)
        {
            expectedElementsDict[default]++; // Increment the count for verification
        }
        else
        {
            Debug.Assert(false, $"Unexpected module address {moduleAddr:x} at index {index}");
        }
    }
#endif
    int ISOSDacInterface.TraverseModuleMap(ModuleMapType mmt, ClrDataAddress moduleAddr, delegate* unmanaged<uint, ulong, void*, void> pCallback, void* token)
    {
        int hr = HResults.S_OK;
        IEnumerable<(TargetPointer Address, uint Index)> elements = Enumerable.Empty<(TargetPointer, uint)>();
        try
        {
            if (moduleAddr == 0)
                throw new ArgumentException();

            Contracts.ILoader loader = _target.Contracts.Loader;
            TargetPointer moduleAddrPtr = moduleAddr.ToTargetPointer(_target);
            Contracts.ModuleHandle moduleHandle = loader.GetModuleHandleFromModulePtr(moduleAddrPtr);
            Contracts.ModuleLookupTables lookupTables = loader.GetLookupTables(moduleHandle);
            switch (mmt)
            {
                case ModuleMapType.TYPEDEFTOMETHODTABLE:
                    elements = loader.EnumerateModuleLookupMap(lookupTables.TypeDefToMethodTable);
                    break;
                case ModuleMapType.TYPEREFTOMETHODTABLE:
                    elements = loader.EnumerateModuleLookupMap(lookupTables.TypeRefToMethodTable);
                    break;
                default:
                    throw new ArgumentException();
            }
            foreach ((TargetPointer element, uint index) in elements)
            {
                // Call the callback with each element
                pCallback(index, element.ToClrDataAddress(_target).Value, token);
            }
        }
        catch (System.Exception ex)
        {
            hr = ex.HResult;
        }
#if DEBUG
        if (_legacyImpl is not null)
        {
            Dictionary<ulong, uint> expectedElements = elements.ToDictionary(tuple => tuple.Address.ToClrDataAddress(_target).Value, tuple => tuple.Index);
            expectedElements.Add(default, 0);
            void* tokenDebug = GCHandle.ToIntPtr(GCHandle.Alloc(expectedElements)).ToPointer();
            delegate* unmanaged<uint, ulong, void*, void> callbackDebugPtr = &TraverseModuleMapCallback;

            int hrLocal = _legacyImpl.TraverseModuleMap(mmt, moduleAddr, callbackDebugPtr, tokenDebug);
            Debug.ValidateHResult(hr, hrLocal);
            Debug.Assert(expectedElements[default] == elements.Count(), $"cDAC: {elements.Count()} elements, DAC: {expectedElements[default]} elements");
            GCHandle.FromIntPtr((nint)tokenDebug).Free();
        }
#endif
        return hr;
    }
#if DEBUG
    [UnmanagedCallersOnly]
    private static Interop.BOOL TraverseRCWCleanupListCallback(ulong rcwAddr, ulong ctx, ulong staThread, Interop.BOOL isFreeThreaded, void* expectedElements)
    {
        var expectedElementsDict = (Dictionary<ulong, ulong>)GCHandle.FromIntPtr((nint)expectedElements).Target!;
        if (expectedElementsDict.TryGetValue(rcwAddr, out ulong expectedCtx) && expectedCtx == ctx)
        {
            expectedElementsDict[default]++; // Increment the count for verification
        }
        else
        {
            Debug.Fail($"Unexpected RCW address {rcwAddr:x} or context {ctx:x}");
        }
        return Interop.BOOL.TRUE;
    }
#endif
    int ISOSDacInterface.TraverseRCWCleanupList(ClrDataAddress cleanupListPtr, delegate* unmanaged<ulong, ulong, ulong, Interop.BOOL, void*, Interop.BOOL> pCallback, void* token)
    {
        int hr = HResults.S_OK;
        IEnumerable<Contracts.RCWCleanupInfo> cleanupInfos = Enumerable.Empty<Contracts.RCWCleanupInfo>();
        try
        {
            if (pCallback is null)
                throw new ArgumentException();

            Contracts.IBuiltInCOM contract = _target.Contracts.BuiltInCOM; // E_NOTIMPL if not defined (non-Windows)
            TargetPointer listPtr = cleanupListPtr.ToTargetPointer(_target);

            cleanupInfos = contract.GetRCWCleanupList(listPtr);
            foreach (Contracts.RCWCleanupInfo info in cleanupInfos)
            {
                pCallback(
                    info.RCW.ToClrDataAddress(_target).Value,
                    info.Context.ToClrDataAddress(_target).Value,
                    info.STAThread.ToClrDataAddress(_target).Value,
                    info.IsFreeThreaded ? Interop.BOOL.TRUE : Interop.BOOL.FALSE,
                    token);
            }
        }
        catch (System.Exception ex)
        {
            hr = ex.HResult;
        }
#if DEBUG
        if (_legacyImpl is not null)
        {
            Dictionary<ulong, ulong> expectedElements = cleanupInfos.ToDictionary(info => info.RCW.ToClrDataAddress(_target).Value, info => info.Context.ToClrDataAddress(_target).Value);
            expectedElements.Add(default, 0);
            GCHandle expectedElementsHandle = GCHandle.Alloc(expectedElements);
            void* tokenDebug = GCHandle.ToIntPtr(expectedElementsHandle).ToPointer();
            delegate* unmanaged<ulong, ulong, ulong, Interop.BOOL, void*, Interop.BOOL> callbackDebugPtr = &TraverseRCWCleanupListCallback;

            int hrLocal = _legacyImpl.TraverseRCWCleanupList(cleanupListPtr, callbackDebugPtr, tokenDebug);
            Debug.ValidateHResult(hr, hrLocal);
            Debug.Assert(expectedElements[default] == (ulong)cleanupInfos.Count(), $"cDAC: {cleanupInfos.Count()} elements, DAC: {expectedElements[default]} elements");
            expectedElementsHandle.Free();
        }
#endif
        return hr;
    }
    int ISOSDacInterface.TraverseVirtCallStubHeap(ClrDataAddress pAppDomain, VCSHeapType heaptype, delegate* unmanaged<ulong, nuint, Interop.BOOL, void> pCallback)
    {
        int hr = HResults.S_OK;
        try
        {
            // Native DAC only validates pAppDomain here; traversal always uses the global loader allocator.
            if (pAppDomain == 0 || pCallback is null)
                throw new ArgumentException();

            Contracts.ILoader loader = _target.Contracts.Loader;
            TargetPointer globalLoaderAllocator = loader.GetGlobalLoaderAllocator();
            IReadOnlyDictionary<Contracts.LoaderAllocatorHeapType, TargetPointer> heaps = loader.GetLoaderAllocatorHeaps(globalLoaderAllocator);

            if (!heaps.ContainsKey(Contracts.LoaderAllocatorHeapType.IndcellHeap))
                throw new NullReferenceException();

            Contracts.LoaderAllocatorHeapType heapKey = heaptype switch
            {
                VCSHeapType.IndcellHeap => Contracts.LoaderAllocatorHeapType.IndcellHeap,
                VCSHeapType.CacheEntryHeap => Contracts.LoaderAllocatorHeapType.CacheEntryHeap,
                _ => throw new ArgumentException(),
            };

            if (heaps.TryGetValue(heapKey, out TargetPointer heap) && heap != TargetPointer.Null)
            {
                hr = TraverseLoaderHeapCore(heap, pCallback);
            }
        }
        catch (System.Exception ex)
        {
            hr = ex.HResult;
        }

#if DEBUG
        if (_legacyImpl is not null)
        {
            int cdacCount = DebugTraverseLoaderHeapBlocks.Count;
            delegate* unmanaged<ulong, nuint, Interop.BOOL, void> debugCallbackPtr = &TraverseLoaderHeapDebugCallback;
            int hrLocal = _legacyImpl.TraverseVirtCallStubHeap(pAppDomain, heaptype, debugCallbackPtr);
            Debug.ValidateHResult(hr, hrLocal);
            if (hr == HResults.S_OK || hr == HResults.S_FALSE)
            {
                Debug.Assert(DebugTraverseLoaderHeapBlocks.Count == 0,
                    $"cDAC found {cdacCount} blocks, DAC matched {_debugTraverseLoaderDebugCount}, {DebugTraverseLoaderHeapBlocks.Count} unmatched");
                Debug.Assert(_debugTraverseLoaderDebugCount == (uint)cdacCount,
                    $"cDAC: {cdacCount} blocks, DAC: {_debugTraverseLoaderDebugCount} blocks");
            }
        }
#endif
        return hr;
    }
#endregion ISOSDacInterface

    #region ISOSDacInterface2
    int ISOSDacInterface2.GetObjectExceptionData(ClrDataAddress objectAddress, DacpExceptionObjectData* data)
    {
        try
        {
            Contracts.IException contract = _target.Contracts.Exception;
            Contracts.ExceptionData exceptionData = contract.GetExceptionData(objectAddress.ToTargetPointer(_target));
            data->Message = exceptionData.Message.ToClrDataAddress(_target);
            data->InnerException = exceptionData.InnerException.ToClrDataAddress(_target);
            data->StackTrace = exceptionData.StackTrace.ToClrDataAddress(_target);
            data->WatsonBuckets = exceptionData.WatsonBuckets.ToClrDataAddress(_target);
            data->StackTraceString = exceptionData.StackTraceString.ToClrDataAddress(_target);
            data->RemoteStackTraceString = exceptionData.RemoteStackTraceString.ToClrDataAddress(_target);
            data->HResult = exceptionData.HResult;
            data->XCode = exceptionData.XCode;
        }
        catch (System.Exception ex)
        {
            return ex.HResult;
        }

        return HResults.S_OK;
    }

    int ISOSDacInterface2.IsRCWDCOMProxy(ClrDataAddress rcwAddress, int* inDCOMProxy)
    {
        int hr = HResults.S_OK;
        try
        {
            if (inDCOMProxy == null)
                throw new NullReferenceException(); // HResults.E_POINTER;

            *inDCOMProxy = (int)Interop.BOOL.FALSE;

            if (_target.ReadGlobal<byte>(Constants.Globals.FeatureCOMInterop) == 0)
            {
                hr = HResults.E_NOTIMPL;
            }
        }
        catch (System.Exception ex)
        {
            hr = ex.HResult;
        }

#if DEBUG
        if (_legacyImpl2 is not null)
        {
            int inDCOMProxyLocal;
            int hrLocal = _legacyImpl2.IsRCWDCOMProxy(rcwAddress, &inDCOMProxyLocal);
            Debug.ValidateHResult(hr, hrLocal);
            if (hr == HResults.S_OK)
            {
                Debug.Assert(*inDCOMProxy == inDCOMProxyLocal);
            }
        }
#endif
        return hr;
    }
    #endregion ISOSDacInterface2

    #region ISOSDacInterface3
    int ISOSDacInterface3.GetGCInterestingInfoData(ClrDataAddress interestingInfoAddr, DacpGCInterestingInfoData* data)
    {
        int hr = HResults.S_OK;

        try
        {
            if (interestingInfoAddr == 0 || data == null)
                throw new ArgumentException();

            IGC gc = _target.Contracts.GC;
            string[] gcIdentifiers = gc.GetGCIdentifiers();

            // doesn't make sense to call this on WKS mode
            if (!gcIdentifiers.Contains(GCIdentifiers.Server))
                throw Marshal.GetExceptionForHR(HResults.E_FAIL)!;

            // For server GC, use GetHeapData(TargetPointer heap)
            GCHeapData heapData = gc.GetHeapData(interestingInfoAddr.ToTargetPointer(_target));

            PopulateGCInterestingInfoData(heapData, data);
        }
        catch (System.Exception ex)
        {
            hr = ex.HResult;
        }

#if DEBUG
        if (_legacyImpl3 is not null)
        {
            DacpGCInterestingInfoData dataLocal = default;
            int hrLocal = _legacyImpl3.GetGCInterestingInfoData(interestingInfoAddr, &dataLocal);
            Debug.ValidateHResult(hr, hrLocal);
            if (hr == HResults.S_OK)
            {
                VerifyGCInterestingInfoData(data, &dataLocal);
            }
        }
#endif

        return hr;
    }

    int ISOSDacInterface3.GetGCInterestingInfoStaticData(DacpGCInterestingInfoData* data)
    {
        int hr = HResults.S_OK;

        try
        {
            if (data == null)
                throw new ArgumentException();

            IGC gc = _target.Contracts.GC;
            string[] gcIdentifiers = gc.GetGCIdentifiers();

            // doesn't make sense to call this on SVR mode
            if (!gcIdentifiers.Contains(GCIdentifiers.Workstation))
                throw Marshal.GetExceptionForHR(HResults.E_FAIL)!;

            // For workstation GC, use GetHeapData()
            GCHeapData heapData = gc.GetHeapData();

            PopulateGCInterestingInfoData(heapData, data);
        }
        catch (System.Exception ex)
        {
            hr = ex.HResult;
        }

#if DEBUG
        if (_legacyImpl3 is not null)
        {
            DacpGCInterestingInfoData dataLocal = default;
            int hrLocal = _legacyImpl3.GetGCInterestingInfoStaticData(&dataLocal);
            Debug.ValidateHResult(hr, hrLocal);
            if (hr == HResults.S_OK)
            {
                VerifyGCInterestingInfoData(data, &dataLocal);
            }
        }
#endif

        return hr;
    }

    private static void PopulateGCInterestingInfoData(GCHeapData heapData, DacpGCInterestingInfoData* data)
    {
        *data = default;

        // The DacpGCInterestingInfoData struct hardcodes platform sized ints.
        // This is problematic for new cross-bit scenarios.
        // If the target platform is 64-bits but the cDAC/SOS is 32-bits, the values will be truncated.

        // Copy interesting data points
        for (int i = 0; i < Math.Min(GCConstants.DAC_NUM_GC_DATA_POINTS, heapData.InterestingData.Count); i++)
            data->interestingDataPoints[i] = (nuint)heapData.InterestingData[i].Value;

        // Copy compact reasons
        for (int i = 0; i < Math.Min(GCConstants.DAC_MAX_COMPACT_REASONS_COUNT, heapData.CompactReasons.Count); i++)
            data->compactReasons[i] = (nuint)heapData.CompactReasons[i].Value;

        // Copy expand mechanisms
        for (int i = 0; i < Math.Min(GCConstants.DAC_MAX_EXPAND_MECHANISMS_COUNT, heapData.ExpandMechanisms.Count); i++)
            data->expandMechanisms[i] = (nuint)heapData.ExpandMechanisms[i].Value;

        // Copy interesting mechanism bits
        for (int i = 0; i < Math.Min(GCConstants.DAC_MAX_GC_MECHANISM_BITS_COUNT, heapData.InterestingMechanismBits.Count); i++)
            data->bitMechanisms[i] = (nuint)heapData.InterestingMechanismBits[i].Value;
    }

#if DEBUG
    private static void VerifyGCInterestingInfoData(DacpGCInterestingInfoData* cdacData, DacpGCInterestingInfoData* legacyData)
    {
        // Compare interesting data points array
        for (int i = 0; i < GCConstants.DAC_NUM_GC_DATA_POINTS; i++)
        {
            Debug.Assert(cdacData->interestingDataPoints[i] == legacyData->interestingDataPoints[i],
                $"interestingDataPoints[{i}] - cDAC: {cdacData->interestingDataPoints[i]}, DAC: {legacyData->interestingDataPoints[i]}");
        }

        // Compare compact reasons array
        for (int i = 0; i < GCConstants.DAC_MAX_COMPACT_REASONS_COUNT; i++)
        {
            Debug.Assert(cdacData->compactReasons[i] == legacyData->compactReasons[i],
                $"compactReasons[{i}] - cDAC: {cdacData->compactReasons[i]}, DAC: {legacyData->compactReasons[i]}");
        }

        // Compare expand mechanisms array
        for (int i = 0; i < GCConstants.DAC_MAX_EXPAND_MECHANISMS_COUNT; i++)
        {
            Debug.Assert(cdacData->expandMechanisms[i] == legacyData->expandMechanisms[i],
                $"expandMechanisms[{i}] - cDAC: {cdacData->expandMechanisms[i]}, DAC: {legacyData->expandMechanisms[i]}");
        }

        // Compare bit mechanisms array
        for (int i = 0; i < GCConstants.DAC_MAX_GC_MECHANISM_BITS_COUNT; i++)
        {
            Debug.Assert(cdacData->bitMechanisms[i] == legacyData->bitMechanisms[i],
                $"bitMechanisms[{i}] - cDAC: {cdacData->bitMechanisms[i]}, DAC: {legacyData->bitMechanisms[i]}");
        }

        // Compare global mechanisms array
        for (int i = 0; i < GCConstants.DAC_MAX_GLOBAL_GC_MECHANISMS_COUNT; i++)
        {
            Debug.Assert(cdacData->globalMechanisms[i] == legacyData->globalMechanisms[i],
                $"globalMechanisms[{i}] - cDAC: {cdacData->globalMechanisms[i]}, DAC: {legacyData->globalMechanisms[i]}");
        }
    }
#endif

    int ISOSDacInterface3.GetGCGlobalMechanisms(nuint* globalMechanisms)
    {
        int hr = HResults.S_OK;
        try
        {
            if (globalMechanisms == null)
                throw new ArgumentException();

            IGC gc = _target.Contracts.GC;
            IReadOnlyList<TargetNUInt> globalMechanismsData = gc.GetGlobalMechanisms();

            // Clear the array
            for (int i = 0; i < GCConstants.DAC_MAX_GLOBAL_GC_MECHANISMS_COUNT; i++)
                globalMechanisms[i] = 0;

            // Copy global mechanisms data
            for (int i = 0; i < Math.Min(GCConstants.DAC_MAX_GLOBAL_GC_MECHANISMS_COUNT, globalMechanismsData.Count); i++)
            {
                // This API hardcodes platform sized ints in the struct
                // This is problematic for new cross-bit scenarios.
                // If the target platform is 64-bits but the cDAC/SOS is 32-bits, the values will be truncated.
                globalMechanisms[i] = (nuint)globalMechanismsData[i].Value;
            }
        }
        catch (System.Exception ex)
        {
            hr = ex.HResult;
        }

#if DEBUG
        if (_legacyImpl3 is not null)
        {
            nuint[] globalMechanismsLocal = new nuint[GCConstants.DAC_MAX_GLOBAL_GC_MECHANISMS_COUNT];
            fixed (nuint* pLocal = globalMechanismsLocal)
            {
                int hrLocal = _legacyImpl3.GetGCGlobalMechanisms(pLocal);
                Debug.ValidateHResult(hr, hrLocal);
                if (hr == HResults.S_OK)
                {
                    for (int i = 0; i < GCConstants.DAC_MAX_GLOBAL_GC_MECHANISMS_COUNT; i++)
                    {
                        Debug.Assert(globalMechanisms[i] == globalMechanismsLocal[i],
                            $"globalMechanisms[{i}] - cDAC: {globalMechanisms[i]}, DAC: {globalMechanismsLocal[i]}");
                    }
                }
            }
        }
#endif

        return hr;
    }
    #endregion ISOSDacInterface3

    #region ISOSDacInterface4
    int ISOSDacInterface4.GetClrNotification(ClrDataAddress[] arguments, int count, int* pNeeded)
    {
        int hr = HResults.S_OK;
        uint MaxClrNotificationArgs = _target.ReadGlobal<uint>(Constants.Globals.MaxClrNotificationArgs);
        try
        {
            *pNeeded = (int)MaxClrNotificationArgs;
            TargetPointer basePtr = _target.ReadGlobalPointer(Constants.Globals.ClrNotificationArguments);
            if (_target.ReadNUInt(basePtr).Value == 0)
                throw Marshal.GetExceptionForHR(HResults.E_FAIL)!;

            for (int i = 0; i < count && i < MaxClrNotificationArgs; i++)
            {
                arguments[i] = _target.ReadNUInt(basePtr.Value + (ulong)(i * _target.PointerSize)).Value;
            }
        }
        catch (System.Exception ex)
        {
            hr = ex.HResult;
        }
#if DEBUG
        if (_legacyImpl4 is not null)
        {
            ClrDataAddress[] argumentsLocal = new ClrDataAddress[count];
            int neededLocal;
            int hrLocal = _legacyImpl4.GetClrNotification(argumentsLocal, count, &neededLocal);
            Debug.ValidateHResult(hr, hrLocal);
            if (hr == HResults.S_OK)
            {
                Debug.Assert(*pNeeded == neededLocal);
                for (int i = 0; i < count && i < MaxClrNotificationArgs; i++)
                {
                    Debug.Assert(arguments[i] == argumentsLocal[i]);
                }
            }
        }
#endif
        return hr;
    }
    #endregion ISOSDacInterface4

    #region ISOSDacInterface5
    int ISOSDacInterface5.GetTieredVersions(
        ClrDataAddress methodDesc,
        int rejitId,
        [In, MarshalUsing(CountElementName = nameof(cNativeCodeAddrs)), Out] DacpTieredVersionData[]? nativeCodeAddrs,
        int cNativeCodeAddrs,
        int* pcNativeCodeAddrs)
    {
        int hr = HResults.S_OK;
        try
        {
            if (methodDesc == 0 || cNativeCodeAddrs == 0 || pcNativeCodeAddrs == null || nativeCodeAddrs is null)
            {
                throw new ArgumentException();
            }

            *pcNativeCodeAddrs = 0;

            ILoader loader = _target.Contracts.Loader;
            ICodeVersions codeVersions = _target.Contracts.CodeVersions;
            IReJIT rejitContract = _target.Contracts.ReJIT;
            TargetPointer methodDescPtr = methodDesc.ToTargetPointer(_target);
            ILCodeVersionHandle ilCodeVersionHandle = codeVersions.GetILCodeVersions(methodDescPtr)
                .FirstOrDefault(ilcode => rejitContract.GetRejitId(ilcode).Value == (ulong)rejitId, ILCodeVersionHandle.Invalid);

            if (!ilCodeVersionHandle.IsValid)
                throw new ArgumentException();

            IRuntimeTypeSystem runtimeTypeSystemContract = _target.Contracts.RuntimeTypeSystem;
            MethodDescHandle methodDescHandle = runtimeTypeSystemContract.GetMethodDescHandle(methodDescPtr);
            TargetPointer modulePtr = runtimeTypeSystemContract.GetModule(runtimeTypeSystemContract.GetTypeHandle(runtimeTypeSystemContract.GetMethodTable(methodDescHandle)));
            Contracts.ModuleHandle moduleHandle = loader.GetModuleHandleFromModulePtr(modulePtr);

            TargetPointer r2rImageBase = TargetPointer.Null;
            TargetPointer r2rImageEnd = TargetPointer.Null;
            if (loader.IsReadyToRun(moduleHandle)
                && loader.TryGetLoadedImageContents(moduleHandle, out r2rImageBase, out uint r2rSize, out _))
            {
                r2rImageEnd = r2rImageBase + r2rSize;
            }

            bool isEligibleForTieredCompilation = runtimeTypeSystemContract.IsEligibleForTieredCompilation(methodDescHandle);

            int count = 0;
            foreach (NativeCodeVersionHandle nativeCodeVersionHandle in codeVersions.GetNativeCodeVersions(methodDescPtr, ilCodeVersionHandle))
            {
                TargetCodePointer nativeCode = _target.Contracts.PrecodeStubs.GetInterpreterCodeFromInterpreterPrecodeIfPresent(codeVersions.GetNativeCode(nativeCodeVersionHandle));
                TargetPointer nativeCodeAddr = nativeCode.ToAddress(_target);
                nativeCodeAddrs[count].nativeCodeAddr = nativeCodeAddr.ToClrDataAddress(_target);
                nativeCodeAddrs[count].nativeCodeVersionNodePtr = nativeCodeVersionHandle.CodeVersionNodeAddress.ToClrDataAddress(_target);

                if (r2rImageBase <= nativeCodeAddr && nativeCodeAddr < r2rImageEnd)
                {
                    nativeCodeAddrs[count].optimizationTier = DacpTieredVersionData.OptimizationTier.ReadyToRun;
                }
                else if (isEligibleForTieredCompilation)
                {
                    switch (codeVersions.GetOptimizationTier(nativeCodeVersionHandle))
                    {
                        default:
                            nativeCodeAddrs[count].optimizationTier = DacpTieredVersionData.OptimizationTier.Unknown;
                            break;
                        case OptimizationTier.OptimizationTier0:
                            nativeCodeAddrs[count].optimizationTier = DacpTieredVersionData.OptimizationTier.QuickJitted;
                            break;
                        case OptimizationTier.OptimizationTier1:
                            nativeCodeAddrs[count].optimizationTier = DacpTieredVersionData.OptimizationTier.OptimizedTier1;
                            break;
                        case OptimizationTier.OptimizationTier1OSR:
                            nativeCodeAddrs[count].optimizationTier = DacpTieredVersionData.OptimizationTier.OptimizedTier1OSR;
                            break;
                        case OptimizationTier.OptimizationTierOptimized:
                            nativeCodeAddrs[count].optimizationTier = DacpTieredVersionData.OptimizationTier.Optimized;
                            break;
                        case OptimizationTier.OptimizationTier0Instrumented:
                            nativeCodeAddrs[count].optimizationTier = DacpTieredVersionData.OptimizationTier.QuickJittedInstrumented;
                            break;
                        case OptimizationTier.OptimizationTier1Instrumented:
                            nativeCodeAddrs[count].optimizationTier = DacpTieredVersionData.OptimizationTier.OptimizedTier1Instrumented;
                            break;
                    }
                }
                else
                {
                    nativeCodeAddrs[count].optimizationTier = DacpTieredVersionData.OptimizationTier.Unknown;
                }

                count++;

                if (count >= cNativeCodeAddrs)
                {
                    hr = HResults.S_FALSE;
                    break;
                }
            }

            *pcNativeCodeAddrs = count;
        }
        catch (NotImplementedException)
        {
            // ReJIT contract not available — feature not active in the target runtime
            return HResults.S_OK;
        }
        catch (System.Exception ex)
        {
            hr = ex.HResult;
        }
#if DEBUG
        if (_legacyImpl5 is not null)
        {
            var legacyBuffer = new DacpTieredVersionData[cNativeCodeAddrs];
            int legacyCount;
            int hrLocal = _legacyImpl5.GetTieredVersions(methodDesc, rejitId, legacyBuffer, cNativeCodeAddrs, &legacyCount);
            Debug.ValidateHResult(hr, hrLocal);
            if (hr == HResults.S_OK || hr == HResults.S_FALSE)
            {
                Debug.Assert(*pcNativeCodeAddrs == legacyCount, $"cDAC count: {*pcNativeCodeAddrs}, DAC count: {legacyCount}");
                if (nativeCodeAddrs is not null)
                {
                    for (int i = 0; i < *pcNativeCodeAddrs; i++)
                    {
                        Debug.Assert(nativeCodeAddrs[i].nativeCodeAddr == legacyBuffer[i].nativeCodeAddr,
                            $"[{i}] cDAC nativeCodeAddr: 0x{(ulong)nativeCodeAddrs[i].nativeCodeAddr:x}, DAC: 0x{(ulong)legacyBuffer[i].nativeCodeAddr:x}");
                        Debug.Assert(nativeCodeAddrs[i].nativeCodeVersionNodePtr == legacyBuffer[i].nativeCodeVersionNodePtr,
                            $"[{i}] cDAC nodePtr: 0x{(ulong)nativeCodeAddrs[i].nativeCodeVersionNodePtr:x}, DAC: 0x{(ulong)legacyBuffer[i].nativeCodeVersionNodePtr:x}");
                        Debug.Assert(nativeCodeAddrs[i].optimizationTier == legacyBuffer[i].optimizationTier,
                            $"[{i}] cDAC tier: {nativeCodeAddrs[i].optimizationTier}, DAC: {legacyBuffer[i].optimizationTier}");
                    }
                }
            }
        }
#endif // DEBUG
        return hr;
    }
    #endregion ISOSDacInterface5

    #region ISOSDacInterface6
    int ISOSDacInterface6.GetMethodTableCollectibleData(ClrDataAddress mt, DacpMethodTableCollectibleData* data)
    {
        int hr = HResults.S_OK;
        try
        {
            if (mt == 0 || data == null)
                throw new ArgumentException();

            Contracts.IRuntimeTypeSystem rtsContract = _target.Contracts.RuntimeTypeSystem;
            ILoader loaderContract = _target.Contracts.Loader;
            Contracts.TypeHandle typeHandle = rtsContract.GetTypeHandle(mt.ToTargetPointer(_target));

            bool isCollectible = rtsContract.IsCollectible(typeHandle);
            if (isCollectible)
            {
                TargetPointer modulePtr = rtsContract.GetLoaderModule(typeHandle);
                Contracts.ModuleHandle moduleHandle = loaderContract.GetModuleHandleFromModulePtr(modulePtr);
                TargetPointer loaderAllocator = loaderContract.GetLoaderAllocator(moduleHandle);
                TargetPointer loaderAllocatorHandle = loaderContract.GetObjectHandle(loaderAllocator);
                data->LoaderAllocatorObjectHandle = loaderAllocatorHandle.ToClrDataAddress(_target);
            }
            data->bCollectible = isCollectible ? 1 : 0;
        }
        catch (System.Exception ex)
        {
            hr = ex.HResult;
        }
#if DEBUG
        if (_legacyImpl6 is not null)
        {
            DacpMethodTableCollectibleData dataLocal;
            int hrLocal = _legacyImpl6.GetMethodTableCollectibleData(mt, &dataLocal);
            Debug.ValidateHResult(hr, hrLocal);
            if (hr == HResults.S_OK)
            {
                Debug.Assert((data->bCollectible == 0) == (dataLocal.bCollectible == 0), $"cDAC: {data->bCollectible}, DAC: {dataLocal.bCollectible}");
                Debug.Assert(data->LoaderAllocatorObjectHandle == dataLocal.LoaderAllocatorObjectHandle, $"cDAC: {data->LoaderAllocatorObjectHandle:x}, DAC: {dataLocal.LoaderAllocatorObjectHandle:x}");
            }
        }
#endif
        return hr;
    }
    #endregion ISOSDacInterface6

    #region ISOSDacInterface7
    int ISOSDacInterface7.GetPendingReJITID(ClrDataAddress methodDesc, int* pRejitId)
    {
        int hr = HResults.S_OK;
        try
        {
            if (methodDesc == 0 || pRejitId == null)
                throw new ArgumentException();
            Contracts.IReJIT rejitContract = _target.Contracts.ReJIT;
            Contracts.ICodeVersions codeVersionsContract = _target.Contracts.CodeVersions;
            TargetPointer methodDescPtr = methodDesc.ToTargetPointer(_target);
            Contracts.ILCodeVersionHandle activeILCodeVersion = codeVersionsContract.GetActiveILCodeVersion(methodDescPtr);

            if (rejitContract.GetRejitState(activeILCodeVersion) == Contracts.RejitState.Requested)
            {
                *pRejitId = (int)rejitContract.GetRejitId(activeILCodeVersion).Value;
            }
            else
            {
                hr = HResults.S_FALSE;
            }
        }
        catch (System.Exception ex)
        {
            hr = ex.HResult;
        }
#if DEBUG
        if (_legacyImpl7 is not null)
        {
            int rejitIdLocal;
            int hrLocal = _legacyImpl7.GetPendingReJITID(methodDesc, &rejitIdLocal);
            Debug.ValidateHResult(hr, hrLocal);
            if (hr == HResults.S_OK)
            {
                Debug.Assert(*pRejitId == rejitIdLocal);
            }
        }

#endif
        return hr;
    }
    int ISOSDacInterface7.GetReJITInformation(ClrDataAddress methodDesc, int rejitId, DacpReJitData2* pRejitData)
    {
        int hr = HResults.S_OK;
        try
        {
            if (methodDesc == 0 || pRejitData == null || rejitId < 0)
                throw new ArgumentException();
            ICodeVersions cv = _target.Contracts.CodeVersions;
            IReJIT rejitContract = _target.Contracts.ReJIT;
            TargetPointer methodDescPtr = methodDesc.ToTargetPointer(_target);
            ILCodeVersionHandle ilCodeVersion = cv.GetILCodeVersions(methodDescPtr)
                .FirstOrDefault(ilcode => rejitContract.GetRejitId(ilcode).Value == (ulong)rejitId,
                    ILCodeVersionHandle.Invalid);

            if (!ilCodeVersion.IsValid)
                throw new ArgumentException();
            else
            {
                pRejitData->rejitID = (uint)rejitId;
                switch (rejitContract.GetRejitState(ilCodeVersion))
                {
                    case RejitState.Requested:
                        pRejitData->flags = DacpReJitData2.Flags.kRequested;
                        break;
                    case RejitState.Active:
                        pRejitData->flags = DacpReJitData2.Flags.kActive;
                        break;
                    default:
                        Debug.Assert(true, "Unknown SharedRejitInfo state.  cDAC should be updated to understand this new state.");
                        pRejitData->flags = DacpReJitData2.Flags.kUnknown;
                        break;
                }
                pRejitData->il = cv.GetIL(ilCodeVersion).ToClrDataAddress(_target);
                if (ilCodeVersion.IsExplicit)
                    pRejitData->ilCodeVersionNodePtr = ilCodeVersion.ILCodeVersionNode.ToClrDataAddress(_target);
                else
                    pRejitData->ilCodeVersionNodePtr = 0;
            }
        }
        catch (System.Exception ex)
        {
            return ex.HResult;
        }
#if DEBUG
        if (_legacyImpl7 is not null)
        {
            DacpReJitData2 rejitDataLocal;
            int hrLocal = _legacyImpl7.GetReJITInformation(methodDesc, rejitId, &rejitDataLocal);
            Debug.ValidateHResult(hr, hrLocal);
            if (hr == HResults.S_OK)
            {
                Debug.Assert(pRejitData->rejitID == rejitDataLocal.rejitID);
                Debug.Assert(pRejitData->il == rejitDataLocal.il);
                Debug.Assert(pRejitData->flags == rejitDataLocal.flags);
                Debug.Assert(pRejitData->ilCodeVersionNodePtr == rejitDataLocal.ilCodeVersionNodePtr);
            }
        }
#endif
        return hr;
    }
    int ISOSDacInterface7.GetProfilerModifiedILInformation(ClrDataAddress methodDesc, DacpProfilerILData* pILData)
    {
        int hr = HResults.S_OK;
        try
        {
            if (methodDesc == 0 || pILData == null)
                throw new ArgumentException();
            pILData->type = DacpProfilerILData.ModificationType.Unmodified;
            pILData->rejitID = 0;
            pILData->il = 0;

            Contracts.IReJIT rejit = _target.Contracts.ReJIT;
            Contracts.ICodeVersions cv = _target.Contracts.CodeVersions;
            Contracts.ILoader loader = _target.Contracts.Loader;
            Contracts.IRuntimeTypeSystem rts = _target.Contracts.RuntimeTypeSystem;
            TargetPointer methodDescPtr = methodDesc.ToTargetPointer(_target);
            // getting the module handle and the token from the method desc
            MethodDescHandle mdh = rts.GetMethodDescHandle(methodDescPtr);
            TargetPointer mt = rts.GetMethodTable(mdh);
            TypeHandle typeHandle = rts.GetTypeHandle(mt);
            TargetPointer modulePtr = rts.GetModule(typeHandle);
            uint token = rts.GetMethodToken(mdh);
            Contracts.ModuleHandle moduleHandle = loader.GetModuleHandleFromModulePtr(modulePtr);

            Contracts.ILCodeVersionHandle activeILCodeVersion = cv.GetActiveILCodeVersion(methodDescPtr);

            // rejit in progress or rejit applied?
            if (rejit.GetRejitState(activeILCodeVersion) != RejitState.Active || !cv.HasDefaultIL(activeILCodeVersion))
            {
                pILData->type = DacpProfilerILData.ModificationType.ReJITModified;
                pILData->rejitID = (uint)rejit.GetRejitId(activeILCodeVersion).Value;
            }

            TargetPointer il = loader.GetDynamicIL(moduleHandle, token);
            if (il != 0)
            {
                pILData->type = DacpProfilerILData.ModificationType.ILModified;
                pILData->il = il.ToClrDataAddress(_target);
            }
        }
        catch (System.Exception ex)
        {
            hr = ex.HResult;
        }
#if DEBUG
        if (_legacyImpl7 is not null)
        {
            DacpProfilerILData ilDataLocal;
            int hrLocal = _legacyImpl7.GetProfilerModifiedILInformation(methodDesc, &ilDataLocal);
            Debug.ValidateHResult(hr, hrLocal);
            if (hr == HResults.S_OK)
            {
                Debug.Assert(pILData->type == ilDataLocal.type, $"cDAC: {pILData->type}, DAC: {ilDataLocal.type}");
                Debug.Assert(pILData->rejitID == ilDataLocal.rejitID, $"cDAC: {pILData->rejitID}, DAC: {ilDataLocal.rejitID}");
                Debug.Assert(pILData->il == ilDataLocal.il, $"cDAC: {pILData->il:x}, DAC: {ilDataLocal.il:x}");
            }
        }
#endif
        return hr;
    }
    int ISOSDacInterface7.GetMethodsWithProfilerModifiedIL(ClrDataAddress mod, ClrDataAddress* methodDescs, int cMethodDescs, int* pcMethodDescs)
    {
        int hr = HResults.S_OK;
        try
        {
            if (mod == 0 || methodDescs == null || cMethodDescs == 0 || pcMethodDescs == null)
                throw new ArgumentException();
            *pcMethodDescs = 0;
            Contracts.ILoader loader = _target.Contracts.Loader;
            Contracts.IRuntimeTypeSystem rts = _target.Contracts.RuntimeTypeSystem;
            Contracts.IReJIT rejit = _target.Contracts.ReJIT;
            Contracts.ICodeVersions cv = _target.Contracts.CodeVersions;

            TargetPointer modulePtr = mod.ToTargetPointer(_target);
            Contracts.ModuleHandle moduleHandle = loader.GetModuleHandleFromModulePtr(modulePtr);
            // iterate through typedef to method table map
            foreach ((TargetPointer ptr, _) in loader.EnumerateModuleLookupMap(loader.GetLookupTables(moduleHandle).TypeDefToMethodTable))
            {
                if (*pcMethodDescs >= cMethodDescs)
                    break;
                TypeHandle typeHandle = rts.GetTypeHandle(ptr);
                foreach (TargetPointer md in rts.GetIntroducedMethodDescs(typeHandle))
                {
                    MethodDescHandle mdh = rts.GetMethodDescHandle(md);
                    uint token = rts.GetMethodToken(mdh);
                    Contracts.ILCodeVersionHandle activeILCodeVersion = cv.GetActiveILCodeVersion(md);
                    // first condition: is method in process of being rejitted?
                    // second condition: has rejit been applied or null default IL been otherwise used for profiler modification (see src/coreclr/vm/codeversion.cpp comment)?
                    // third condition: has profiler modified IL through ICorProfilerInfo::SetILFunctionBody?
                    if (rejit.GetRejitState(activeILCodeVersion) != RejitState.Active ||
                        !cv.HasDefaultIL(activeILCodeVersion) ||
                        loader.GetDynamicIL(moduleHandle, token) != 0)
                    {
                        methodDescs[*pcMethodDescs] = md.ToClrDataAddress(_target);
                        (*pcMethodDescs)++;
                    }
                    if (*pcMethodDescs >= cMethodDescs)
                        break;
                }
            }
        }
        catch (System.Exception ex)
        {
            hr = ex.HResult;
        }
#if DEBUG
        if (_legacyImpl7 is not null)
        {
            ClrDataAddress[] methodDescsLocal = new ClrDataAddress[cMethodDescs];
            int pcMethodDescsLocal;
            int hrLocal;
            fixed (ClrDataAddress* ptr = methodDescsLocal)
            {
                hrLocal = _legacyImpl7.GetMethodsWithProfilerModifiedIL(mod, ptr, cMethodDescs, &pcMethodDescsLocal);
            }
            Debug.ValidateHResult(hr, hrLocal);
            if (hr == HResults.S_OK)
            {
                Debug.Assert(*pcMethodDescs == pcMethodDescsLocal, $"cDAC: {*pcMethodDescs}, DAC: {pcMethodDescsLocal}");
                for (int i = 0; i < *pcMethodDescs; i++)
                {
                    Debug.Assert(methodDescs[i] == methodDescsLocal[i], $"cDAC: {methodDescs[i]:x}, DAC: {methodDescsLocal[i]:x}");
                }
            }
        }
#endif
        return hr;
    }
    #endregion ISOSDacInterface7

    #region ISOSDacInterface8
    int ISOSDacInterface8.GetNumberGenerations(uint* pGenerations)
    {
        int hr = HResults.S_OK;
        try
        {
            if (pGenerations is null)
                throw new ArgumentException();

            IGC gc = _target.Contracts.GC;
            string[] gcIdentifiers = gc.GetGCIdentifiers();
            GCHeapData heapData = gcIdentifiers.Contains(GCIdentifiers.Server)
                ? gc.GetHeapData(gc.GetGCHeaps().First())
                : gc.GetHeapData();
            *pGenerations = (uint)heapData.GenerationTable.Count;
        }
        catch (System.Exception ex)
        {
            hr = ex.HResult;
        }

#if DEBUG
        if (_legacyImpl8 is not null)
        {
            uint pGenerationsLocal;
            int hrLocal = _legacyImpl8.GetNumberGenerations(&pGenerationsLocal);
            Debug.ValidateHResult(hr, hrLocal);
            if (hr == HResults.S_OK)
            {
                Debug.Assert(*pGenerations == pGenerationsLocal);
            }
        }
#endif
        return hr;
    }

    int ISOSDacInterface8.GetGenerationTable(uint cGenerations, DacpGenerationData* pGenerationData, uint* pNeeded)
    {
        int hr = HResults.S_OK;
        try
        {
            if (cGenerations > 0 && pGenerationData is null)
                throw new ArgumentException();

            IGC gc = _target.Contracts.GC;
            string[] gcIdentifiers = gc.GetGCIdentifiers();
            if (gcIdentifiers.Contains(GCIdentifiers.Server))
                throw Marshal.GetExceptionForHR(HResults.E_FAIL)!;

            GCHeapData heapData = gc.GetHeapData();
            uint totalGenerationCount = (uint)heapData.GenerationTable.Count;

            if (pNeeded is not null)
                *pNeeded = totalGenerationCount;

            if (cGenerations < totalGenerationCount)
            {
                hr = HResults.S_FALSE;
            }
            else
            {
                for (int i = 0; i < (int)totalGenerationCount; i++)
                {
                    GCGenerationData gen = heapData.GenerationTable[i];
                    pGenerationData[i].start_segment = gen.StartSegment.ToClrDataAddress(_target);
                    pGenerationData[i].allocation_start = gen.AllocationStart.ToClrDataAddress(_target);
                    pGenerationData[i].allocContextPtr = gen.AllocationContextPointer.ToClrDataAddress(_target);
                    pGenerationData[i].allocContextLimit = gen.AllocationContextLimit.ToClrDataAddress(_target);
                }
            }
        }
        catch (System.Exception ex)
        {
            hr = ex.HResult;
        }

#if DEBUG
        if (_legacyImpl8 is not null)
        {
            uint pNeededLocal;
            DacpGenerationData[]? genDataLocal = cGenerations > 0 ? new DacpGenerationData[cGenerations] : null;
            fixed (DacpGenerationData* pGenDataLocal = genDataLocal)
            {
                int hrLocal = _legacyImpl8.GetGenerationTable(cGenerations, pGenDataLocal, &pNeededLocal);
                Debug.ValidateHResult(hr, hrLocal);
                if (pNeeded is not null)
                {
                    Debug.Assert(*pNeeded == pNeededLocal);
                }
                if (hr == HResults.S_OK && pGenerationData is not null)
                {
                    for (int i = 0; i < (int)pNeededLocal; i++)
                    {
                        Debug.Assert(pGenDataLocal[i].start_segment == pGenerationData[i].start_segment);
                        Debug.Assert(pGenDataLocal[i].allocation_start == pGenerationData[i].allocation_start);
                        Debug.Assert(pGenDataLocal[i].allocContextPtr == pGenerationData[i].allocContextPtr);
                        Debug.Assert(pGenDataLocal[i].allocContextLimit == pGenerationData[i].allocContextLimit);
                    }
                }
            }
        }
#endif
        return hr;
    }

    int ISOSDacInterface8.GetFinalizationFillPointers(uint cFillPointers, ClrDataAddress* pFinalizationFillPointers, uint* pNeeded)
    {
        int hr = HResults.S_OK;
        try
        {
            if (cFillPointers > 0 && pFinalizationFillPointers is null)
                throw new ArgumentException();

            IGC gc = _target.Contracts.GC;
            string[] gcIdentifiers = gc.GetGCIdentifiers();
            if (gcIdentifiers.Contains(GCIdentifiers.Server))
                throw Marshal.GetExceptionForHR(HResults.E_FAIL)!;

            GCHeapData heapData = gc.GetHeapData();
            uint numFillPointers = (uint)heapData.FillPointers.Count;

            if (pNeeded is not null)
                *pNeeded = numFillPointers;

            if (cFillPointers < numFillPointers)
            {
                hr = HResults.S_FALSE;
            }
            else
            {
                for (int i = 0; i < (int)numFillPointers; i++)
                {
                    pFinalizationFillPointers[i] = heapData.FillPointers[i].ToClrDataAddress(_target);
                }
            }
        }
        catch (System.Exception ex)
        {
            hr = ex.HResult;
        }

#if DEBUG
        if (_legacyImpl8 is not null)
        {
            uint pNeededLocal;
            ClrDataAddress[]? fillPointersLocal = cFillPointers > 0 ? new ClrDataAddress[cFillPointers] : null;
            fixed (ClrDataAddress* pFillPointersLocal = fillPointersLocal)
            {
                int hrLocal = _legacyImpl8.GetFinalizationFillPointers(cFillPointers, pFillPointersLocal, &pNeededLocal);
                Debug.ValidateHResult(hr, hrLocal);
                if (pNeeded is not null)
                {
                    Debug.Assert(*pNeeded == pNeededLocal);
                }
                if (hr == HResults.S_OK && pFinalizationFillPointers is not null)
                {
                    for (int i = 0; i < (int)pNeededLocal; i++)
                    {
                        Debug.Assert(pFillPointersLocal[i] == pFinalizationFillPointers[i]);
                    }
                }
            }
        }
#endif
        return hr;
    }

    int ISOSDacInterface8.GetGenerationTableSvr(ClrDataAddress heapAddr, uint cGenerations, DacpGenerationData* pGenerationData, uint* pNeeded)
    {
        int hr = HResults.S_OK;
        try
        {
            if (heapAddr == 0 || (cGenerations > 0 && pGenerationData is null))
                throw new ArgumentException();

            IGC gc = _target.Contracts.GC;
            string[] gcIdentifiers = gc.GetGCIdentifiers();
            if (!gcIdentifiers.Contains(GCIdentifiers.Server))
                throw Marshal.GetExceptionForHR(HResults.E_FAIL)!;

            GCHeapData heapData = gc.GetHeapData(heapAddr.ToTargetPointer(_target));
            uint totalGenerationCount = (uint)heapData.GenerationTable.Count;

            if (pNeeded is not null)
                *pNeeded = totalGenerationCount;

            if (cGenerations < totalGenerationCount)
            {
                hr = HResults.S_FALSE;
            }
            else
            {
                for (int i = 0; i < (int)totalGenerationCount; i++)
                {
                    GCGenerationData gen = heapData.GenerationTable[i];
                    pGenerationData[i].start_segment = gen.StartSegment.ToClrDataAddress(_target);
                    pGenerationData[i].allocation_start = gen.AllocationStart.ToClrDataAddress(_target);
                    pGenerationData[i].allocContextPtr = gen.AllocationContextPointer.ToClrDataAddress(_target);
                    pGenerationData[i].allocContextLimit = gen.AllocationContextLimit.ToClrDataAddress(_target);
                }
            }
        }
        catch (System.Exception ex)
        {
            hr = ex.HResult;
        }

#if DEBUG
        if (_legacyImpl8 is not null)
        {
            uint pNeededLocal;
            DacpGenerationData[]? genDataLocal = cGenerations > 0 ? new DacpGenerationData[cGenerations] : null;
            fixed (DacpGenerationData* pGenDataLocal = genDataLocal)
            {
                int hrLocal = _legacyImpl8.GetGenerationTableSvr(heapAddr, cGenerations, pGenDataLocal, &pNeededLocal);
                Debug.ValidateHResult(hr, hrLocal);
                if (pNeeded is not null)
                {
                    Debug.Assert(*pNeeded == pNeededLocal);
                }
                if (hr == HResults.S_OK && pGenerationData is not null)
                {
                    for (int i = 0; i < (int)pNeededLocal; i++)
                    {
                        Debug.Assert(pGenDataLocal[i].start_segment == pGenerationData[i].start_segment);
                        Debug.Assert(pGenDataLocal[i].allocation_start == pGenerationData[i].allocation_start);
                        Debug.Assert(pGenDataLocal[i].allocContextPtr == pGenerationData[i].allocContextPtr);
                        Debug.Assert(pGenDataLocal[i].allocContextLimit == pGenerationData[i].allocContextLimit);
                    }
                }
            }
        }
#endif
        return hr;
    }

    int ISOSDacInterface8.GetFinalizationFillPointersSvr(ClrDataAddress heapAddr, uint cFillPointers, ClrDataAddress* pFinalizationFillPointers, uint* pNeeded)
    {
        int hr = HResults.S_OK;
        try
        {
            if (heapAddr == 0 || (cFillPointers > 0 && pFinalizationFillPointers is null))
                throw new ArgumentException();

            IGC gc = _target.Contracts.GC;
            string[] gcIdentifiers = gc.GetGCIdentifiers();
            if (!gcIdentifiers.Contains(GCIdentifiers.Server))
                throw Marshal.GetExceptionForHR(HResults.E_FAIL)!;

            GCHeapData heapData = gc.GetHeapData(heapAddr.ToTargetPointer(_target));
            uint numFillPointers = (uint)heapData.FillPointers.Count;

            if (pNeeded is not null)
                *pNeeded = numFillPointers;

            if (cFillPointers < numFillPointers)
            {
                hr = HResults.S_FALSE;
            }
            else
            {
                for (int i = 0; i < (int)numFillPointers; i++)
                {
                    pFinalizationFillPointers[i] = heapData.FillPointers[i].ToClrDataAddress(_target);
                }
            }
        }
        catch (System.Exception ex)
        {
            hr = ex.HResult;
        }

#if DEBUG
        if (_legacyImpl8 is not null)
        {
            uint pNeededLocal;
            ClrDataAddress[]? fillPointersLocal = cFillPointers > 0 ? new ClrDataAddress[cFillPointers] : null;
            fixed (ClrDataAddress* pFillPointersLocal = fillPointersLocal)
            {
                int hrLocal = _legacyImpl8.GetFinalizationFillPointersSvr(heapAddr, cFillPointers, pFillPointersLocal, &pNeededLocal);
                Debug.ValidateHResult(hr, hrLocal);
                if (pNeeded is not null)
                {
                    Debug.Assert(*pNeeded == pNeededLocal);
                }
                if (hr == HResults.S_OK && pFinalizationFillPointers is not null)
                {
                    int fillPointersToCompare = (int)Math.Min(cFillPointers, pNeededLocal);
                    for (int i = 0; i < fillPointersToCompare; i++)
                    {
                        Debug.Assert(pFillPointersLocal[i] == pFinalizationFillPointers[i]);
                    }
                }
            }
        }
#endif
        return hr;
    }

    int ISOSDacInterface8.GetAssemblyLoadContext(ClrDataAddress methodTable, ClrDataAddress* assemblyLoadContext)
    {
        int hr = HResults.S_OK;
        try
        {
            if (methodTable == 0 || assemblyLoadContext == null)
                throw new ArgumentException();

            Contracts.IRuntimeTypeSystem rtsContract = _target.Contracts.RuntimeTypeSystem;
            Contracts.ILoader loaderContract = _target.Contracts.Loader;
            Contracts.TypeHandle methodTableHandle = rtsContract.GetTypeHandle(methodTable.ToTargetPointer(_target));
            Contracts.ModuleHandle moduleHandle = loaderContract.GetModuleHandleFromModulePtr(rtsContract.GetModule(methodTableHandle));
            TargetPointer alc = loaderContract.GetAssemblyLoadContext(moduleHandle);
            *assemblyLoadContext = alc.ToClrDataAddress(_target);
        }
        catch (System.Exception ex)
        {
            hr = ex.HResult;
        }
#if DEBUG
        if (_legacyImpl8 is not null)
        {
            ClrDataAddress assemblyLoadContextLocal;
            int hrLocal = _legacyImpl8.GetAssemblyLoadContext(methodTable, &assemblyLoadContextLocal);
            Debug.ValidateHResult(hr, hrLocal);
            if (hr == HResults.S_OK)
            {
                Debug.Assert(*assemblyLoadContext == assemblyLoadContextLocal);
            }
        }
#endif
        return hr;
    }
    #endregion ISOSDacInterface8

    #region ISOSDacInterface9
    int ISOSDacInterface9.GetBreakingChangeVersion()
    {
        int version = _target.ReadGlobal<byte>(Constants.Globals.SOSBreakingChangeVersion);

#if DEBUG
        if (_legacyImpl9 is not null)
        {
            Debug.Assert(version == _legacyImpl9.GetBreakingChangeVersion());
        }
#endif
        return version;
    }
    #endregion ISOSDacInterface9

    #region ISOSDacInterface10
    int ISOSDacInterface10.GetObjectComWrappersData(ClrDataAddress objAddr, ClrDataAddress* rcw, uint count, [In, MarshalUsing(CountElementName = "count"), Out] ClrDataAddress[]? mowList, uint* pNeeded)
    {
        int hr = HResults.S_FALSE;
        try
        {
            if (objAddr == 0 || (count > 0 && mowList == null))
                throw new ArgumentException();

            if (pNeeded != null)
                *pNeeded = 0;

            if (rcw != null)
                *rcw = 0;

            Contracts.IComWrappers comWrappersContract = _target.Contracts.ComWrappers;
            TargetPointer objPtr = objAddr.ToTargetPointer(_target);

            TargetPointer rcwObj = comWrappersContract.GetComWrappersRCWForObject(objPtr);
            if (rcwObj != TargetPointer.Null)
            {
                if (rcw != null)
                    *rcw = rcwObj.ToClrDataAddress(_target) | _rcwMask;
                hr = HResults.S_OK;
            }

            List<TargetPointer> mows = comWrappersContract.GetMOWs(objPtr, out bool hasMOWTable);
            if (hasMOWTable)
                hr = HResults.S_OK;
            if (mows.Count > 0)
            {
                if (pNeeded != null)
                    *pNeeded = (uint)mows.Count;

                if (count < (uint)mows.Count)
                    hr = HResults.S_FALSE;

                for (int i = 0; i < (int)count && i < mows.Count; i++)
                {
                    TargetPointer comIdentity = comWrappersContract.GetIdentityForMOW(mows[i]);
                    mowList![i] = comIdentity.ToClrDataAddress(_target);
                }
            }
        }
        catch (System.Exception ex)
        {
            hr = ex.HResult;
        }
#if DEBUG
        if (_legacyImpl10 is not null)
        {
            ClrDataAddress rcwLocal = 0;
            uint neededLocal = 0;
            ClrDataAddress[]? mowListLocal =  count > 0 ? new ClrDataAddress[count] : null;
            int hrLocal = _legacyImpl10.GetObjectComWrappersData(objAddr, rcw == null ? null : &rcwLocal, count, mowListLocal, &neededLocal);
            Debug.ValidateHResult(hr, hrLocal);
            if (hr == HResults.S_OK || hr == HResults.S_FALSE)
            {
                if (rcw != null)
                    Debug.Assert(*rcw == rcwLocal);
                if (pNeeded != null)
                    Debug.Assert(*pNeeded == neededLocal);
                if (mowList != null)
                {
                    for (int i = 0; i < (int)neededLocal && i < count; i++)
                    {
                        Debug.Assert(mowList[i] == mowListLocal![i]);
                    }
                }
            }
        }
#endif
        return hr;
    }
    int ISOSDacInterface10.IsComWrappersCCW(ClrDataAddress ccw, Interop.BOOL* isComWrappersCCW)
    {
        int hr = HResults.S_OK;
        try
        {
            Contracts.IComWrappers comWrappersContract = _target.Contracts.ComWrappers;
            if (ccw == 0)
                throw new ArgumentException();

            if (isComWrappersCCW != null)
            {
                TargetPointer ccwPtr = comWrappersContract.GetManagedObjectWrapperFromCCW(ccw.ToTargetPointer(_target));
                *isComWrappersCCW = (ccwPtr != TargetPointer.Null) ? Interop.BOOL.TRUE : Interop.BOOL.FALSE;
                hr = (ccwPtr != TargetPointer.Null) ? HResults.S_OK : HResults.S_FALSE;
            }
        }
        catch (System.Exception ex)
        {
            hr = ex.HResult;
        }
#if DEBUG
        if (_legacyImpl10 is not null)
        {
            Interop.BOOL isComWrappersCCWLocal;
            int hrLocal = _legacyImpl10.IsComWrappersCCW(ccw, &isComWrappersCCWLocal);
            Debug.ValidateHResult(hr, hrLocal);
            if (hr == HResults.S_OK || hr == HResults.S_FALSE)
            {
                Debug.Assert(*isComWrappersCCW == isComWrappersCCWLocal);
            }
        }
#endif
        return hr;
    }
    int ISOSDacInterface10.GetComWrappersCCWData(ClrDataAddress ccw, ClrDataAddress* managedObject, int* refCount)
    {
        int hr = HResults.S_OK;
        try
        {
            if (ccw == 0)
                throw new ArgumentException();
            TargetPointer ccwPtr = ccw.ToTargetPointer(_target);
            Contracts.IComWrappers comWrappersContract = _target.Contracts.ComWrappers;
            TargetPointer managedObjectPtr = comWrappersContract.GetManagedObjectWrapperFromCCW(ccwPtr);
            if (managedObjectPtr == TargetPointer.Null)
                throw new ArgumentException();

            if (managedObject != null)
            {
                *managedObject = 0;
                *managedObject = comWrappersContract.GetComWrappersObjectFromMOW(managedObjectPtr).ToClrDataAddress(_target);
            }

            if (refCount != null)
                *refCount = (int)comWrappersContract.GetMOWReferenceCount(managedObjectPtr);
        }
        catch (System.Exception ex)
        {
            hr = ex.HResult;
        }
#if DEBUG
        if (_legacyImpl10 is not null)
        {
            ClrDataAddress managedObjectLocal;
            int refCountLocal;
            int hrLocal = _legacyImpl10.GetComWrappersCCWData(ccw, &managedObjectLocal, &refCountLocal);
            Debug.ValidateHResult(hr, hrLocal);
            if (hr == HResults.S_OK)
            {
                if (managedObject != null)
                    Debug.Assert(*managedObject == managedObjectLocal);
                if (refCount != null)
                    Debug.Assert(*refCount == refCountLocal);
            }
        }
#endif
        return hr;
    }
    int ISOSDacInterface10.IsComWrappersRCW(ClrDataAddress rcw, Interop.BOOL* isComWrappersRCW)
    {
        int hr = HResults.S_OK;
        try
        {
            Contracts.IComWrappers comWrappersContract = _target.Contracts.ComWrappers;
            if (rcw == 0)
                throw new ArgumentException();
            else if (isComWrappersRCW != null)
            {
                if ((rcw & _rcwMask) == 0)
                    *isComWrappersRCW = Interop.BOOL.FALSE;
                else
                {
                    TargetPointer rcwPtr = rcw.ToTargetPointer(_target) & ~_rcwMask;
                    *isComWrappersRCW = comWrappersContract.IsComWrappersRCW(rcwPtr) ? Interop.BOOL.TRUE : Interop.BOOL.FALSE;
                }
                hr = (*isComWrappersRCW != Interop.BOOL.FALSE) ? HResults.S_OK : HResults.S_FALSE;
            }
        }
        catch (System.Exception ex)
        {
            hr = ex.HResult;
        }
#if DEBUG
        if (_legacyImpl10 is not null)
        {
            Interop.BOOL isComWrappersRCWLocal;
            int hrLocal = _legacyImpl10.IsComWrappersRCW(rcw, &isComWrappersRCWLocal);
            Debug.ValidateHResult(hr, hrLocal);
            if (hr == HResults.S_OK || hr == HResults.S_FALSE)
            {
                Debug.Assert(*isComWrappersRCW == isComWrappersRCWLocal);
            }
        }
#endif
        return hr;
    }
    int ISOSDacInterface10.GetComWrappersRCWData(ClrDataAddress rcw, ClrDataAddress* identity)
    {
        int hr = HResults.S_OK;
        try
        {
            Contracts.IComWrappers comWrappersContract = _target.Contracts.ComWrappers;
            if (rcw == 0 || identity == null)
                throw new ArgumentException();
            else if ((rcw & _rcwMask) == 0)
                *identity = 0;
            else
            {
                TargetPointer identityPtr = comWrappersContract.GetComWrappersIdentity(rcw.ToTargetPointer(_target) & ~_rcwMask);
                *identity = identityPtr.ToClrDataAddress(_target);
            }
        }
        catch (System.Exception ex)
        {
            hr = ex.HResult;
        }
#if DEBUG
        if (_legacyImpl10 is not null)
        {
            ClrDataAddress identityLocal;
            int hrLocal = _legacyImpl10.GetComWrappersRCWData(rcw, &identityLocal);
            Debug.ValidateHResult(hr, hrLocal);
            if (hr == HResults.S_OK)
            {
                Debug.Assert(*identity == identityLocal);
            }
        }
#endif
        return hr;
    }
    #endregion ISOSDacInterface10

    #region ISOSDacInterface11
    int ISOSDacInterface11.IsTrackedType(ClrDataAddress objAddr, Interop.BOOL* isTrackedType, Interop.BOOL* hasTaggedMemory)
    {
        int hr = HResults.S_OK;
        try
        {
            if (objAddr == 0 || isTrackedType == null || hasTaggedMemory == null)
                throw new ArgumentException();

            *isTrackedType = Interop.BOOL.FALSE;
            *hasTaggedMemory = Interop.BOOL.FALSE;

            TargetPointer objPtr = objAddr.ToTargetPointer(_target);
            Contracts.IObject objectContract = _target.Contracts.Object;

            TargetPointer mt = objectContract.GetMethodTableAddress(objPtr);
            if (mt == TargetPointer.Null)
                throw new ArgumentException();

            Contracts.IRuntimeTypeSystem rtsContract = _target.Contracts.RuntimeTypeSystem;
            TypeHandle mtHandle = rtsContract.GetTypeHandle(mt);
            if (rtsContract.IsTrackedReferenceWithFinalizer(mtHandle))
                *isTrackedType = Interop.BOOL.TRUE;

            hr = (*isTrackedType == Interop.BOOL.TRUE) ? HResults.S_OK : HResults.S_FALSE;

            if (_target.Contracts.TryGetContract<IObjectiveCMarshal>(out IObjectiveCMarshal? objcContract))
            {
                TargetPointer taggedMemoryPtr = objcContract.GetTaggedMemory(objPtr, out _);
                if (taggedMemoryPtr != TargetPointer.Null)
                    *hasTaggedMemory = Interop.BOOL.TRUE;
            }
        }
        catch (System.Exception ex)
        {
            hr = ex.HResult;
        }
#if DEBUG
        if (_legacyImpl11 is not null)
        {
            Interop.BOOL isTrackedTypeLocal;
            Interop.BOOL hasTaggedMemoryLocal;
            int hrLocal = _legacyImpl11.IsTrackedType(objAddr, &isTrackedTypeLocal, &hasTaggedMemoryLocal);
            Debug.ValidateHResult(hr, hrLocal);
            if (hr == HResults.S_OK || hr == HResults.S_FALSE)
            {
                Debug.Assert(*isTrackedType == isTrackedTypeLocal);
                Debug.Assert(*hasTaggedMemory == hasTaggedMemoryLocal);
            }
        }
#endif
        return hr;
    }

    int ISOSDacInterface11.GetTaggedMemory(ClrDataAddress objAddr, ClrDataAddress* taggedMemory, nuint* taggedMemorySizeInBytes)
    {
        int hr = HResults.S_FALSE;
        try
        {
            if (objAddr == 0 || taggedMemory == null || taggedMemorySizeInBytes == null)
                throw new ArgumentException();

            *taggedMemory = 0;
            *taggedMemorySizeInBytes = 0;

            TargetPointer objPtr = objAddr.ToTargetPointer(_target);
            if (_target.Contracts.TryGetContract<IObjectiveCMarshal>(out IObjectiveCMarshal? objcContract))
            {
                TargetPointer taggedMemoryPtr = objcContract.GetTaggedMemory(objPtr, out TargetNUInt taggedMemorySizeNUInt);
                if (taggedMemoryPtr != TargetPointer.Null)
                {
                    *taggedMemory = taggedMemoryPtr.ToClrDataAddress(_target);
                    *taggedMemorySizeInBytes = (nuint)taggedMemorySizeNUInt.Value;
                    hr = HResults.S_OK;
                }
            }
        }
        catch (System.Exception ex)
        {
            hr = ex.HResult;
        }
#if DEBUG
        if (_legacyImpl11 is not null)
        {
            ClrDataAddress taggedMemoryLocal;
            nuint taggedMemorySizeInBytesLocal;
            int hrLocal = _legacyImpl11.GetTaggedMemory(objAddr, &taggedMemoryLocal, &taggedMemorySizeInBytesLocal);
            Debug.ValidateHResult(hr, hrLocal);
            if (hr == HResults.S_OK || hr == HResults.S_FALSE)
            {
                Debug.Assert(*taggedMemory == taggedMemoryLocal);
                Debug.Assert(*taggedMemorySizeInBytes == taggedMemorySizeInBytesLocal);
            }
        }
#endif
        return hr;
    }
    #endregion ISOSDacInterface11

    #region ISOSDacInterface12
    int ISOSDacInterface12.GetGlobalAllocationContext(ClrDataAddress* allocPtr, ClrDataAddress* allocLimit)
    {
        int hr = HResults.S_OK;
        try
        {
            if (allocPtr == null || allocLimit == null)
                throw new ArgumentException();

            Contracts.IGC gcContract = _target.Contracts.GC;
            gcContract.GetGlobalAllocationContext(out TargetPointer pointer, out TargetPointer limit);
            *allocPtr = pointer.ToClrDataAddress(_target);
            *allocLimit = limit.ToClrDataAddress(_target);
        }
        catch (System.Exception ex)
        {
            hr = ex.HResult;
        }

#if DEBUG
        if (_legacyImpl12 is not null)
        {
            ClrDataAddress allocPtrLocal = default;
            ClrDataAddress allocLimitLocal = default;
            int hrLocal = _legacyImpl12.GetGlobalAllocationContext(&allocPtrLocal, &allocLimitLocal);
            Debug.ValidateHResult(hr, hrLocal);
            if (hr == HResults.S_OK)
            {
                Debug.Assert(*allocPtr == allocPtrLocal);
                Debug.Assert(*allocLimit == allocLimitLocal);
            }
        }
#endif
        return hr;
    }
    #endregion ISOSDacInterface12

    #region ISOSDacInterface13

    int ISOSDacInterface13.TraverseLoaderHeap(ClrDataAddress loaderHeapAddr, /*LoaderHeapKind*/ int kind, /*VISITHEAP*/ delegate* unmanaged<ulong, nuint, Interop.BOOL, void> pCallback)
    {
        int hr = TraverseLoaderHeapCore(loaderHeapAddr.ToTargetPointer(_target), pCallback);
#if DEBUG
        if (_legacyImpl13 is not null)
        {
            int cdacCount = DebugTraverseLoaderHeapBlocks.Count;
            delegate* unmanaged<ulong, nuint, Interop.BOOL, void> debugCallbackPtr = &TraverseLoaderHeapDebugCallback;
            int hrLocal = _legacyImpl13.TraverseLoaderHeap(loaderHeapAddr, kind, debugCallbackPtr);
            Debug.ValidateHResult(hr, hrLocal);
            if (hr == HResults.S_OK || hr == HResults.S_FALSE)
            {
                Debug.Assert(DebugTraverseLoaderHeapBlocks.Count == 0,
                    $"cDAC found {cdacCount} blocks, DAC matched {_debugTraverseLoaderDebugCount}, {DebugTraverseLoaderHeapBlocks.Count} unmatched");
                Debug.Assert(_debugTraverseLoaderDebugCount == (uint)cdacCount,
                    $"cDAC: {cdacCount} blocks, DAC: {_debugTraverseLoaderDebugCount} blocks");
            }
        }
#endif
        return hr;
    }
    int ISOSDacInterface13.GetDomainLoaderAllocator(ClrDataAddress domainAddress, ClrDataAddress* pLoaderAllocator)
    {
        int hr = HResults.S_OK;
        try
        {
            if (pLoaderAllocator == null)
                throw new ArgumentException();

            if (domainAddress == 0)
            {
                *pLoaderAllocator = 0;
                hr = HResults.S_FALSE;
            }
            else
            {
                // The one and only app domain uses the global loader allocator
                Contracts.ILoader contract = _target.Contracts.Loader;
                TargetPointer globalLoaderAllocator = contract.GetGlobalLoaderAllocator();
                *pLoaderAllocator = globalLoaderAllocator.ToClrDataAddress(_target);
            }
        }
        catch (System.Exception ex)
        {
            hr = ex.HResult;
        }

#if DEBUG
        if (_legacyImpl13 is not null)
        {
            ClrDataAddress pLoaderAllocatorLocal;
            int hrLocal = _legacyImpl13.GetDomainLoaderAllocator(domainAddress, &pLoaderAllocatorLocal);
            Debug.ValidateHResult(hr, hrLocal);
            if (hr == HResults.S_OK || hr == HResults.S_FALSE)
            {
                Debug.Assert(*pLoaderAllocator == pLoaderAllocatorLocal);
            }
        }
#endif
        return hr;
    }

    // Static ANSI string pointers for all known heap names, in the canonical order matching
    // LoaderAllocatorLoaderHeapNames in request.cpp. These are process-lifetime allocations,
    // equivalent to static const char* literals in C++.
    private static readonly (LoaderAllocatorHeapType HeapType, nint AnsiPtr)[] s_heapNameEntries = InitializeHeapNameEntries();
    private (LoaderAllocatorHeapType HeapType, nint AnsiPtr)[]? _filteredHeapNameEntries;

    private static (LoaderAllocatorHeapType heapType, nint AnsiPtr)[] InitializeHeapNameEntries()
    {
        // Order must match LoaderAllocatorLoaderHeapNames in src/coreclr/debug/daccess/request.cpp
        LoaderAllocatorHeapType[] heapTypes =
        [
            LoaderAllocatorHeapType.LowFrequencyHeap,
            LoaderAllocatorHeapType.HighFrequencyHeap,
            LoaderAllocatorHeapType.StaticsHeap,
            LoaderAllocatorHeapType.StubHeap,
            LoaderAllocatorHeapType.ExecutableHeap,
            LoaderAllocatorHeapType.FixupPrecodeHeap,
            LoaderAllocatorHeapType.NewStubPrecodeHeap,
            LoaderAllocatorHeapType.DynamicHelpersStubHeap,
            LoaderAllocatorHeapType.IndcellHeap,
            LoaderAllocatorHeapType.CacheEntryHeap
        ];
        var entries = new (LoaderAllocatorHeapType, nint)[heapTypes.Length];
        for (int i = 0; i < heapTypes.Length; i++)
            entries[i] = (heapTypes[i], Marshal.StringToHGlobalAnsi(heapTypes[i].ToString()));
        return entries;
    }

    // Returns the set of heap name entries for this target, filtered by which
    // data descriptor fields exist. This mirrors the DAC's compile-time
    // LoaderAllocatorLoaderHeapNames array and ensures a fixed count/ordering
    // regardless of per-loader-allocator runtime state (e.g. VCS manager being null).
    private (LoaderAllocatorHeapType HeapType, nint AnsiPtr)[] GetFilteredHeapNameEntries()
    {
        if (_filteredHeapNameEntries is not null)
            return _filteredHeapNameEntries;

        Target.TypeInfo laType = _target.GetTypeInfo(DataType.LoaderAllocator);
        Target.TypeInfo vcsType = _target.GetTypeInfo(DataType.VirtualCallStubManager);

        var entries = new List<(LoaderAllocatorHeapType HeapType, nint AnsiPtr)>();
        foreach (var entry in s_heapNameEntries)
        {
            bool include = entry.HeapType is LoaderAllocatorHeapType.IndcellHeap or LoaderAllocatorHeapType.CacheEntryHeap
                ? vcsType.Fields.ContainsKey(entry.HeapType.ToString())
                : laType.Fields.ContainsKey(entry.HeapType.ToString());
            if (include)
                entries.Add(entry);
        }

        _filteredHeapNameEntries = entries.ToArray();
        return _filteredHeapNameEntries;
    }

    int ISOSDacInterface13.GetLoaderAllocatorHeapNames(int count, char** ppNames, int* pNeeded)
    {
        int hr = HResults.S_OK;
        try
        {
            var filteredEntries = GetFilteredHeapNameEntries();
            int loaderHeapCount = filteredEntries.Length;

            if (pNeeded != null)
                *pNeeded = loaderHeapCount;

            if (ppNames != null)
            {
                int fillCount = Math.Min(count, loaderHeapCount);
                for (int i = 0; i < fillCount; i++)
                    ppNames[i] = (char*)filteredEntries[i].AnsiPtr;
            }

            if (count < loaderHeapCount)
                hr = HResults.S_FALSE;
        }
        catch (System.Exception ex)
        {
            hr = ex.HResult;
        }

#if DEBUG
        if (_legacyImpl13 is not null)
        {
            int pNeededLocal;
            _legacyImpl13.GetLoaderAllocatorHeapNames(0, null, &pNeededLocal);
            Debug.Assert(pNeeded is null || *pNeeded == pNeededLocal, $"cDAC needed: {(pNeeded != null ? *pNeeded : -1)}, DAC needed: {pNeededLocal}");
            if (hr >= 0 && ppNames != null && pNeededLocal > 0)
            {
                char** ppNamesLocal = stackalloc char*[pNeededLocal];
                _legacyImpl13.GetLoaderAllocatorHeapNames(pNeededLocal, ppNamesLocal, null);
                int compareCount = Math.Min(count, pNeededLocal);
                for (int i = 0; i < compareCount; i++)
                {
                    string cdacName = Marshal.PtrToStringAnsi((nint)ppNames[i])!;
                    string dacName = Marshal.PtrToStringAnsi((nint)ppNamesLocal[i])!;
                    Debug.Assert(cdacName == dacName, $"HeapName[{i}] - cDAC: {cdacName}, DAC: {dacName}");
                }
            }
        }
#endif
        return hr;
    }
    int ISOSDacInterface13.GetLoaderAllocatorHeaps(ClrDataAddress loaderAllocator, int count, ClrDataAddress* pLoaderHeaps, /*LoaderHeapKind*/ int* pKinds, int* pNeeded)
    {
        int hr = HResults.S_OK;
        try
        {
            if (loaderAllocator == 0)
                throw new ArgumentException("loaderAllocator cannot be zero.", nameof(loaderAllocator));
            Contracts.ILoader contract = _target.Contracts.Loader;
            IReadOnlyDictionary<LoaderAllocatorHeapType, TargetPointer> heaps = contract.GetLoaderAllocatorHeaps(loaderAllocator.ToTargetPointer(_target));
            var filteredEntries = GetFilteredHeapNameEntries();
            int loaderHeapCount = filteredEntries.Length;

            if (pNeeded != null)
                *pNeeded = loaderHeapCount;

            if (pLoaderHeaps != null)
            {
                if (count < loaderHeapCount)
                {
                    throw new ArgumentException($"The count parameter ({count}) is less than the number of loader heaps ({loaderHeapCount}).", nameof(count));
                }
                for (int i = 0; i < loaderHeapCount; i++)
                {
                    pLoaderHeaps[i] = heaps.TryGetValue(filteredEntries[i].HeapType, out TargetPointer heapAddr)
                        ? heapAddr.ToClrDataAddress(_target)
                        : 0;
                    pKinds[i] = 0; // LoaderHeapKindNormal
                }
            }
        }
        catch (System.Exception ex)
        {
            hr = ex.HResult;
        }

#if DEBUG
        if (_legacyImpl13 is not null)
        {
            int pNeededLocal;
            int hrLocal = _legacyImpl13.GetLoaderAllocatorHeaps(loaderAllocator, 0, null, null, &pNeededLocal);
            Debug.Assert(pNeeded is null || *pNeeded == pNeededLocal, $"cDAC needed: {(pNeeded != null ? *pNeeded : -1)}, DAC needed: {pNeededLocal}");
            if (hr >= 0 && pLoaderHeaps != null && pNeededLocal > 0)
            {
                ClrDataAddress* pLoaderHeapsLocal = stackalloc ClrDataAddress[pNeededLocal];
                int* pKindsLocal = stackalloc int[pNeededLocal];
                hrLocal = _legacyImpl13.GetLoaderAllocatorHeaps(loaderAllocator, pNeededLocal, pLoaderHeapsLocal, pKindsLocal, null);
                Debug.Assert(hrLocal == hr, $"cDAC: {hr:x}, DAC: {hrLocal:x}");
                if (hrLocal >= 0)
                {
                    for (int i = 0; i < pNeededLocal; i++)
                    {
                        Debug.Assert(pLoaderHeaps[i] == pLoaderHeapsLocal[i], $"Heap[{i}] - cDAC: {pLoaderHeaps[i]:x}, DAC: {pLoaderHeapsLocal[i]:x}");
                        Debug.Assert(pKinds[i] == pKindsLocal[i], $"Kind[{i}] - cDAC: {pKinds[i]}, DAC: {pKindsLocal[i]}");
                    }
                }
            }
        }
#endif
        return hr;
    }

    int ISOSDacInterface13.GetHandleTableMemoryRegions(DacComNullableByRef<ISOSMemoryEnum> ppEnum)
    {
        int hr = HResults.S_OK;
        try
        {
            IReadOnlyList<GCMemoryRegionData> regions = _target.Contracts.GC.GetHandleTableMemoryRegions();
            ISOSMemoryEnum? legacyMemoryEnum = null;
#if DEBUG
            if (_legacyImpl13 is not null)
            {
                DacComNullableByRef<ISOSMemoryEnum> legacyOut = new(isNullRef: false);
                int hrLocal = _legacyImpl13.GetHandleTableMemoryRegions(legacyOut);
                Debug.ValidateHResult(hr, hrLocal);
                legacyMemoryEnum = legacyOut.Interface;
            }
#endif
            ppEnum.Interface = new SOSMemoryEnum(_target, regions, legacyMemoryEnum);
        }
        catch (System.Exception e)
        {
            hr = e.HResult;
        }

        return hr;
    }
    int ISOSDacInterface13.GetGCBookkeepingMemoryRegions(DacComNullableByRef<ISOSMemoryEnum> ppEnum)
    {
        int hr = HResults.S_OK;
        try
        {
            IReadOnlyList<GCMemoryRegionData> regions = _target.Contracts.GC.GetGCBookkeepingMemoryRegions();
            ISOSMemoryEnum? legacyMemoryEnum = null;
#if DEBUG
            if (_legacyImpl13 is not null)
            {
                DacComNullableByRef<ISOSMemoryEnum> legacyOut = new(isNullRef: false);
                int hrLocal = _legacyImpl13.GetGCBookkeepingMemoryRegions(legacyOut);
                Debug.ValidateHResult(hr, hrLocal);
                legacyMemoryEnum = legacyOut.Interface;
            }
#endif
            ppEnum.Interface = new SOSMemoryEnum(_target, regions, legacyMemoryEnum);
        }
        catch (System.Exception e)
        {
            hr = e.HResult;
        }

        return hr;
    }
    int ISOSDacInterface13.GetGCFreeRegions(DacComNullableByRef<ISOSMemoryEnum> ppEnum)
    {
        int hr = HResults.S_OK;
        try
        {
            IReadOnlyList<GCMemoryRegionData> regions = _target.Contracts.GC.GetGCFreeRegions();
            ISOSMemoryEnum? legacyMemoryEnum = null;
#if DEBUG
            if (_legacyImpl13 is not null)
            {
                DacComNullableByRef<ISOSMemoryEnum> legacyOut = new(isNullRef: false);
                int hrLocal = _legacyImpl13.GetGCFreeRegions(legacyOut);
                Debug.ValidateHResult(hr, hrLocal);
                legacyMemoryEnum = legacyOut.Interface;
            }
#endif
            ppEnum.Interface = new SOSMemoryEnum(_target, regions, legacyMemoryEnum);
        }
        catch (System.Exception e)
        {
            hr = e.HResult;
        }

        return hr;
    }
    int ISOSDacInterface13.LockedFlush()
    {
        _target.Flush();

        // As long as any part of cDAC falls back to the legacy DAC, we need to propagate the Flush call
        if (_legacyImpl13 is not null)
            return _legacyImpl13.LockedFlush();

        return HResults.S_OK;
    }
    #endregion ISOSDacInterface13

    #region ISOSDacInterface14
    int ISOSDacInterface14.GetStaticBaseAddress(ClrDataAddress methodTable, ClrDataAddress* nonGCStaticsAddress, ClrDataAddress* GCStaticsAddress)
    {
        int hr = HResults.S_OK;
        try
        {
            if (nonGCStaticsAddress == null && GCStaticsAddress == null)
                throw new NullReferenceException();
            if (methodTable == 0)
                throw new ArgumentException();

            Contracts.IRuntimeTypeSystem rtsContract = _target.Contracts.RuntimeTypeSystem;
            Contracts.TypeHandle typeHandle = rtsContract.GetTypeHandle(methodTable.ToTargetPointer(_target));
            if (GCStaticsAddress != null)
                *GCStaticsAddress = rtsContract.GetGCStaticsBasePointer(typeHandle).ToClrDataAddress(_target);
            if (nonGCStaticsAddress != null)
                *nonGCStaticsAddress = rtsContract.GetNonGCStaticsBasePointer(typeHandle).ToClrDataAddress(_target);
        }
        catch (System.Exception ex)
        {
            hr = ex.HResult;
        }
#if DEBUG
        if (_legacyImpl14 is not null)
        {
            ClrDataAddress nonGCStaticsAddressLocal;
            ClrDataAddress GCStaticsAddressLocal;
            int hrLocal = _legacyImpl14.GetStaticBaseAddress(methodTable, &nonGCStaticsAddressLocal, &GCStaticsAddressLocal);
            Debug.ValidateHResult(hr, hrLocal);
            if (hr == HResults.S_OK)
            {
                if (GCStaticsAddress != null)
                    Debug.Assert(*GCStaticsAddress == GCStaticsAddressLocal);
                if (nonGCStaticsAddress != null)
                    Debug.Assert(*nonGCStaticsAddress == nonGCStaticsAddressLocal);
            }
        }
#endif
        return hr;
    }
    int ISOSDacInterface14.GetThreadStaticBaseAddress(ClrDataAddress methodTable, ClrDataAddress thread, ClrDataAddress* nonGCStaticsAddress, ClrDataAddress* GCStaticsAddress)
    {
        int hr = HResults.S_OK;
        try
        {
            if (nonGCStaticsAddress == null && GCStaticsAddress == null)
                throw new NullReferenceException();
            if (methodTable == 0 || thread == 0)
                throw new ArgumentException();

            Contracts.IRuntimeTypeSystem rtsContract = _target.Contracts.RuntimeTypeSystem;
            TargetPointer methodTablePtr = methodTable.ToTargetPointer(_target);
            TargetPointer threadPtr = thread.ToTargetPointer(_target);
            Contracts.TypeHandle typeHandle = rtsContract.GetTypeHandle(methodTablePtr);
            ushort numThreadStaticFields = rtsContract.GetNumThreadStaticFields(typeHandle);
            if (numThreadStaticFields == 0)
            {
                if (GCStaticsAddress != null)
                    *GCStaticsAddress = 0;
                if (nonGCStaticsAddress != null)
                    *nonGCStaticsAddress = 0;
            }
            else
            {
                if (GCStaticsAddress != null)
                    *GCStaticsAddress = rtsContract.GetGCThreadStaticsBasePointer(typeHandle, threadPtr).ToClrDataAddress(_target);
                if (nonGCStaticsAddress != null)
                    *nonGCStaticsAddress = rtsContract.GetNonGCThreadStaticsBasePointer(typeHandle, threadPtr).ToClrDataAddress(_target);
            }
        }
        catch (System.Exception ex)
        {
            hr = ex.HResult;
        }
#if DEBUG
        if (_legacyImpl14 is not null)
        {
            ClrDataAddress nonGCStaticsAddressLocal = default;
            ClrDataAddress GCStaticsAddressLocal = default;
            ClrDataAddress* nonGCStaticsAddressOrNull = nonGCStaticsAddress != null ? &nonGCStaticsAddressLocal : null;
            ClrDataAddress* gcStaticsAddressOrNull = GCStaticsAddress != null ? &GCStaticsAddressLocal : null;
            int hrLocal = _legacyImpl14.GetThreadStaticBaseAddress(methodTable, thread, nonGCStaticsAddressOrNull, gcStaticsAddressOrNull);
            Debug.ValidateHResult(hr, hrLocal);
            if (hr == HResults.S_OK)
            {
                if (nonGCStaticsAddress != null)
                    Debug.Assert(*nonGCStaticsAddress == nonGCStaticsAddressLocal);
                if (GCStaticsAddress != null)
                    Debug.Assert(*GCStaticsAddress == GCStaticsAddressLocal);
            }
        }
#endif
        return hr;
    }

    int ISOSDacInterface14.GetMethodTableInitializationFlags(ClrDataAddress methodTable, MethodTableInitializationFlags* initializationStatus)
    {
        int hr = HResults.S_OK;
        try
        {
            if (methodTable == 0)
                throw new ArgumentException();
            if (initializationStatus == null)
                throw new NullReferenceException();

            Contracts.IRuntimeTypeSystem rtsContract = _target.Contracts.RuntimeTypeSystem;
            Contracts.TypeHandle methodTableHandle = rtsContract.GetTypeHandle(methodTable.ToTargetPointer(_target));
            *initializationStatus = (MethodTableInitializationFlags)0;
            if (rtsContract.IsClassInited(methodTableHandle))
                *initializationStatus = MethodTableInitializationFlags.MethodTableInitialized;
            if (rtsContract.IsInitError(methodTableHandle))
                *initializationStatus |= MethodTableInitializationFlags.MethodTableInitializationFailed;
        }
        catch (System.Exception ex)
        {
            hr = ex.HResult;
        }
#if DEBUG
        if (_legacyImpl14 is not null)
        {
            MethodTableInitializationFlags initializationStatusLocal;
            int hrLocal = _legacyImpl14.GetMethodTableInitializationFlags(methodTable, &initializationStatusLocal);
            Debug.ValidateHResult(hr, hrLocal);
            if (hr == HResults.S_OK)
            {
                Debug.Assert(*initializationStatus == initializationStatusLocal);
            }
        }
#endif
        return hr;
    }

    #endregion ISOSDacInterface14

    #region ISOSDacInterface15
    [GeneratedComClass]
    internal sealed unsafe partial class SOSMethodEnum : ISOSMethodEnum
    {
        private readonly Target _target;
        private readonly IRuntimeTypeSystem _rts;
        private readonly TypeHandle _methodTable;

        private readonly ISOSMethodEnum? _legacyMethodEnum;

        private uint _iteratorIndex;
        private List<SOSMethodData> _methods = [];

        public SOSMethodEnum(Target target, TypeHandle methodTable, ISOSMethodEnum? legacyMethodEnum)
        {
            _target = target;
            _rts = _target.Contracts.RuntimeTypeSystem;
            _methodTable = methodTable;
            _legacyMethodEnum = legacyMethodEnum;

            PopulateMethods();
        }

        private void PopulateMethods()
        {
            ushort numVtableSlots = _rts.GetNumVtableSlots(_methodTable);

            for (ushort i = 0; i < numVtableSlots; i++)
            {
                SOSMethodData methodData = default;
                TargetPointer mdAddr = TargetPointer.Null;
                try
                {
                    mdAddr = _rts.GetMethodDescForSlot(_methodTable, i);
                }
                catch (System.Exception)
                {
                    // Ignore exceptions reading method data
                }

                if (mdAddr != TargetPointer.Null)
                {
                    MethodDescHandle mdh = _rts.GetMethodDescHandle(mdAddr);

                    methodData.MethodDesc = mdAddr.ToClrDataAddress(_target);

                    TargetPointer mtAddr = _rts.GetMethodTable(mdh);
                    methodData.DefiningMethodTable = mtAddr.ToClrDataAddress(_target);

                    TypeHandle typeHandle = _rts.GetTypeHandle(mtAddr);
                    methodData.DefiningModule = _rts.GetModule(typeHandle).ToClrDataAddress(_target);
                    methodData.Token = _rts.GetMethodToken(mdh);
                }

                methodData.Entrypoint = _rts.GetSlot(_methodTable, i).ToClrDataAddress(_target);
                methodData.Slot = i;

                _methods.Add(methodData);
            }

            foreach (TargetPointer mdAddr in _rts.GetIntroducedMethodDescs(_methodTable))
            {
                MethodDescHandle mdh = _rts.GetMethodDescHandle(mdAddr);
                ushort slot = _rts.GetSlotNumber(mdh);
                if (slot >= numVtableSlots)
                {
                    SOSMethodData methodData = default;
                    methodData.MethodDesc = mdAddr.ToClrDataAddress(_target);
                    methodData.Entrypoint = _rts.GetMethodEntryPointIfExists(mdh).ToClrDataAddress(_target);

                    TargetPointer mtAddr = _rts.GetMethodTable(mdh);
                    methodData.DefiningMethodTable = mtAddr.ToClrDataAddress(_target);

                    TypeHandle typeHandle = _rts.GetTypeHandle(mtAddr);
                    methodData.DefiningModule = _rts.GetModule(typeHandle).ToClrDataAddress(_target);
                    methodData.Token = _rts.GetMethodToken(mdh);

                    if (slot == ushort.MaxValue)
                        methodData.Slot = uint.MaxValue;
                    else
                        methodData.Slot = slot;

                    _methods.Add(methodData);
                }
            }
        }

        int ISOSMethodEnum.Next(uint count, [In, Out, MarshalUsing(CountElementName = nameof(count))] SOSMethodData[] values, uint* pNeeded)
        {
            int hr = HResults.S_OK;
            try
            {
                if (pNeeded is null)
                    throw new NullReferenceException();
                if (values is null)
                    throw new NullReferenceException();

                uint i = 0;
                while (i < count && _iteratorIndex < _methods.Count)
                {
                    values[i++] = _methods[(int)_iteratorIndex++];
                }

                *pNeeded = i;

                hr = i < count ? HResults.S_FALSE : HResults.S_OK;
            }
            catch (System.Exception ex)
            {
                hr = ex.HResult;
            }

#if DEBUG
            if (_legacyMethodEnum is not null)
            {
                SOSMethodData[] valuesLocal = new SOSMethodData[count];
                uint neededLocal;
                int hrLocal = _legacyMethodEnum.Next(count, valuesLocal, &neededLocal);

                Debug.ValidateHResult(hr, hrLocal);
                if (hr == HResults.S_OK || hr == HResults.S_FALSE)
                {
                    Debug.Assert(*pNeeded == neededLocal, $"cDAC: {*pNeeded}, DAC: {neededLocal}");
                    for (uint i = 0; i < *pNeeded; i++)
                    {
                        Debug.Assert(values[i].MethodDesc == valuesLocal[i].MethodDesc, $"cDAC: {values[i].MethodDesc:x}, DAC: {valuesLocal[i].MethodDesc:x}");
                        Debug.Assert(values[i].DefiningMethodTable == valuesLocal[i].DefiningMethodTable, $"cDAC: {values[i].DefiningMethodTable:x}, DAC: {valuesLocal[i].DefiningMethodTable:x}");
                        Debug.Assert(values[i].DefiningModule == valuesLocal[i].DefiningModule, $"cDAC: {values[i].DefiningModule:x}, DAC: {valuesLocal[i].DefiningModule:x}");
                        Debug.Assert(values[i].Token == valuesLocal[i].Token, $"cDAC: {values[i].Token}, DAC: {valuesLocal[i].Token}");
                        Debug.Assert(values[i].Entrypoint == valuesLocal[i].Entrypoint, $"cDAC: {values[i].Entrypoint:x}, DAC: {valuesLocal[i].Entrypoint:x}");
                        Debug.Assert(values[i].Slot == valuesLocal[i].Slot, $"cDAC: {values[i].Slot}, DAC: {valuesLocal[i].Slot}");
                    }
                }
            }
#endif

            return hr;
        }

        int ISOSEnum.Skip(uint count)
        {
            _iteratorIndex += count;
#if DEBUG
            _legacyMethodEnum?.Skip(count);
#endif
            return HResults.S_OK;
        }

        int ISOSEnum.Reset()
        {
            _iteratorIndex = 0;
#if DEBUG
            _legacyMethodEnum?.Reset();
#endif
            return HResults.S_OK;
        }

        int ISOSEnum.GetCount(uint* pCount)
        {
            int hr = HResults.S_OK;
            try
            {
                if (pCount == null)
                    throw new NullReferenceException();
#if DEBUG
                if (_legacyMethodEnum is not null)
                {
                    uint countLocal;
                    _legacyMethodEnum.GetCount(&countLocal);
                    Debug.Assert(countLocal == (uint)_methods.Count);
                }
#endif
                *pCount = (uint)_methods.Count;
            }
            catch (System.Exception ex)
            {
                hr = ex.HResult;
            }
            return hr;
        }
    }

    int ISOSDacInterface15.GetMethodTableSlotEnumerator(ClrDataAddress mt, DacComNullableByRef<ISOSMethodEnum> enumerator)
    {
        int hr = HResults.S_OK;

        try
        {
            if (mt == 0)
                throw new ArgumentException();

            IRuntimeTypeSystem rts = _target.Contracts.RuntimeTypeSystem;
            TypeHandle methodTableHandle = rts.GetTypeHandle(mt.ToTargetPointer(_target));

            ISOSMethodEnum? legacyMethodEnum = null;
#if DEBUG
            if (_legacyImpl15 is not null)
            {
                DacComNullableByRef<ISOSMethodEnum> legacyOut = new(isNullRef: false);
                int hrLocal = _legacyImpl15.GetMethodTableSlotEnumerator(mt, legacyOut);
                Debug.ValidateHResult(hr, hrLocal);
                legacyMethodEnum = legacyOut.Interface;
            }
#endif

            enumerator.Interface = new SOSMethodEnum(_target, methodTableHandle, legacyMethodEnum);
        }
        catch (System.Exception ex)
        {
            hr = ex.HResult;
        }

        return hr;
    }
    #endregion ISOSDacInterface15

    #region ISOSDacInterface16
    int ISOSDacInterface16.GetGCDynamicAdaptationMode(int* pDynamicAdaptationMode)
    {
        int hr = HResults.S_OK;

        try
        {
            if (pDynamicAdaptationMode == null)
                throw new ArgumentException();

            IGC gc = _target.Contracts.GC;
            if (gc.TryGetGCDynamicAdaptationMode(out int mode))
            {
                *pDynamicAdaptationMode = mode;
            }
            else
            {
                *pDynamicAdaptationMode = -1;
                hr = HResults.S_FALSE;
            }
        }
        catch (System.Exception ex)
        {
            hr = ex.HResult;
        }

#if DEBUG
        if (_legacyImpl16 is not null)
        {
            int dynamicAdaptationModeLocal;
            int hrLocal = _legacyImpl16.GetGCDynamicAdaptationMode(&dynamicAdaptationModeLocal);
            Debug.ValidateHResult(hr, hrLocal);
            if (hr == HResults.S_OK || hr == HResults.S_FALSE)
            {
                Debug.Assert(pDynamicAdaptationMode == null || *pDynamicAdaptationMode == dynamicAdaptationModeLocal);
            }
        }
#endif
        return hr;
    }
    #endregion ISOSDacInterface16
}