| File: Compiler\ObjectWriter\CoffObjectWriter.Aot.cs | Web Access |
| Project: 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 System.IO; using System.Linq; using System.Numerics; using System.Reflection.PortableExecutable; using System.Runtime.InteropServices; using System.Text; using ILCompiler.DependencyAnalysis; using ILCompiler.DependencyAnalysisFramework; using Internal.Text; using Internal.TypeSystem; using Internal.TypeSystem.TypesDebugInfo; using static ILCompiler.DependencyAnalysis.RelocType; using static ILCompiler.ObjectWriter.CoffObjectWriter.CoffRelocationType; namespace ILCompiler.ObjectWriter { /// <summary> /// COFF object file format writer for Windows targets. /// </summary> /// <remarks> /// The PE/COFF object format is described in the official specifciation at /// https://learn.microsoft.com/windows/win32/debug/pe-format. However, /// numerous extensions are missing in the specification. The most notable /// ones are listed below. /// /// Object files with more than 65279 sections use an extended big object /// file format that is recognized by the Microsoft linker. Many of the /// internal file structures are different. The code below denotes it by /// "BigObj" in parameters and variables. /// /// Section names longer than 8 bytes need to be written indirectly in the /// string table. The PE/COFF specification describes the /NNNNNNN syntax /// for referencing them. However, if the string table gets big enough the /// syntax no longer works. There's an undocumented //BBBBBB syntax where /// base64 offset is used instead. /// /// CodeView debugging format uses 16-bit section index relocations. Once /// the number of sections exceeds 2^16 the same file format is still used. /// The linker treats the CodeView relocations symbolically. /// </remarks> internal partial class CoffObjectWriter : ObjectWriter { // Debugging private SectionWriter _debugTypesSectionWriter; private SectionWriter _debugSymbolSectionWriter; private CodeViewFileTableBuilder _debugFileTableBuilder; private CodeViewSymbolsBuilder _debugSymbolsBuilder; private CodeViewTypesBuilder _debugTypesBuilder; // Exception handling private static readonly ObjectNodeSection XDataSection = new ObjectNodeSection("xdata", SectionType.ReadOnly); private static readonly ObjectNodeSection PDataSection = new ObjectNodeSection("pdata", SectionType.UnwindData); private SectionWriter _pdataSectionWriter; private protected override void CreateEhSections() { // Create .pdata _pdataSectionWriter = GetOrCreateSection(PDataSection); } private protected override void EmitUnwindInfo( SectionWriter sectionWriter, INodeWithCodeInfo nodeWithCodeInfo, Utf8String currentSymbolName) { if (nodeWithCodeInfo.FrameInfos is FrameInfo[] frameInfos && nodeWithCodeInfo is ISymbolDefinitionNode) { SectionWriter xdataSectionWriter; SectionWriter pdataSectionWriter; bool shareSymbol = ShouldShareSymbol((ObjectNode)nodeWithCodeInfo); 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; _utf8StringBuilder.Clear() .Append("_unwind"u8) .Append(FormatUtf8Int(i_str, i)) .Append(currentSymbolName); Utf8String unwindSymbolName = _utf8StringBuilder.ToUtf8String(); if (shareSymbol) { // Produce an associative COMDAT symbol. xdataSectionWriter = GetOrCreateSection(XDataSection, currentSymbolName, unwindSymbolName); pdataSectionWriter = GetOrCreateSection(PDataSection, currentSymbolName, default); } else { // Produce a COMDAT section for each unwind symbol and let linker // do the deduplication across the ones with identical content. xdataSectionWriter = GetOrCreateSection(XDataSection, unwindSymbolName, unwindSymbolName); pdataSectionWriter = _pdataSectionWriter; } // Need to emit the UNWIND_INFO at 4-byte alignment to ensure that the // pointer has the lower two bits in .pdata section set to zero. On ARM64 // non-zero bits would mean a compact encoding. xdataSectionWriter.EmitAlignment(4); xdataSectionWriter.EmitSymbolDefinition(unwindSymbolName); // Emit UNWIND_INFO xdataSectionWriter.Write(blob); FrameInfoFlags flags = frameInfo.Flags; if (i != 0) { xdataSectionWriter.WriteByte((byte)flags); } 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; xdataSectionWriter.WriteByte((byte)flags); if (associatedDataNode is not null) { xdataSectionWriter.EmitSymbolReference( IMAGE_REL_BASED_ADDR32NB, GetMangledName(associatedDataNode)); } if (ehInfo is not null) { xdataSectionWriter.EmitSymbolReference( IMAGE_REL_BASED_ADDR32NB, GetMangledName(ehInfo)); } if (nodeWithCodeInfo.GCInfo is not null) { xdataSectionWriter.Write(nodeWithCodeInfo.GCInfo); } } // Emit RUNTIME_FUNCTION pdataSectionWriter.EmitAlignment(4); pdataSectionWriter.EmitSymbolReference(IMAGE_REL_BASED_ADDR32NB, currentSymbolName, start); // Only x86/x64 has the End symbol if (_machine is Machine.I386 or Machine.Amd64) { pdataSectionWriter.EmitSymbolReference(IMAGE_REL_BASED_ADDR32NB, currentSymbolName, end); } // Unwind info pointer pdataSectionWriter.EmitSymbolReference(IMAGE_REL_BASED_ADDR32NB, unwindSymbolName, 0); } } } private protected override ITypesDebugInfoWriter CreateDebugInfoBuilder() { _debugFileTableBuilder = new CodeViewFileTableBuilder(); _debugSymbolSectionWriter = GetOrCreateSection(DebugSymbolSection); _debugSymbolSectionWriter.EmitAlignment(4); _debugSymbolsBuilder = new CodeViewSymbolsBuilder( _nodeFactory.Target.Architecture, _debugSymbolSectionWriter); _debugTypesSectionWriter = GetOrCreateSection(DebugTypesSection); _debugTypesSectionWriter.EmitAlignment(4); _debugTypesBuilder = new CodeViewTypesBuilder( _nodeFactory.NameMangler, _nodeFactory.Target.PointerSize, _debugTypesSectionWriter); return _debugTypesBuilder; } private protected override void EmitDebugFunctionInfo( uint methodTypeIndex, Utf8String methodName, SymbolDefinition methodSymbol, INodeWithDebugInfo debugNode, bool hasSequencePoints) { DebugEHClauseInfo[] clauses = null; CodeViewSymbolsBuilder debugSymbolsBuilder; if (debugNode is INodeWithCodeInfo nodeWithCodeInfo) { clauses = nodeWithCodeInfo.DebugEHClauseInfos; } if (ShouldShareSymbol((ObjectNode)debugNode)) { // If the method is emitted in COMDAT section then we need to create an // associated COMDAT section for the debugging symbols. var sectionWriter = GetOrCreateSection(DebugSymbolSection, methodName, default); debugSymbolsBuilder = new CodeViewSymbolsBuilder(_nodeFactory.Target.Architecture, sectionWriter); } else { debugSymbolsBuilder = _debugSymbolsBuilder; } debugSymbolsBuilder.EmitSubprogramInfo( methodName, methodSymbol.Size, methodTypeIndex, debugNode.GetDebugVars().Select(debugVar => (debugVar, GetVarTypeIndex(debugNode.IsStateMachineMoveNextMethod, debugVar))), clauses ?? Array.Empty<DebugEHClauseInfo>()); if (hasSequencePoints) { debugSymbolsBuilder.EmitLineInfo( _debugFileTableBuilder, methodName, methodSymbol.Size, debugNode.GetNativeSequencePoints()); } } private protected override void EmitDebugThunkInfo( Utf8String methodName, SymbolDefinition methodSymbol, INodeWithDebugInfo debugNode) { if (!debugNode.GetNativeSequencePoints().Any()) return; CodeViewSymbolsBuilder debugSymbolsBuilder; if (ShouldShareSymbol((ObjectNode)debugNode)) { // If the method is emitted in COMDAT section then we need to create an // associated COMDAT section for the debugging symbols. var sectionWriter = GetOrCreateSection(DebugSymbolSection, methodName, default); debugSymbolsBuilder = new CodeViewSymbolsBuilder(_nodeFactory.Target.Architecture, sectionWriter); } else { debugSymbolsBuilder = _debugSymbolsBuilder; } debugSymbolsBuilder.EmitLineInfo( _debugFileTableBuilder, methodName, methodSymbol.Size, debugNode.GetNativeSequencePoints()); } private protected override void EmitDebugSections(IDictionary<Utf8String, SymbolDefinition> definedSymbols) { _debugSymbolsBuilder.WriteUserDefinedTypes(_debugTypesBuilder.UserDefinedTypes); _debugFileTableBuilder.Write(_debugSymbolSectionWriter); } } }