File: Compiler\ObjectWriter\ElfObjectWriter.Aot.cs
Web Access
Project: src\src\runtime\src\coreclr\tools\aot\ILCompiler.Compiler\ILCompiler.Compiler.csproj (ILCompiler.Compiler)
// 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.Buffers.Binary;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Numerics;
using System.Reflection;
using ILCompiler.DependencyAnalysis;
using ILCompiler.DependencyAnalysisFramework;
using Internal.Text;
using Internal.TypeSystem;
using static ILCompiler.DependencyAnalysis.RelocType;
using static ILCompiler.ObjectWriter.EabiNative;
using static ILCompiler.ObjectWriter.ElfNative;

namespace ILCompiler.ObjectWriter
{
    /// <summary>
    /// ELF object file format writer for Linux/Unix targets.
    /// </summary>
    /// <remarks>
    /// ELF object format is described by the official specification hosted
    /// at https://refspecs.linuxfoundation.org/elf/elf.pdf. Different
    /// architectures specify the details in the ABI specification.
    ///
    /// Like COFF there are several quirks related to large number of sections
    /// (> 65279). Some of the fields in the ELF file header are moved to the
    /// first (NULL) section header. The symbol table that is normally a single
    /// section in the file is extended with a second .symtab_shndx section
    /// to accommodate the section indexes that don't fit within the regular
    /// section number field.
    /// </remarks>
    internal sealed partial class ElfObjectWriter : UnixObjectWriter
    {
        private Dictionary<int, (SectionWriter ExidxSectionWriter, SectionWriter ExtabSectionWriter)> _armUnwindSections;
        private static readonly ObjectNodeSection ArmUnwindIndexSection = new ObjectNodeSection(".ARM.exidx", SectionType.UnwindData);
        private static readonly ObjectNodeSection ArmUnwindTableSection = new ObjectNodeSection(".ARM.extab", SectionType.ReadOnly);

        private protected override void CreateEhSections()
        {
            // ARM creates the EHABI sections lazily in EmitUnwindInfo
            if (_machine is not EM_ARM)
            {
                base.CreateEhSections();
            }
        }

        private protected override void EmitUnwindInfo(
            SectionWriter sectionWriter,
            INodeWithCodeInfo nodeWithCodeInfo,
            Utf8String currentSymbolName)
        {
            if (_machine is not EM_ARM)
            {
                base.EmitUnwindInfo(sectionWriter, nodeWithCodeInfo, currentSymbolName);
                return;
            }

            if (nodeWithCodeInfo.FrameInfos is FrameInfo[] frameInfos &&
                nodeWithCodeInfo is ISymbolDefinitionNode)
            {
                SectionWriter exidxSectionWriter;
                SectionWriter extabSectionWriter;

                if (ShouldShareSymbol((ObjectNode)nodeWithCodeInfo))
                {
                    exidxSectionWriter = GetOrCreateSection(ArmUnwindIndexSection, currentSymbolName, Utf8String.Concat("_unwind0"u8, currentSymbolName.AsSpan()));
                    extabSectionWriter = GetOrCreateSection(ArmUnwindTableSection, currentSymbolName, Utf8String.Concat("_extab0"u8, currentSymbolName.AsSpan()));
                    _sections[exidxSectionWriter.SectionIndex].LinkSection = _sections[sectionWriter.SectionIndex];
                }
                else
                {
                    _armUnwindSections ??= new();
                    if (_armUnwindSections.TryGetValue(sectionWriter.SectionIndex, out var unwindSections))
                    {
                        exidxSectionWriter = unwindSections.ExidxSectionWriter;
                        extabSectionWriter = unwindSections.ExtabSectionWriter;
                    }
                    else
                    {
                        string sectionName = _sections[sectionWriter.SectionIndex].Name;
                        exidxSectionWriter = GetOrCreateSection(new ObjectNodeSection($"{ArmUnwindIndexSection.Name}{sectionName}", ArmUnwindIndexSection.Type));
                        extabSectionWriter = GetOrCreateSection(new ObjectNodeSection($"{ArmUnwindTableSection.Name}{sectionName}", ArmUnwindTableSection.Type));
                        _sections[exidxSectionWriter.SectionIndex].LinkSection = _sections[sectionWriter.SectionIndex];
                        _armUnwindSections.Add(sectionWriter.SectionIndex, (exidxSectionWriter, extabSectionWriter));
                    }
                }

                long mainLsdaOffset = 0;
                Span<byte> unwindWord = stackalloc byte[4];
                Span<byte> i_str = stackalloc byte[16];
                for (int i = 0; i < frameInfos.Length; i++)
                {
                    FrameInfo frameInfo = frameInfos[i];
                    int start = frameInfo.StartOffset;
                    int end = frameInfo.EndOffset;
                    byte[] blob = frameInfo.BlobData;

                    Utf8String framSymbolName = _utf8StringBuilder.Clear().Append("_fram"u8).Append(FormatUtf8Int(i_str, i)).Append(currentSymbolName).ToUtf8String();
                    Utf8String extabSymbolName = _utf8StringBuilder.Clear().Append("_extab"u8).Append(FormatUtf8Int(i_str, i)).Append(currentSymbolName).ToUtf8String();

                    sectionWriter.EmitSymbolDefinition(framSymbolName, start);

                    // Emit the index info
                    exidxSectionWriter.EmitSymbolReference(IMAGE_REL_ARM_PREL31, framSymbolName);
                    exidxSectionWriter.EmitSymbolReference(IMAGE_REL_ARM_PREL31, extabSymbolName);

                    Span<byte> armUnwindInfo = EabiUnwindConverter.ConvertCFIToEabi(blob);
                    string personalitySymbolName;

                    if (armUnwindInfo.Length <= 3)
                    {
                        personalitySymbolName = "__aeabi_unwind_cpp_pr0";
                        unwindWord[3] = 0x80;
                        unwindWord[2] = (byte)(armUnwindInfo.Length > 0 ? armUnwindInfo[0] : 0xB0);
                        unwindWord[1] = (byte)(armUnwindInfo.Length > 1 ? armUnwindInfo[1] : 0xB0);
                        unwindWord[0] = (byte)(armUnwindInfo.Length > 2 ? armUnwindInfo[2] : 0xB0);
                        armUnwindInfo = Span<byte>.Empty;
                    }
                    else
                    {
                        Debug.Assert(armUnwindInfo.Length <= 1024);
                        personalitySymbolName = "__aeabi_unwind_cpp_pr1";
                        unwindWord[3] = 0x81;
                        unwindWord[2] = (byte)(((armUnwindInfo.Length - 2) + 3) / 4);
                        unwindWord[1] = armUnwindInfo[0];
                        unwindWord[0] = armUnwindInfo[1];
                        armUnwindInfo = armUnwindInfo.Slice(2);
                    }

                    extabSectionWriter.EmitAlignment(4);
                    extabSectionWriter.EmitSymbolDefinition(extabSymbolName);

                    // ARM EHABI requires emitting a dummy relocation to the personality routine
                    // to tell the linker to preserve it.
                    extabSectionWriter.EmitRelocation(0, unwindWord, IMAGE_REL_BASED_ABSOLUTE, new Utf8String(personalitySymbolName), 0);

                    // Emit the unwinding code. First word specifies the personality routine,
                    // format and first few bytes of the unwind code. For longer unwind codes
                    // the other words follow. They are padded with the "finish" instruction
                    // (0xB0).
                    extabSectionWriter.Write(unwindWord);
                    while (armUnwindInfo.Length > 0)
                    {
                        unwindWord[3] = (byte)(armUnwindInfo.Length > 0 ? armUnwindInfo[0] : 0xB0);
                        unwindWord[2] = (byte)(armUnwindInfo.Length > 1 ? armUnwindInfo[1] : 0xB0);
                        unwindWord[1] = (byte)(armUnwindInfo.Length > 2 ? armUnwindInfo[2] : 0xB0);
                        unwindWord[0] = (byte)(armUnwindInfo.Length > 3 ? armUnwindInfo[3] : 0xB0);
                        extabSectionWriter.Write(unwindWord);
                        armUnwindInfo = armUnwindInfo.Length > 3 ? armUnwindInfo.Slice(4) : Span<byte>.Empty;
                    }

                    // Emit our LSDA info directly into the exception table
                    EmitLsda(nodeWithCodeInfo, frameInfos, i, extabSectionWriter, ref mainLsdaOffset);
                }
            }
        }
    }
}