File: Compiler\DependencyAnalysis\ReadyToRun\ExceptionInfoLookupTableNode.cs
Web Access
Project: src\src\runtime\src\coreclr\tools\aot\ILCompiler.ReadyToRun\ILCompiler.ReadyToRun.csproj (ILCompiler.ReadyToRun)
// 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;
using System.Collections.Generic;
using ILCompiler.DependencyAnalysisFramework;
using Internal.Text;
using Internal.TypeSystem;

using Debug = System.Diagnostics.Debug;

namespace ILCompiler.DependencyAnalysis.ReadyToRun
{
    public class EHInfoNode : ObjectNode, ISymbolDefinitionNode
    {
        public override ObjectNodeSection GetSection(NodeFactory factory) => ObjectNodeSection.ReadOnlyDataSection;

        public override bool IsShareable => false;

        public override int ClassCode => 354769871;

        public override bool StaticDependenciesAreComputed => true;

        public int Offset => 0;

        private ArrayBuilder<byte> _ehInfoBuilder;

        public EHInfoNode()
        {
            _ehInfoBuilder = new ArrayBuilder<byte>();
        }

        public int AddEHInfo(byte[] ehInfo)
        {
            int offset = _ehInfoBuilder.Count;
            _ehInfoBuilder.Append(ehInfo);
            return offset;
        }

        public int Count => _ehInfoBuilder.Count;

        public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb)
        {
            // EH info node is a singleton in the R2R PE file
            sb.Append("EHInfoNode"u8);
        }

        public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false)
        {
            return new ObjectData(_ehInfoBuilder.ToArray(), Array.Empty<Relocation>(), alignment: 4, definedSymbols: new ISymbolDefinitionNode[] { this });
        }

        protected override string GetName(NodeFactory context)
        {
            Utf8StringBuilder sb = new Utf8StringBuilder();
            AppendMangledName(context.NameMangler, sb);
            return sb.ToString();
        }
    }

    public class ExceptionInfoLookupTableNode : HeaderTableNode
    {
        private List<MethodWithGCInfo> _methodNodes;
        private List<int> _ehInfoOffsets;

        private readonly NodeFactory _nodeFactory;

        private readonly EHInfoNode _ehInfoNode;

        public ExceptionInfoLookupTableNode(NodeFactory nodeFactory)
        {
            _nodeFactory = nodeFactory;
            _ehInfoNode = new EHInfoNode();
        }

        public override void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb)
        {
            sb.Append(nameMangler.CompilationUnitPrefix);
            sb.Append("__ReadyToRunExceptionInfoLookupTable@"u8);
            sb.Append(Offset.ToString());
        }

        internal void LayoutMethodsWithEHInfo()
        {
            if (_methodNodes != null)
            {
                // Already initialized
                return;
            }

            _methodNodes = new List<MethodWithGCInfo>();
            _ehInfoOffsets = new List<int>();

            foreach (MethodWithGCInfo method in _nodeFactory.EnumerateCompiledMethods())
            {
                ObjectData ehInfo = method.EHInfo;
                if (ehInfo != null && ehInfo.Data.Length != 0)
                {
                    _methodNodes.Add(method);
                    _ehInfoOffsets.Add(_ehInfoNode.AddEHInfo(ehInfo.Data));
                }
            }
        }

        public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false)
        {
            if (relocsOnly)
            {
                return new ObjectData(Array.Empty<byte>(), Array.Empty<Relocation>(), 1, new ISymbolDefinitionNode[] { this });
            }

            LayoutMethodsWithEHInfo();

            ObjectDataBuilder exceptionInfoLookupBuilder = new ObjectDataBuilder(factory, relocsOnly);
            exceptionInfoLookupBuilder.RequireInitialAlignment(2 * sizeof(uint));

            // Add the symbol representing this object node
            exceptionInfoLookupBuilder.AddSymbol(this);

            // First, emit the actual EH records in sequence and store map from methods to the EH record symbols
            for (int index = 0; index < _methodNodes.Count; index++)
            {
                exceptionInfoLookupBuilder.EmitReloc(_methodNodes[index], RelocType.IMAGE_REL_BASED_ADDR32NB);
                exceptionInfoLookupBuilder.EmitReloc(_ehInfoNode, RelocType.IMAGE_REL_BASED_ADDR32NB, _ehInfoOffsets[index]);
            }

            // Sentinel record - method RVA = -1, EH info offset = end of the EH info block
            exceptionInfoLookupBuilder.EmitUInt(~0u);
            exceptionInfoLookupBuilder.EmitReloc(_ehInfoNode, RelocType.IMAGE_REL_BASED_ADDR32NB, _ehInfoNode.Count);

            return exceptionInfoLookupBuilder.ToObjectData();
        }

        /// <summary>
        /// CoreCLR runtime asserts that, when the EXCEPTION_INFO table is present, it must have at least
        /// two entries. When we don't have any EH info to emit, we just skip the entire table.
        /// </summary>
        /// <param name="factory">Reference node factory</param>
        /// <returns></returns>
        public override bool ShouldSkipEmittingObjectNode(NodeFactory factory)
        {
            LayoutMethodsWithEHInfo();
            return _methodNodes.Count == 0;
        }

        protected override DependencyList ComputeNonRelocationBasedDependencies(NodeFactory factory)
        {
            return new DependencyNodeCore<NodeFactory>.DependencyList(new DependencyListEntry[] { new DependencyListEntry(_ehInfoNode, "EH info array") });
        }

        public override int ClassCode => 582513248;
    }
}