File: ConversionExtensions.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.Diagnostics;
using Microsoft.Diagnostics.DataContractReader.Contracts;

namespace Microsoft.Diagnostics.DataContractReader.Legacy;

public static class ConversionExtensions
{
    private const uint Arm32ThumbBit = 1;

    /// <summary>
    /// Converts a TargetPointer to a ClrDataAddress using sign extension if required.
    /// </summary>
    public static ClrDataAddress ToClrDataAddress(this TargetPointer address, Target target)
    {
        if (target.PointerSize == sizeof(ulong))
        {
            return address.Value;
        }
        else
        {
            return (ulong)(int)address.Value;
        }
    }

    /// <summary>
    /// Converts a TargetCodePointer to a ClrDataAddress using sign extension if required.
    /// </summary>
    public static ClrDataAddress ToClrDataAddress(this TargetCodePointer address, Target target)
    {
        if (target.PointerSize == sizeof(ulong))
        {
            return address.Value;
        }
        else
        {
            return (ulong)(int)address.Value;
        }
    }

    /// <summary>
    /// Converts a ClrDataAddress to a TargetPointer, ensuring the address is within the valid range for the target platform.
    /// When overrideCheck is true, this will not check the range and will allow any address. This is used on legacy endpoints which
    /// may pass in invalid ClrDataAddress values.
    /// </summary>
    public static TargetPointer ToTargetPointer(this ClrDataAddress address, Target target, bool overrideCheck = false)
    {
        if (target.PointerSize == sizeof(ulong))
        {
            return new TargetPointer(address);
        }
        else
        {
            long signedAddr = (long)address.Value;
            if (!overrideCheck && (signedAddr > int.MaxValue || signedAddr < int.MinValue))
            {
                throw new ArgumentException($"ClrDataAddress 0x{address.Value:x} out of range for the target platform.", nameof(address));
            }
            return new TargetPointer((uint)address);
        }
    }

    /// <summary>
    /// Converts a ClrDataAddress to a TargetCodePointer, ensuring the address is within the valid range for the target platform.
    /// </summary>
    public static TargetCodePointer ToTargetCodePointer(this ClrDataAddress address, Target target)
    {
        if (target.PointerSize == sizeof(ulong))
        {
            return new TargetCodePointer(address);
        }
        else
        {
            long signedAddr = (long)address.Value;
            if (signedAddr > int.MaxValue || signedAddr < int.MinValue)
            {
                throw new ArgumentException($"ClrDataAddress 0x{address.Value:x} out of range for the target platform.", nameof(address));
            }
            return new TargetCodePointer((uint)address);
        }
    }

    /// <summary>
    /// Converts a TargetCodePointer to an address TargetPointer, removing any platform-specific bits such as the ARM32 Thumb bit or ARM64 pointer authentication.
    /// </summary>
    public static TargetPointer ToAddress(this TargetCodePointer code, Target target)
    {
        IPlatformMetadata metadata = target.Contracts.PlatformMetadata;
        CodePointerFlags flags = metadata.GetCodePointerFlags();
        if (flags.HasFlag(CodePointerFlags.HasArm32ThumbBit))
        {
            return new TargetPointer(code.Value & ~Arm32ThumbBit);
        }
        else if (flags.HasFlag(CodePointerFlags.HasArm64PtrAuth))
        {
            throw new NotImplementedException($"{nameof(ToAddress)}: ARM64 with pointer authentication");
        }
        Debug.Assert(flags == default);
        return new TargetPointer(code.Value);
    }
}