File: Contracts\ExecutionManager\Helpers\UnwindDataSize.cs
Web Access
Project: src\src\runtime\src\native\managed\cdac\Microsoft.Diagnostics.DataContractReader.Contracts\Microsoft.Diagnostics.DataContractReader.Contracts.csproj (Microsoft.Diagnostics.DataContractReader.Contracts)
// 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.ExecutionManagerHelpers;

internal static class UnwindDataSize
{
    public static uint GetUnwindDataSize(Target target, TargetPointer unwindInfo, RuntimeInfoArchitecture arch)
    {
        switch (arch)
        {
            case RuntimeInfoArchitecture.X86:
                return sizeof(uint);
            case RuntimeInfoArchitecture.X64:
            {
                // see https://learn.microsoft.com/cpp/build/exception-handling-x64
                int sizeOfUnwindCode = 2; // from spec
                int unwindCodeOffset = 4; // from spec
                int countOfUnwindCodes = target.Read<byte>(unwindInfo + 2); // from spec
                return AlignUp(
                    unwindCodeOffset +
                    (countOfUnwindCodes * sizeOfUnwindCode) +
                    sizeof(uint) /* personality routine is always present */,
                    sizeof(uint));
            }
            case RuntimeInfoArchitecture.Arm:
            case RuntimeInfoArchitecture.Arm64:
            {
                TargetPointer xdata = unwindInfo;
                uint xdata0 = target.Read<uint>(xdata);
                uint size = 4; // initial header
                uint unwindWords;
                uint epilogScopes;
                if (arch == RuntimeInfoArchitecture.Arm)
                {
                    // See https://learn.microsoft.com/cpp/build/arm-exception-handling
                    unwindWords = xdata0 >> 28;
                    epilogScopes = (xdata0 >> 23) & 0x1F;
                }
                else
                {
                    // See https://learn.microsoft.com/cpp/build/arm64-exception-handling
                    unwindWords = xdata0 >> 27;
                    epilogScopes = (xdata0 >> 22) & 0x1F;
                }
                if (unwindWords == 0 && epilogScopes == 0)
                {
                    size += 4;
                    uint xdata1 = target.Read<uint>(xdata + 4);
                    unwindWords = (xdata1 >> 16) & 0xff;
                    epilogScopes = xdata1 & 0xffff;
                }

                if ((xdata0 & (1 << 21)) == 0)
                    size += 4 * epilogScopes;

                size += 4 * unwindWords;
                size += 4;
                return size;
            }

            case RuntimeInfoArchitecture.LoongArch64:
            case RuntimeInfoArchitecture.RiscV64:
            {
                TargetPointer xdata = unwindInfo;
                uint xdata0 = target.Read<uint>(xdata);
                uint size;
                uint unwindWords;
                uint epilogScopes;

                unwindWords = (xdata0 >> 27) & 0x1f;
                epilogScopes = (xdata0 >> 22) & 0x1f;

                // If either Epilog Count or Code Word is non-zero,
                // epilog/unwind scope info is in the 1-word header.
                // Otherwise this info is given by a 2-word header.
                if (unwindWords != 0 || epilogScopes != 0)
                {
                    size = 4;
                }
                else
                {
                    size = 8;
                    uint xdata1 = target.Read<uint>(xdata + 4);
                    epilogScopes = xdata1 & 0xffff;
                    unwindWords = (xdata1 >> 16) & 0xff;
                }

                if ((xdata0 & (1 << 21)) == 0)
                    size += 4 * epilogScopes;

                size += 4 * unwindWords;

                size += 4; // exception handler RVA
                return size;
            }
            default:
                throw new NotSupportedException($"GetUnwindDataSize not supported for architecture: {arch}");
        }
    }

    private static uint AlignUp(int offset, int align)
        => (uint)((offset + align - 1) & ~(align - 1));
}