|
// 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.Generic;
using System.IO;
using System.Linq;
using System.Buffers.Binary;
using ILCompiler.DependencyAnalysis;
using Internal.Text;
using Internal.TypeSystem;
using Internal.TypeSystem.TypesDebugInfo;
using Debug = System.Diagnostics.Debug;
namespace ILCompiler.ObjectWriter
{
/// <summary>
/// Base implementation for ELF and Mach-O object file format writers. Implements
/// the common code for DWARF debugging and exception handling information.
/// </summary>
internal abstract partial class UnixObjectWriter : ObjectWriter
{
// Exception handling sections
private SectionWriter _lsdaSectionWriter;
private int _ehFrameSectionIndex;
private DwarfCie _dwarfCie;
private DwarfEhFrame _dwarfEhFrame;
protected int EhFrameSectionIndex => _ehFrameSectionIndex;
// Debugging
private DwarfBuilder _dwarfBuilder;
private static readonly ObjectNodeSection DebugInfoSection = new ObjectNodeSection(".debug_info", SectionType.Debug);
private static readonly ObjectNodeSection DebugStringSection = new ObjectNodeSection(".debug_str", SectionType.Debug);
private static readonly ObjectNodeSection DebugAbbrevSection = new ObjectNodeSection(".debug_abbrev", SectionType.Debug);
private static readonly ObjectNodeSection DebugLocSection = new ObjectNodeSection(".debug_loc", SectionType.Debug);
private static readonly ObjectNodeSection DebugRangesSection = new ObjectNodeSection(".debug_ranges", SectionType.Debug);
private static readonly ObjectNodeSection DebugLineSection = new ObjectNodeSection(".debug_line", SectionType.Debug);
private static readonly ObjectNodeSection DebugARangesSection = new ObjectNodeSection(".debug_aranges", SectionType.Debug);
private protected virtual bool UseFrameNames => false;
private protected virtual bool EmitCompactUnwinding(Utf8String startSymbolName, ulong length, Utf8String lsdaSymbolName, byte[] blob) => false;
private protected override void CreateEhSections()
{
SectionWriter ehFrameSectionWriter;
// Create sections for exception handling
_lsdaSectionWriter = GetOrCreateSection(LsdaSection);
ehFrameSectionWriter = GetOrCreateSection(EhFrameSection);
_lsdaSectionWriter.EmitAlignment(8);
ehFrameSectionWriter.EmitAlignment(8);
_ehFrameSectionIndex = ehFrameSectionWriter.SectionIndex;
// We always use the same CIE in DWARF EH frames, so create and emit it now
bool is64Bit = _nodeFactory.Target.Architecture switch
{
TargetArchitecture.X86 => false,
TargetArchitecture.ARM => false,
_ => true
};
_dwarfCie = new DwarfCie(_nodeFactory.Target.Architecture);
_dwarfEhFrame = new DwarfEhFrame(ehFrameSectionWriter, is64Bit);
_dwarfEhFrame.AddCie(_dwarfCie);
}
private protected void EmitLsda(
INodeWithCodeInfo nodeWithCodeInfo,
FrameInfo[] frameInfos,
int frameInfoIndex,
SectionWriter lsdaSectionWriter,
ref long mainLsdaOffset)
{
FrameInfo frameInfo = frameInfos[frameInfoIndex];
FrameInfoFlags flags = frameInfo.Flags;
if (frameInfoIndex != 0)
{
lsdaSectionWriter.WriteByte((byte)flags);
lsdaSectionWriter.WriteLittleEndian<int>((int)(mainLsdaOffset - lsdaSectionWriter.Position));
// Emit relative offset from the main function
lsdaSectionWriter.WriteLittleEndian<uint>((uint)(frameInfo.StartOffset - frameInfos[0].StartOffset));
}
else
{
MethodExceptionHandlingInfoNode ehInfo = nodeWithCodeInfo.EHInfo;
ISymbolNode associatedDataNode = nodeWithCodeInfo.GetAssociatedDataNode(_nodeFactory) as ISymbolNode;
flags |= ehInfo is not null ? FrameInfoFlags.HasEHInfo : 0;
flags |= associatedDataNode is not null ? FrameInfoFlags.HasAssociatedData : 0;
mainLsdaOffset = lsdaSectionWriter.Position;
lsdaSectionWriter.WriteByte((byte)flags);
if (associatedDataNode is not null)
{
Utf8String symbolName = GetMangledName(associatedDataNode);
lsdaSectionWriter.EmitSymbolReference(RelocType.IMAGE_REL_BASED_RELPTR32, symbolName, 0);
}
if (ehInfo is not null)
{
Utf8String symbolName = GetMangledName(ehInfo);
lsdaSectionWriter.EmitSymbolReference(RelocType.IMAGE_REL_BASED_RELPTR32, symbolName, 0);
}
if (nodeWithCodeInfo.GCInfo is not null)
{
lsdaSectionWriter.Write(nodeWithCodeInfo.GCInfo);
}
}
}
private sealed class LsdaCache
{
private sealed class LsdaComparer : IEqualityComparer<INodeWithCodeInfo>
{
public static readonly LsdaComparer Instance = new LsdaComparer();
public bool Equals(INodeWithCodeInfo x, INodeWithCodeInfo y)
{
Debug.Assert(IsCacheable(x));
Debug.Assert(IsCacheable(y));
ReadOnlySpan<byte> xGc = x.GCInfo;
ReadOnlySpan<byte> yGc = y.GCInfo;
if (!xGc.SequenceEqual(yGc))
return false;
ReadOnlySpan<FrameInfo> xFrames = x.FrameInfos;
ReadOnlySpan<FrameInfo> yFrames = y.FrameInfos;
return xFrames.SequenceEqual(yFrames);
}
public int GetHashCode(INodeWithCodeInfo obj)
{
Debug.Assert(IsCacheable(obj));
HashCode hash = default;
hash.AddBytes(obj.GCInfo);
foreach (FrameInfo f in obj.FrameInfos)
hash.Add(f.GetHashCode());
return hash.ToHashCode();
}
}
private Dictionary<INodeWithCodeInfo, Utf8String[]> _lsdas = new Dictionary<INodeWithCodeInfo, Utf8String[]>(LsdaComparer.Instance);
public static bool IsCacheable(INodeWithCodeInfo nodeWithCodeInfo)
=> nodeWithCodeInfo.EHInfo == null && !MethodAssociatedDataNode.MethodHasAssociatedData((IMethodNode)nodeWithCodeInfo);
public Utf8String[] FindCachedLsda(INodeWithCodeInfo nodeWithCodeInfo)
{
Debug.Assert(IsCacheable(nodeWithCodeInfo));
return _lsdas.GetValueOrDefault(nodeWithCodeInfo);
}
public void AddLsdaToCache(INodeWithCodeInfo nodeWithCodeInfo, Utf8String[] symbols)
{
Debug.Assert(IsCacheable(nodeWithCodeInfo));
_lsdas.Add(nodeWithCodeInfo, symbols);
}
}
private readonly LsdaCache _lsdaCache = new LsdaCache();
private protected override void EmitUnwindInfo(
SectionWriter sectionWriter,
INodeWithCodeInfo nodeWithCodeInfo,
Utf8String currentSymbolName)
{
if (nodeWithCodeInfo.FrameInfos is FrameInfo[] frameInfos &&
nodeWithCodeInfo is ISymbolDefinitionNode)
{
bool useFrameNames = UseFrameNames;
SectionWriter lsdaSectionWriter;
Utf8String[] newLsdaSymbols = null;
Utf8String[] emittedLsdaSymbols = null;
if (ShouldShareSymbol((ObjectNode)nodeWithCodeInfo))
{
lsdaSectionWriter = GetOrCreateSection(LsdaSection, currentSymbolName, Utf8String.Concat("_lsda0"u8, currentSymbolName.AsSpan()));
}
else
{
lsdaSectionWriter = _lsdaSectionWriter;
if (LsdaCache.IsCacheable(nodeWithCodeInfo) && !useFrameNames)
{
emittedLsdaSymbols = _lsdaCache.FindCachedLsda(nodeWithCodeInfo);
if (emittedLsdaSymbols == null)
newLsdaSymbols = new Utf8String[frameInfos.Length];
}
}
long mainLsdaOffset = 0;
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 lsdaSymbolName;
if (emittedLsdaSymbols != null)
{
lsdaSymbolName = emittedLsdaSymbols[i];
}
else
{
lsdaSymbolName = _utf8StringBuilder.Clear().Append("_lsda"u8).Append(FormatUtf8Int(i_str, i)).Append(currentSymbolName).ToUtf8String();
if (newLsdaSymbols != null)
newLsdaSymbols[i] = lsdaSymbolName;
lsdaSectionWriter.EmitSymbolDefinition(lsdaSymbolName);
EmitLsda(nodeWithCodeInfo, frameInfos, i, _lsdaSectionWriter, ref mainLsdaOffset);
}
Utf8String framSymbolName = _utf8StringBuilder.Clear().Append("_fram"u8).Append(FormatUtf8Int(i_str, i)).Append(currentSymbolName).ToUtf8String();
if (useFrameNames && start != 0)
{
sectionWriter.EmitSymbolDefinition(framSymbolName, start);
}
Utf8String startSymbolName = useFrameNames && start != 0 ? framSymbolName : currentSymbolName;
ulong length = (ulong)(end - start);
if (!EmitCompactUnwinding(startSymbolName, length, lsdaSymbolName, blob))
{
var fde = new DwarfFde(
_dwarfCie,
blob,
pcStartSymbolName: startSymbolName,
pcStartSymbolOffset: useFrameNames ? 0 : start,
pcLength: (ulong)(end - start),
lsdaSymbolName,
personalitySymbolName: default);
_dwarfEhFrame.AddFde(fde);
}
}
if (newLsdaSymbols != null)
_lsdaCache.AddLsdaToCache(nodeWithCodeInfo, newLsdaSymbols);
}
}
private protected override void EmitDebugFunctionInfo(
uint methodTypeIndex,
Utf8String methodName,
SymbolDefinition methodSymbol,
INodeWithDebugInfo debugNode,
bool hasSequencePoints)
{
DebugEHClauseInfo[] clauses = null;
if (debugNode is INodeWithCodeInfo nodeWithCodeInfo)
{
clauses = nodeWithCodeInfo.DebugEHClauseInfos;
}
if (_sections[methodSymbol.SectionIndex] is UnixSectionDefinition section)
{
_dwarfBuilder.EmitSubprogramInfo(
methodName,
section.SymbolName,
methodSymbol.Value,
methodSymbol.Size,
methodTypeIndex,
debugNode.GetDebugVars().Select(debugVar => (debugVar, GetVarTypeIndex(debugNode.IsStateMachineMoveNextMethod, debugVar))),
clauses ?? []);
if (hasSequencePoints)
{
_dwarfBuilder.EmitLineInfo(
methodSymbol.SectionIndex,
section.SymbolName,
methodSymbol.Value,
debugNode.GetNativeSequencePoints());
}
}
}
private protected override void EmitDebugSections(IDictionary<Utf8String, SymbolDefinition> definedSymbols)
{
foreach (UnixSectionDefinition section in _sections)
{
if (section is not null)
{
_dwarfBuilder.EmitSectionInfo(section.SymbolName, (ulong)section.SectionStream.Length);
}
}
SectionWriter infoSectionWriter = GetOrCreateSection(DebugInfoSection);
SectionWriter stringSectionWriter = GetOrCreateSection(DebugStringSection);
SectionWriter abbrevSectionWriter = GetOrCreateSection(DebugAbbrevSection);
SectionWriter locSectionWriter = GetOrCreateSection(DebugLocSection);
SectionWriter rangeSectionWriter = GetOrCreateSection(DebugRangesSection);
SectionWriter lineSectionWriter = GetOrCreateSection(DebugLineSection);
SectionWriter arangeSectionWriter = GetOrCreateSection(DebugARangesSection);
_dwarfBuilder.Write(
infoSectionWriter,
stringSectionWriter,
abbrevSectionWriter,
locSectionWriter,
rangeSectionWriter,
lineSectionWriter,
arangeSectionWriter,
symbolName =>
{
if (definedSymbols.TryGetValue(ExternCName(symbolName), out SymbolDefinition symbolDef) &&
_sections[symbolDef.SectionIndex] is UnixSectionDefinition section)
{
return (section.SymbolName, symbolDef.Value);
}
return (default, 0);
});
}
private protected override ITypesDebugInfoWriter CreateDebugInfoBuilder()
{
return _dwarfBuilder = new DwarfBuilder(
_nodeFactory.NameMangler,
_nodeFactory.Target,
_options.HasFlag(ObjectWritingOptions.UseDwarf5));
}
}
}
|