File: Compiler\ObjectWriter\Dwarf\DwarfInfoWriter.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;
using System.Buffers.Binary;
using System.Collections.Generic;
using System.Diagnostics;
using ILCompiler.DependencyAnalysis;
using Internal.Text;
using Internal.TypeSystem;

namespace ILCompiler.ObjectWriter
{
    internal sealed class DwarfInfoWriter : IDisposable
    {
        private sealed record InfoReference(uint TypeIndex, int Position, byte[] Data);

        private readonly SectionWriter _infoSectionWriter;
        private readonly SectionWriter _stringTableWriter;
        private readonly SectionWriter _abbrevSectionWriter;
        private readonly SectionWriter _locSectionWriter;
        private readonly SectionWriter _rangeSectionWriter;
        private readonly DwarfBuilder _builder;
        private readonly RelocType _codeRelocType;
        private readonly List<InfoReference> _lateBoundReferences = new();
        private readonly Stack<DwarfAbbrev> _dieStack = new();
        private readonly Dictionary<DwarfAbbrev, int> _usedAbbrevs = new();
        private readonly ArrayBufferWriter<byte> _expressionBufferWriter = new();

        public DwarfInfoWriter(
            SectionWriter infoSectionWriter,
            SectionWriter stringTableWriter,
            SectionWriter abbrevSectionWriter,
            SectionWriter locSectionWriter,
            SectionWriter rangeSectionWriter,
            DwarfBuilder builder,
            RelocType codeRelocType)
        {
            _infoSectionWriter = infoSectionWriter;
            _stringTableWriter = stringTableWriter;
            _abbrevSectionWriter = abbrevSectionWriter;
            _locSectionWriter = locSectionWriter;
            _rangeSectionWriter = rangeSectionWriter;
            _builder = builder;
            _codeRelocType = codeRelocType;
        }

        public TargetArchitecture TargetArchitecture => _builder.TargetArchitecture;
        public int FrameRegister => _builder.FrameRegister;
        public byte TargetPointerSize => _builder.TargetPointerSize;
        public long Position => _infoSectionWriter.Position;

        public void WriteStartDIE(DwarfAbbrev abbrev)
        {
            if (_dieStack.Count > 0 && !_dieStack.Peek().HasChildren)
            {
                throw new InvalidOperationException($"Trying to write a children into DIE (Tag {_dieStack.Peek().Tag}) with DW_CHILDREN_no");
            }

            if (!_usedAbbrevs.TryGetValue(abbrev, out int abbreviationCode))
            {
                abbreviationCode = _usedAbbrevs.Count + 1;
                _usedAbbrevs.Add(abbrev, abbreviationCode);
            }


            _dieStack.Push(abbrev);
            WriteULEB128((ulong)abbreviationCode);
        }

        public void WriteEndDIE()
        {
            var abbrev = _dieStack.Pop();
            if (abbrev.HasChildren)
            {
                // End children list
                WriteUInt8(0);
            }
        }

        public void Write(ReadOnlySpan<byte> buffer) => _infoSectionWriter.Write(buffer);
        public void WriteULEB128(ulong value) => _infoSectionWriter.WriteULEB128(value);
        public void WriteUInt8(byte value) => _infoSectionWriter.WriteLittleEndian<byte>(value);
        public void WriteUInt16(ushort value) => _infoSectionWriter.WriteLittleEndian<ushort>(value);
        public void WriteUInt32(uint value) => _infoSectionWriter.WriteLittleEndian<uint>(value);
        public void WriteUInt64(ulong value) => _infoSectionWriter.WriteLittleEndian<ulong>(value);

        public void WriteAddressSize(ulong value)
        {
            switch (TargetPointerSize)
            {
                case 4: WriteUInt32((uint)value); break;
                case 8: WriteUInt64(value); break;
                default: throw new NotSupportedException();
            }
        }

        public void WriteStringReference(Utf8String value)
        {
            long stringsOffset = _stringTableWriter.Position;
            _stringTableWriter.WriteUtf8String(value);

            Debug.Assert(stringsOffset < uint.MaxValue);
            _infoSectionWriter.EmitSymbolReference(RelocType.IMAGE_REL_BASED_HIGHLOW, new Utf8String(".debug_str"u8), stringsOffset);
        }

        public void WriteStringReference(string value)
        {
            long stringsOffset = _stringTableWriter.Position;
            _stringTableWriter.WriteUtf8String(value);

            Debug.Assert(stringsOffset < uint.MaxValue);
            _infoSectionWriter.EmitSymbolReference(RelocType.IMAGE_REL_BASED_HIGHLOW, new Utf8String(".debug_str"u8), stringsOffset);
        }

        public void WriteInfoAbsReference(long offset)
        {
            Debug.Assert(offset < uint.MaxValue);
            WriteUInt32((uint)offset);
        }

        public void WriteInfoReference(uint typeIndex)
        {
            uint offset = _builder.ResolveOffset(typeIndex);

            if (offset == 0)
            {
                // Late bound forward reference
                var data = new byte[sizeof(uint)];
                _lateBoundReferences.Add(new InfoReference(typeIndex, (int)_infoSectionWriter.Position, data));
                _infoSectionWriter.EmitData(data);
            }
            else
            {
                WriteUInt32(offset);
            }
        }

        public void WriteCodeReference(Utf8String sectionSymbolName, long offset = 0)
        {
            Debug.Assert(offset >= 0);
            _infoSectionWriter.EmitSymbolReference(_codeRelocType, sectionSymbolName, offset);
        }

        public void WriteLineReference(long offset)
        {
            Debug.Assert(offset < uint.MaxValue);
            _infoSectionWriter.EmitSymbolReference(RelocType.IMAGE_REL_BASED_HIGHLOW, new Utf8String(".debug_line"u8), offset);
        }

        public DwarfExpressionBuilder GetExpressionBuilder()
        {
            _expressionBufferWriter.ResetWrittenCount();
            return new DwarfExpressionBuilder(TargetArchitecture, TargetPointerSize, _expressionBufferWriter);
        }

        public void WriteExpression(DwarfExpressionBuilder expressionBuilder)
        {
            _ = expressionBuilder;
            WriteULEB128((uint)_expressionBufferWriter.WrittenCount);
            Write(_expressionBufferWriter.WrittenSpan);
        }

        public void WriteStartLocationList()
        {
            long offset = _locSectionWriter.Position;
            Debug.Assert(offset < uint.MaxValue);
            _infoSectionWriter.EmitSymbolReference(RelocType.IMAGE_REL_BASED_HIGHLOW, new Utf8String(".debug_loc"u8), (int)offset);
        }

        public void WriteLocationListExpression(Utf8String methodName, long startOffset, long endOffset, DwarfExpressionBuilder expressionBuilder)
        {
            _ = expressionBuilder;
            _locSectionWriter.EmitSymbolReference(_codeRelocType, methodName, startOffset);
            _locSectionWriter.EmitSymbolReference(_codeRelocType, methodName, endOffset);
            _locSectionWriter.WriteLittleEndian<ushort>((ushort)_expressionBufferWriter.WrittenCount);
            _locSectionWriter.Write(_expressionBufferWriter.WrittenSpan);
        }

        public void WriteEndLocationList()
        {
            _locSectionWriter.WritePadding(TargetPointerSize * 2);
        }

        public void WriteStartRangeList()
        {
            long offset = _rangeSectionWriter.Position;
            Debug.Assert(offset < uint.MaxValue);
            _infoSectionWriter.EmitSymbolReference(RelocType.IMAGE_REL_BASED_HIGHLOW, new Utf8String(".debug_ranges"u8), offset);
        }

        public void WriteRangeListEntry(Utf8String symbolName, long startOffset, long endOffset)
        {
            _rangeSectionWriter.EmitSymbolReference(_codeRelocType, symbolName, startOffset);
            _rangeSectionWriter.EmitSymbolReference(_codeRelocType, symbolName, endOffset);
        }

        public void WriteEndRangeList()
        {
            _rangeSectionWriter.WritePadding(TargetPointerSize * 2);
        }

        public void Dispose()
        {
            // Debug.Assert(_dieStack.Count == 0);

            // Flush late bound forward references
            foreach (var lateBoundReference in _lateBoundReferences)
            {
                uint offset = _builder.ResolveOffset(lateBoundReference.TypeIndex);
                BinaryPrimitives.WriteUInt32LittleEndian(lateBoundReference.Data, offset);
            }

            // Write abbreviation section
            foreach ((DwarfAbbrev abbrev, int abbreviationCode) in _usedAbbrevs)
            {
                _abbrevSectionWriter.WriteULEB128((ulong)abbreviationCode);
                abbrev.Write(_abbrevSectionWriter, TargetPointerSize);
            }
            _abbrevSectionWriter.Write([0, 0]);
        }
    }
}