From abffa8fd7c9a9eaaaba70ca8052d605b85918bd4 Mon Sep 17 00:00:00 2001 From: Filip Navara Date: Mon, 8 Jan 2024 12:59:44 +0100 Subject: [PATCH] ObjWriter in C# (#95876) This is reimplementation of NativeAOT ObjWriter in pure C# instead of depending on LLVM. It implements Mach-O, ELF, COFF object file emitter with DWARF and CodeView debugging information. Only x64 and arm64 targets are implemented to cover officially supported platforms (win-x86 code is present and lightly tested; linux-x86 code is present and incomplete, only serves as a test bed for emitting 32-bit ELF files if we ever need that). Original object writer code is still present and can be used by setting the `DOTNET_USE_LLVM_OBJWRITER=1` environment variable. **Thanks to @am11 for helping with testing and debugging this, @xoofx for making LibObjectFile which helped kickstart this project, @PaulusParssinen for tips about branchless U/SLEB128 size calculation, and all the people on the .NET team who helped push this forward!** Fixes #77178 --- .../DependencyAnalysis/ObjectNodeSection.cs | 1 + .../Compiler/DependencyAnalysis/Relocation.cs | 7 +- .../CodeView/CodeViewFileTableBuilder.cs | 70 + .../ObjectWriter/CodeView/CodeViewNative.cs | 2364 +++++++++++++++++ .../CodeView/CodeViewSymbolsBuilder.cs | 434 +++ .../CodeView/CodeViewTypesBuilder.cs | 533 ++++ .../Compiler/ObjectWriter/CoffObjectWriter.cs | 1097 ++++++++ .../ObjectWriter/Dwarf/DwarfAbbrev.cs | 301 +++ .../ObjectWriter/Dwarf/DwarfBuilder.cs | 488 ++++ .../Compiler/ObjectWriter/Dwarf/DwarfCie.cs | 75 + .../ObjectWriter/Dwarf/DwarfEhFrame.cs | 197 ++ .../Dwarf/DwarfExpressionBuilder.cs | 169 ++ .../Compiler/ObjectWriter/Dwarf/DwarfFde.cs | 124 + .../ObjectWriter/Dwarf/DwarfFileName.cs | 7 + .../ObjectWriter/Dwarf/DwarfHelper.cs | 83 + .../Compiler/ObjectWriter/Dwarf/DwarfInfo.cs | 585 ++++ .../ObjectWriter/Dwarf/DwarfInfoWriter.cs | 220 ++ .../Dwarf/DwarfLineProgramTableWriter.cs | 125 + .../Dwarf/DwarfLineSequenceWriter.cs | 124 + .../ObjectWriter/Dwarf/DwarfNative.cs | 548 ++++ .../Compiler/ObjectWriter/ElfNative.cs | 437 +++ .../Compiler/ObjectWriter/ElfObjectWriter.cs | 842 ++++++ .../LegacyObjectWriter.cs} | 29 +- .../Compiler/ObjectWriter/MachNative.cs | 123 + .../Compiler/ObjectWriter/MachObjectWriter.cs | 1081 ++++++++ .../Compiler/ObjectWriter/ObjectWriter.cs | 558 ++++ .../ObjectWriter/ObjectWritingOptions.cs | 15 + .../Compiler/ObjectWriter/SectionData.cs | 180 ++ .../Compiler/ObjectWriter/SectionWriter.cs | 132 + .../ObjectWriter/StringTableBuilder.cs | 185 ++ .../Compiler/ObjectWriter/UnixObjectWriter.cs | 267 ++ .../ILCompiler.Compiler.csproj | 30 +- .../DependencyAnalysis/MethodCodeNode.cs | 21 +- .../Compiler/RyuJitCompilation.cs | 4 +- 34 files changed, 11429 insertions(+), 27 deletions(-) create mode 100644 src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/CodeView/CodeViewFileTableBuilder.cs create mode 100644 src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/CodeView/CodeViewNative.cs create mode 100644 src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/CodeView/CodeViewSymbolsBuilder.cs create mode 100644 src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/CodeView/CodeViewTypesBuilder.cs create mode 100644 src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/CoffObjectWriter.cs create mode 100644 src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/Dwarf/DwarfAbbrev.cs create mode 100644 src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/Dwarf/DwarfBuilder.cs create mode 100644 src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/Dwarf/DwarfCie.cs create mode 100644 src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/Dwarf/DwarfEhFrame.cs create mode 100644 src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/Dwarf/DwarfExpressionBuilder.cs create mode 100644 src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/Dwarf/DwarfFde.cs create mode 100644 src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/Dwarf/DwarfFileName.cs create mode 100644 src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/Dwarf/DwarfHelper.cs create mode 100644 src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/Dwarf/DwarfInfo.cs create mode 100644 src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/Dwarf/DwarfInfoWriter.cs create mode 100644 src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/Dwarf/DwarfLineProgramTableWriter.cs create mode 100644 src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/Dwarf/DwarfLineSequenceWriter.cs create mode 100644 src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/Dwarf/DwarfNative.cs create mode 100644 src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/ElfNative.cs create mode 100644 src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/ElfObjectWriter.cs rename src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/{DependencyAnalysis/ObjectWriter.cs => ObjectWriter/LegacyObjectWriter.cs} (98%) create mode 100644 src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/MachNative.cs create mode 100644 src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/MachObjectWriter.cs create mode 100644 src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/ObjectWriter.cs create mode 100644 src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/ObjectWritingOptions.cs create mode 100644 src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/SectionData.cs create mode 100644 src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/SectionWriter.cs create mode 100644 src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/StringTableBuilder.cs create mode 100644 src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/UnixObjectWriter.cs diff --git a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/ObjectNodeSection.cs b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/ObjectNodeSection.cs index 4545833ab449c..a621e33e59101 100644 --- a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/ObjectNodeSection.cs +++ b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/ObjectNodeSection.cs @@ -9,6 +9,7 @@ public enum SectionType Writeable, Executable, Uninitialized, + Debug, } /// diff --git a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Relocation.cs b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Relocation.cs index adf1474a709d2..bbe1ce26784b8 100644 --- a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Relocation.cs +++ b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Relocation.cs @@ -24,6 +24,7 @@ public enum RelocType // This is a special NGEN-specific relocation type // for relative pointer (used to make NGen relocation // section smaller) + IMAGE_REL_SECTION = 0x79, // 16 bit section index containing target IMAGE_REL_BASED_ARM64_PAGEBASE_REL21 = 0x81, // ADRP IMAGE_REL_BASED_ARM64_PAGEOFFSET_12A = 0x82, // ADD/ADDS (immediate) with zero shift, for page offset @@ -465,9 +466,13 @@ public static unsafe void WriteValue(RelocType relocType, void* location, long v switch (relocType) { case RelocType.IMAGE_REL_BASED_ABSOLUTE: + case RelocType.IMAGE_REL_BASED_ADDR32NB: case RelocType.IMAGE_REL_BASED_HIGHLOW: case RelocType.IMAGE_REL_BASED_REL32: - case RelocType.IMAGE_REL_BASED_ADDR32NB: + case RelocType.IMAGE_REL_BASED_RELPTR32: + case RelocType.IMAGE_REL_SECREL: + case RelocType.IMAGE_REL_TLSGD: + case RelocType.IMAGE_REL_TPOFF: case RelocType.IMAGE_REL_SYMBOL_SIZE: case RelocType.IMAGE_REL_FILE_ABSOLUTE: *(int*)location = (int)value; diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/CodeView/CodeViewFileTableBuilder.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/CodeView/CodeViewFileTableBuilder.cs new file mode 100644 index 0000000000000..817ab7a40f71f --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/CodeView/CodeViewFileTableBuilder.cs @@ -0,0 +1,70 @@ +// 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.Text; + +using static ILCompiler.ObjectWriter.CodeViewNative; + +namespace ILCompiler.ObjectWriter +{ + internal sealed class CodeViewFileTableBuilder + { + private readonly MemoryStream _stringTableWriter = new(); + private readonly MemoryStream _fileTableWriter = new(); + private readonly Dictionary _fileNameToIndex = new(); + + public CodeViewFileTableBuilder() + { + // Insert empty entry at the beginning of string table + _stringTableWriter.Write(stackalloc byte[2] { 0, 0 }); + } + + public uint GetFileIndex(string fileName) + { + if (fileName == "") + { + // Match the placeholder value from LLVM. We need to use a non-empty + // string to ensure that the null terminator of the UTF-8 representation + // is not treated as the terminator of the whole file name table. + fileName = ""; + } + + if (_fileNameToIndex.TryGetValue(fileName, out uint fileIndex)) + { + return fileIndex; + } + else + { + uint stringTableIndex = (uint)_stringTableWriter.Position; + _stringTableWriter.Write(Encoding.UTF8.GetBytes(fileName)); + _stringTableWriter.WriteByte(0); + + uint fileTableIndex = (uint)_fileTableWriter.Position; + Span fileTableEntry = stackalloc byte[sizeof(uint) + sizeof(uint)]; + BinaryPrimitives.WriteUInt32LittleEndian(fileTableEntry, stringTableIndex); + BinaryPrimitives.WriteUInt32LittleEndian(fileTableEntry.Slice(4), 0); + _fileTableWriter.Write(fileTableEntry); + + _fileNameToIndex.Add(fileName, fileTableIndex); + + return fileTableIndex; + } + } + + public void Write(SectionWriter sectionWriter) + { + sectionWriter.WriteLittleEndian((uint)DebugSymbolsSubsectionType.FileChecksums); + sectionWriter.WriteLittleEndian((uint)_fileTableWriter.Length); + sectionWriter.EmitData(_fileTableWriter.GetBuffer().AsMemory(0, (int)_fileTableWriter.Length)); + sectionWriter.WriteLittleEndian((uint)DebugSymbolsSubsectionType.StringTable); + sectionWriter.WriteLittleEndian((uint)_stringTableWriter.Length); + sectionWriter.EmitData(_stringTableWriter.GetBuffer().AsMemory(0, (int)_stringTableWriter.Length)); + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/CodeView/CodeViewNative.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/CodeView/CodeViewNative.cs new file mode 100644 index 0000000000000..431ef42dc27ad --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/CodeView/CodeViewNative.cs @@ -0,0 +1,2364 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; + +namespace ILCompiler.ObjectWriter +{ + /// + /// Native constants and enumerations for CodeView binary format. + /// + internal static class CodeViewNative + { + // Matches TYPE_ENUM_e in cvinfo.h + public enum CodeViewType : ushort + { + // Special Types + T_NOTYPE = 0x0000, // uncharacterized type (no type) + T_ABS = 0x0001, // absolute symbol + T_SEGMENT = 0x0002, // segment type + T_VOID = 0x0003, // void + T_HRESULT = 0x0008, // OLE/COM HRESULT + T_32PHRESULT = 0x0408, // OLE/COM HRESULT __ptr32 * + T_64PHRESULT = 0x0608, // OLE/COM HRESULT __ptr64 * + + T_PVOID = 0x0103, // near pointer to void + T_PFVOID = 0x0203, // far pointer to void + T_PHVOID = 0x0303, // huge pointer to void + T_32PVOID = 0x0403, // 32 bit pointer to void + T_32PFVOID = 0x0503, // 16:32 pointer to void + T_64PVOID = 0x0603, // 64 bit pointer to void + T_CURRENCY = 0x0004, // BASIC 8 byte currency value + T_NBASICSTR = 0x0005, // Near BASIC string + T_FBASICSTR = 0x0006, // Far BASIC string + T_NOTTRANS = 0x0007, // type not translated by cvpack + T_BIT = 0x0060, // bit + T_PASCHAR = 0x0061, // Pascal CHAR + T_BOOL32FF = 0x0062, // 32-bit BOOL where true is 0xFFFFFFFF + + // Character types + T_CHAR = 0x0010, // 8 bit signed + T_PCHAR = 0x0110, // 16 bit pointer to 8 bit signed + T_PFCHAR = 0x0210, // 16:16 far pointer to 8 bit signed + T_PHCHAR = 0x0310, // 16:16 huge pointer to 8 bit signed + T_32PCHAR = 0x0410, // 32 bit pointer to 8 bit signed + T_32PFCHAR = 0x0510, // 16:32 pointer to 8 bit signed + T_64PCHAR = 0x0610, // 64 bit pointer to 8 bit signed + + T_UCHAR = 0x0020, // 8 bit unsigned + T_PUCHAR = 0x0120, // 16 bit pointer to 8 bit unsigned + T_PFUCHAR = 0x0220, // 16:16 far pointer to 8 bit unsigned + T_PHUCHAR = 0x0320, // 16:16 huge pointer to 8 bit unsigned + T_32PUCHAR = 0x0420, // 32 bit pointer to 8 bit unsigned + T_32PFUCHAR = 0x0520, // 16:32 pointer to 8 bit unsigned + T_64PUCHAR = 0x0620, // 64 bit pointer to 8 bit unsigned + + // Really a character types + T_RCHAR = 0x0070, // really a char + T_PRCHAR = 0x0170, // 16 bit pointer to a real char + T_PFRCHAR = 0x0270, // 16:16 far pointer to a real char + T_PHRCHAR = 0x0370, // 16:16 huge pointer to a real char + T_32PRCHAR = 0x0470, // 32 bit pointer to a real char + T_32PFRCHAR = 0x0570, // 16:32 pointer to a real char + T_64PRCHAR = 0x0670, // 64 bit pointer to a real char + + // Really a wide character types + T_WCHAR = 0x0071, // wide char + T_PWCHAR = 0x0171, // 16 bit pointer to a wide char + T_PFWCHAR = 0x0271, // 16:16 far pointer to a wide char + T_PHWCHAR = 0x0371, // 16:16 huge pointer to a wide char + T_32PWCHAR = 0x0471, // 32 bit pointer to a wide char + T_32PFWCHAR = 0x0571, // 16:32 pointer to a wide char + T_64PWCHAR = 0x0671, // 64 bit pointer to a wide char + + // Really a 16-bit unicode char + T_CHAR16 = 0x007A, // 16-bit unicode char + T_PCHAR16 = 0x017A, // 16 bit pointer to a 16-bit unicode char + T_PFCHAR16 = 0x027A, // 16:16 far pointer to a 16-bit unicode char + T_PHCHAR16 = 0x037A, // 16:16 huge pointer to a 16-bit unicode char + T_32PCHAR16 = 0x047A, // 32 bit pointer to a 16-bit unicode char + T_32PFCHAR16 = 0x057A, // 16:32 pointer to a 16-bit unicode char + T_64PCHAR16 = 0x067A, // 64 bit pointer to a 16-bit unicode char + + // Really a 32-bit unicode char + T_CHAR32 = 0x007B, // 32-bit unicode char + T_PCHAR32 = 0x017B, // 16 bit pointer to a 32-bit unicode char + T_PFCHAR32 = 0x027B, // 16:16 far pointer to a 32-bit unicode char + T_PHCHAR32 = 0x037B, // 16:16 huge pointer to a 32-bit unicode char + T_32PCHAR32 = 0x047B, // 32 bit pointer to a 32-bit unicode char + T_32PFCHAR32 = 0x057B, // 16:32 pointer to a 32-bit unicode char + T_64PCHAR32 = 0x067B, // 64 bit pointer to a 32-bit unicode char + + // 8-bit int types + T_INT1 = 0x0068, // 8 bit signed int + T_PINT1 = 0x0168, // 16 bit pointer to 8 bit signed int + T_PFINT1 = 0x0268, // 16:16 far pointer to 8 bit signed int + T_PHINT1 = 0x0368, // 16:16 huge pointer to 8 bit signed int + T_32PINT1 = 0x0468, // 32 bit pointer to 8 bit signed int + T_32PFINT1 = 0x0568, // 16:32 pointer to 8 bit signed int + T_64PINT1 = 0x0668, // 64 bit pointer to 8 bit signed int + + T_UINT1 = 0x0069, // 8 bit unsigned int + T_PUINT1 = 0x0169, // 16 bit pointer to 8 bit unsigned int + T_PFUINT1 = 0x0269, // 16:16 far pointer to 8 bit unsigned int + T_PHUINT1 = 0x0369, // 16:16 huge pointer to 8 bit unsigned int + T_32PUINT1 = 0x0469, // 32 bit pointer to 8 bit unsigned int + T_32PFUINT1 = 0x0569, // 16:32 pointer to 8 bit unsigned int + T_64PUINT1 = 0x0669, // 64 bit pointer to 8 bit unsigned int + + // 16-bit short types + T_SHORT = 0x0011, // 16 bit signed + T_PSHORT = 0x0111, // 16 bit pointer to 16 bit signed + T_PFSHORT = 0x0211, // 16:16 far pointer to 16 bit signed + T_PHSHORT = 0x0311, // 16:16 huge pointer to 16 bit signed + T_32PSHORT = 0x0411, // 32 bit pointer to 16 bit signed + T_32PFSHORT = 0x0511, // 16:32 pointer to 16 bit signed + T_64PSHORT = 0x0611, // 64 bit pointer to 16 bit signed + + T_USHORT = 0x0021, // 16 bit unsigned + T_PUSHORT = 0x0121, // 16 bit pointer to 16 bit unsigned + T_PFUSHORT = 0x0221, // 16:16 far pointer to 16 bit unsigned + T_PHUSHORT = 0x0321, // 16:16 huge pointer to 16 bit unsigned + T_32PUSHORT = 0x0421, // 32 bit pointer to 16 bit unsigned + T_32PFUSHORT = 0x0521, // 16:32 pointer to 16 bit unsigned + T_64PUSHORT = 0x0621, // 64 bit pointer to 16 bit unsigned + + // 16-bit int types + T_INT2 = 0x0072, // 16 bit signed int + T_PINT2 = 0x0172, // 16 bit pointer to 16 bit signed int + T_PFINT2 = 0x0272, // 16:16 far pointer to 16 bit signed int + T_PHINT2 = 0x0372, // 16:16 huge pointer to 16 bit signed int + T_32PINT2 = 0x0472, // 32 bit pointer to 16 bit signed int + T_32PFINT2 = 0x0572, // 16:32 pointer to 16 bit signed int + T_64PINT2 = 0x0672, // 64 bit pointer to 16 bit signed int + + T_UINT2 = 0x0073, // 16 bit unsigned int + T_PUINT2 = 0x0173, // 16 bit pointer to 16 bit unsigned int + T_PFUINT2 = 0x0273, // 16:16 far pointer to 16 bit unsigned int + T_PHUINT2 = 0x0373, // 16:16 huge pointer to 16 bit unsigned int + T_32PUINT2 = 0x0473, // 32 bit pointer to 16 bit unsigned int + T_32PFUINT2 = 0x0573, // 16:32 pointer to 16 bit unsigned int + T_64PUINT2 = 0x0673, // 64 bit pointer to 16 bit unsigned int + + // 32-bit long types + T_LONG = 0x0012, // 32 bit signed + T_ULONG = 0x0022, // 32 bit unsigned + T_PLONG = 0x0112, // 16 bit pointer to 32 bit signed + T_PULONG = 0x0122, // 16 bit pointer to 32 bit unsigned + T_PFLONG = 0x0212, // 16:16 far pointer to 32 bit signed + T_PFULONG = 0x0222, // 16:16 far pointer to 32 bit unsigned + T_PHLONG = 0x0312, // 16:16 huge pointer to 32 bit signed + T_PHULONG = 0x0322, // 16:16 huge pointer to 32 bit unsigned + + T_32PLONG = 0x0412, // 32 bit pointer to 32 bit signed + T_32PULONG = 0x0422, // 32 bit pointer to 32 bit unsigned + T_32PFLONG = 0x0512, // 16:32 pointer to 32 bit signed + T_32PFULONG = 0x0522, // 16:32 pointer to 32 bit unsigned + T_64PLONG = 0x0612, // 64 bit pointer to 32 bit signed + T_64PULONG = 0x0622, // 64 bit pointer to 32 bit unsigned + + // 32-bit int types + T_INT4 = 0x0074, // 32 bit signed int + T_PINT4 = 0x0174, // 16 bit pointer to 32 bit signed int + T_PFINT4 = 0x0274, // 16:16 far pointer to 32 bit signed int + T_PHINT4 = 0x0374, // 16:16 huge pointer to 32 bit signed int + T_32PINT4 = 0x0474, // 32 bit pointer to 32 bit signed int + T_32PFINT4 = 0x0574, // 16:32 pointer to 32 bit signed int + T_64PINT4 = 0x0674, // 64 bit pointer to 32 bit signed int + + T_UINT4 = 0x0075, // 32 bit unsigned int + T_PUINT4 = 0x0175, // 16 bit pointer to 32 bit unsigned int + T_PFUINT4 = 0x0275, // 16:16 far pointer to 32 bit unsigned int + T_PHUINT4 = 0x0375, // 16:16 huge pointer to 32 bit unsigned int + T_32PUINT4 = 0x0475, // 32 bit pointer to 32 bit unsigned int + T_32PFUINT4 = 0x0575, // 16:32 pointer to 32 bit unsigned int + T_64PUINT4 = 0x0675, // 64 bit pointer to 32 bit unsigned int + + // 64-bit quad types + T_QUAD = 0x0013, // 64 bit signed + T_PQUAD = 0x0113, // 16 bit pointer to 64 bit signed + T_PFQUAD = 0x0213, // 16:16 far pointer to 64 bit signed + T_PHQUAD = 0x0313, // 16:16 huge pointer to 64 bit signed + T_32PQUAD = 0x0413, // 32 bit pointer to 64 bit signed + T_32PFQUAD = 0x0513, // 16:32 pointer to 64 bit signed + T_64PQUAD = 0x0613, // 64 bit pointer to 64 bit signed + + T_UQUAD = 0x0023, // 64 bit unsigned + T_PUQUAD = 0x0123, // 16 bit pointer to 64 bit unsigned + T_PFUQUAD = 0x0223, // 16:16 far pointer to 64 bit unsigned + T_PHUQUAD = 0x0323, // 16:16 huge pointer to 64 bit unsigned + T_32PUQUAD = 0x0423, // 32 bit pointer to 64 bit unsigned + T_32PFUQUAD = 0x0523, // 16:32 pointer to 64 bit unsigned + T_64PUQUAD = 0x0623, // 64 bit pointer to 64 bit unsigned + + // 64-bit int types + T_INT8 = 0x0076, // 64 bit signed int + T_PINT8 = 0x0176, // 16 bit pointer to 64 bit signed int + T_PFINT8 = 0x0276, // 16:16 far pointer to 64 bit signed int + T_PHINT8 = 0x0376, // 16:16 huge pointer to 64 bit signed int + T_32PINT8 = 0x0476, // 32 bit pointer to 64 bit signed int + T_32PFINT8 = 0x0576, // 16:32 pointer to 64 bit signed int + T_64PINT8 = 0x0676, // 64 bit pointer to 64 bit signed int + + T_UINT8 = 0x0077, // 64 bit unsigned int + T_PUINT8 = 0x0177, // 16 bit pointer to 64 bit unsigned int + T_PFUINT8 = 0x0277, // 16:16 far pointer to 64 bit unsigned int + T_PHUINT8 = 0x0377, // 16:16 huge pointer to 64 bit unsigned int + T_32PUINT8 = 0x0477, // 32 bit pointer to 64 bit unsigned int + T_32PFUINT8 = 0x0577, // 16:32 pointer to 64 bit unsigned int + T_64PUINT8 = 0x0677, // 64 bit pointer to 64 bit unsigned int + + // 128-bit octet types + T_OCT = 0x0014, // 128 bit signed + T_POCT = 0x0114, // 16 bit pointer to 128 bit signed + T_PFOCT = 0x0214, // 16:16 far pointer to 128 bit signed + T_PHOCT = 0x0314, // 16:16 huge pointer to 128 bit signed + T_32POCT = 0x0414, // 32 bit pointer to 128 bit signed + T_32PFOCT = 0x0514, // 16:32 pointer to 128 bit signed + T_64POCT = 0x0614, // 64 bit pointer to 128 bit signed + + T_UOCT = 0x0024, // 128 bit unsigned + T_PUOCT = 0x0124, // 16 bit pointer to 128 bit unsigned + T_PFUOCT = 0x0224, // 16:16 far pointer to 128 bit unsigned + T_PHUOCT = 0x0324, // 16:16 huge pointer to 128 bit unsigned + T_32PUOCT = 0x0424, // 32 bit pointer to 128 bit unsigned + T_32PFUOCT = 0x0524, // 16:32 pointer to 128 bit unsigned + T_64PUOCT = 0x0624, // 64 bit pointer to 128 bit unsigned + + // 128-bit int types + T_INT16 = 0x0078, // 128 bit signed int + T_PINT16 = 0x0178, // 16 bit pointer to 128 bit signed int + T_PFINT16 = 0x0278, // 16:16 far pointer to 128 bit signed int + T_PHINT16 = 0x0378, // 16:16 huge pointer to 128 bit signed int + T_32PINT16 = 0x0478, // 32 bit pointer to 128 bit signed int + T_32PFINT16 = 0x0578, // 16:32 pointer to 128 bit signed int + T_64PINT16 = 0x0678, // 64 bit pointer to 128 bit signed int + + T_UINT16 = 0x0079, // 128 bit unsigned int + T_PUINT16 = 0x0179, // 16 bit pointer to 128 bit unsigned int + T_PFUINT16 = 0x0279, // 16:16 far pointer to 128 bit unsigned int + T_PHUINT16 = 0x0379, // 16:16 huge pointer to 128 bit unsigned int + T_32PUINT16 = 0x0479, // 32 bit pointer to 128 bit unsigned int + T_32PFUINT16 = 0x0579, // 16:32 pointer to 128 bit unsigned int + T_64PUINT16 = 0x0679, // 64 bit pointer to 128 bit unsigned int + + // 16-bit real types + T_REAL16 = 0x0046, // 16 bit real + T_PREAL16 = 0x0146, // 16 bit pointer to 16 bit real + T_PFREAL16 = 0x0246, // 16:16 far pointer to 16 bit real + T_PHREAL16 = 0x0346, // 16:16 huge pointer to 16 bit real + T_32PREAL16 = 0x0446, // 32 bit pointer to 16 bit real + T_32PFREAL16 = 0x0546, // 16:32 pointer to 16 bit real + T_64PREAL16 = 0x0646, // 64 bit pointer to 16 bit real + + // 32-bit real types + T_REAL32 = 0x0040, // 32 bit real + T_PREAL32 = 0x0140, // 16 bit pointer to 32 bit real + T_PFREAL32 = 0x0240, // 16:16 far pointer to 32 bit real + T_PHREAL32 = 0x0340, // 16:16 huge pointer to 32 bit real + T_32PREAL32 = 0x0440, // 32 bit pointer to 32 bit real + T_32PFREAL32 = 0x0540, // 16:32 pointer to 32 bit real + T_64PREAL32 = 0x0640, // 64 bit pointer to 32 bit real + + // 32-bit partial-precision real types + T_REAL32PP = 0x0045, // 32 bit PP real + T_PREAL32PP = 0x0145, // 16 bit pointer to 32 bit PP real + T_PFREAL32PP = 0x0245, // 16:16 far pointer to 32 bit PP real + T_PHREAL32PP = 0x0345, // 16:16 huge pointer to 32 bit PP real + T_32PREAL32PP = 0x0445, // 32 bit pointer to 32 bit PP real + T_32PFREAL32PP = 0x0545, // 16:32 pointer to 32 bit PP real + T_64PREAL32PP = 0x0645, // 64 bit pointer to 32 bit PP real + + // 48-bit real types + T_REAL48 = 0x0044, // 48 bit real + T_PREAL48 = 0x0144, // 16 bit pointer to 48 bit real + T_PFREAL48 = 0x0244, // 16:16 far pointer to 48 bit real + T_PHREAL48 = 0x0344, // 16:16 huge pointer to 48 bit real + T_32PREAL48 = 0x0444, // 32 bit pointer to 48 bit real + T_32PFREAL48 = 0x0544, // 16:32 pointer to 48 bit real + T_64PREAL48 = 0x0644, // 64 bit pointer to 48 bit real + + // 64-bit real types + T_REAL64 = 0x0041, // 64 bit real + T_PREAL64 = 0x0141, // 16 bit pointer to 64 bit real + T_PFREAL64 = 0x0241, // 16:16 far pointer to 64 bit real + T_PHREAL64 = 0x0341, // 16:16 huge pointer to 64 bit real + T_32PREAL64 = 0x0441, // 32 bit pointer to 64 bit real + T_32PFREAL64 = 0x0541, // 16:32 pointer to 64 bit real + T_64PREAL64 = 0x0641, // 64 bit pointer to 64 bit real + + // 80-bit real types + T_REAL80 = 0x0042, // 80 bit real + T_PREAL80 = 0x0142, // 16 bit pointer to 80 bit real + T_PFREAL80 = 0x0242, // 16:16 far pointer to 80 bit real + T_PHREAL80 = 0x0342, // 16:16 huge pointer to 80 bit real + T_32PREAL80 = 0x0442, // 32 bit pointer to 80 bit real + T_32PFREAL80 = 0x0542, // 16:32 pointer to 80 bit real + T_64PREAL80 = 0x0642, // 64 bit pointer to 80 bit real + + // 128-bit real types + T_REAL128 = 0x0043, // 128 bit real + T_PREAL128 = 0x0143, // 16 bit pointer to 128 bit real + T_PFREAL128 = 0x0243, // 16:16 far pointer to 128 bit real + T_PHREAL128 = 0x0343, // 16:16 huge pointer to 128 bit real + T_32PREAL128 = 0x0443, // 32 bit pointer to 128 bit real + T_32PFREAL128 = 0x0543, // 16:32 pointer to 128 bit real + T_64PREAL128 = 0x0643, // 64 bit pointer to 128 bit real + + // 32-bit complex types + T_CPLX32 = 0x0050, // 32 bit complex + T_PCPLX32 = 0x0150, // 16 bit pointer to 32 bit complex + T_PFCPLX32 = 0x0250, // 16:16 far pointer to 32 bit complex + T_PHCPLX32 = 0x0350, // 16:16 huge pointer to 32 bit complex + T_32PCPLX32 = 0x0450, // 32 bit pointer to 32 bit complex + T_32PFCPLX32 = 0x0550, // 16:32 pointer to 32 bit complex + T_64PCPLX32 = 0x0650, // 64 bit pointer to 32 bit complex + + // 64-bit complex types + T_CPLX64 = 0x0051, // 64 bit complex + T_PCPLX64 = 0x0151, // 16 bit pointer to 64 bit complex + T_PFCPLX64 = 0x0251, // 16:16 far pointer to 64 bit complex + T_PHCPLX64 = 0x0351, // 16:16 huge pointer to 64 bit complex + T_32PCPLX64 = 0x0451, // 32 bit pointer to 64 bit complex + T_32PFCPLX64 = 0x0551, // 16:32 pointer to 64 bit complex + T_64PCPLX64 = 0x0651, // 64 bit pointer to 64 bit complex + + // 80-bit complex types + T_CPLX80 = 0x0052, // 80 bit complex + T_PCPLX80 = 0x0152, // 16 bit pointer to 80 bit complex + T_PFCPLX80 = 0x0252, // 16:16 far pointer to 80 bit complex + T_PHCPLX80 = 0x0352, // 16:16 huge pointer to 80 bit complex + T_32PCPLX80 = 0x0452, // 32 bit pointer to 80 bit complex + T_32PFCPLX80 = 0x0552, // 16:32 pointer to 80 bit complex + T_64PCPLX80 = 0x0652, // 64 bit pointer to 80 bit complex + + // 128-bit complex types + T_CPLX128 = 0x0053, // 128 bit complex + T_PCPLX128 = 0x0153, // 16 bit pointer to 128 bit complex + T_PFCPLX128 = 0x0253, // 16:16 far pointer to 128 bit complex + T_PHCPLX128 = 0x0353, // 16:16 huge pointer to 128 bit real + T_32PCPLX128 = 0x0453, // 32 bit pointer to 128 bit complex + T_32PFCPLX128 = 0x0553, // 16:32 pointer to 128 bit complex + T_64PCPLX128 = 0x0653, // 64 bit pointer to 128 bit complex + + // Boolean types + T_BOOL08 = 0x0030, // 8 bit boolean + T_PBOOL08 = 0x0130, // 16 bit pointer to 8 bit boolean + T_PFBOOL08 = 0x0230, // 16:16 far pointer to 8 bit boolean + T_PHBOOL08 = 0x0330, // 16:16 huge pointer to 8 bit boolean + T_32PBOOL08 = 0x0430, // 32 bit pointer to 8 bit boolean + T_32PFBOOL08 = 0x0530, // 16:32 pointer to 8 bit boolean + T_64PBOOL08 = 0x0630, // 64 bit pointer to 8 bit boolean + + T_BOOL16 = 0x0031, // 16 bit boolean + T_PBOOL16 = 0x0131, // 16 bit pointer to 16 bit boolean + T_PFBOOL16 = 0x0231, // 16:16 far pointer to 16 bit boolean + T_PHBOOL16 = 0x0331, // 16:16 huge pointer to 16 bit boolean + T_32PBOOL16 = 0x0431, // 32 bit pointer to 18 bit boolean + T_32PFBOOL16 = 0x0531, // 16:32 pointer to 16 bit boolean + T_64PBOOL16 = 0x0631, // 64 bit pointer to 18 bit boolean + + T_BOOL32 = 0x0032, // 32 bit boolean + T_PBOOL32 = 0x0132, // 16 bit pointer to 32 bit boolean + T_PFBOOL32 = 0x0232, // 16:16 far pointer to 32 bit boolean + T_PHBOOL32 = 0x0332, // 16:16 huge pointer to 32 bit boolean + T_32PBOOL32 = 0x0432, // 32 bit pointer to 32 bit boolean + T_32PFBOOL32 = 0x0532, // 16:32 pointer to 32 bit boolean + T_64PBOOL32 = 0x0632, // 64 bit pointer to 32 bit boolean + + T_BOOL64 = 0x0033, // 64 bit boolean + T_PBOOL64 = 0x0133, // 16 bit pointer to 64 bit boolean + T_PFBOOL64 = 0x0233, // 16:16 far pointer to 64 bit boolean + T_PHBOOL64 = 0x0333, // 16:16 huge pointer to 64 bit boolean + T_32PBOOL64 = 0x0433, // 32 bit pointer to 64 bit boolean + T_32PFBOOL64 = 0x0533, // 16:32 pointer to 64 bit boolean + T_64PBOOL64 = 0x0633, // 64 bit pointer to 64 bit boolean + + T_NCVPTR = 0x01F0, // CV Internal type for created near pointers + T_FCVPTR = 0x02F0, // CV Internal type for created far pointers + T_HCVPTR = 0x03F0, // CV Internal type for created huge pointers + T_32NCVPTR = 0x04F0, // CV Internal type for created near 32-bit pointers + T_32FCVPTR = 0x05F0, // CV Internal type for created far 32-bit pointers + T_64NCVPTR = 0x06F0, // CV Internal type for created near 64-bit pointers + } + + /// Type enum for pointer records + /// + /// CV_PTR_* matches CV_ptrtype_e in cvinfo.h. + /// CV_PTR_MODE_* matches CV_ptrmode_e in cvinfo.h shifted left by 5. + /// CV_PTR_IS_* matches bit fields in lfPointer in cvinfo.h. + /// + public enum CodeViewPointer : uint + { + CV_PTR_NEAR = 0x00, // 16 bit pointer + CV_PTR_FAR = 0x01, // 16:16 far pointer + CV_PTR_HUGE = 0x02, // 16:16 huge pointer + CV_PTR_BASE_SEG = 0x03, // based on segment + CV_PTR_BASE_VAL = 0x04, // based on value of base + CV_PTR_BASE_SEGVAL = 0x05, // based on segment value of base + CV_PTR_BASE_ADDR = 0x06, // based on address of base + CV_PTR_BASE_SEGADDR = 0x07, // based on segment address of base + CV_PTR_BASE_TYPE = 0x08, // based on type + CV_PTR_BASE_SELF = 0x09, // based on self + CV_PTR_NEAR32 = 0x0A, // 32 bit pointer + CV_PTR_FAR32 = 0x0B, // 16:32 pointer + CV_PTR_64 = 0x0C, // 64 bit pointer + CV_PTR_UNUSEDPTR = 0x0D, // first unused pointer type + + CV_PTR_MODE_PTR = 0x00 << 5, // "normal" pointer + CV_PTR_MODE_REF = 0x01 << 5, // "old" reference + CV_PTR_MODE_LVREF = 0x01 << 5, // l-value reference + CV_PTR_MODE_PMEM = 0x02 << 5, // pointer to data member + CV_PTR_MODE_PMFUNC = 0x03 << 5, // pointer to member function + CV_PTR_MODE_RVREF = 0x04 << 5, // r-value reference + CV_PTR_MODE_RESERVED = 0x05 << 5, // first unused pointer mode + + CV_PTR_IS_FLAT32 = 1 << 8, + CV_PTR_IS_VOLATILE = 1 << 9, + CV_PTR_IS_CONST = 1 << 10, + CV_PTR_IS_UNALIGNED = 1 << 11, + CV_PTR_IS_RESTRICTED = 1 << 12, + } + + // Matches bit fields in CV_prop_t in cvinfo.h + public enum CodeViewPropertyFlags : ushort + { + CV_PROP_NONE = 0, + CV_PROP_PACKED = 1, + CV_PROP_HAS_CONSTURUCTOR_OR_DESTRUCTOR = 2, + CV_PROP_HAS_OVERLOADED_OPERATOR = 4, + CV_PROP_NESTED = 8, + CV_PROP_CONTAINS_NESTED_CLASS = 16, + CV_PROP_HAS_OVERLOADED_ASSIGNMENT_OPERATOR = 32, + CV_PROP_HAS_CONVERSION_OPERATOR = 64, + CV_PROP_FORWARD_REFERENCE = 128, + CV_PROP_SCOPED = 256, + CV_PROP_HAS_UNIQUE_NAME = 512, + CV_PROP_SEALED = 1024, + CV_PROP_INTRINSIC = 2048, + } + + // Matches DEBUG_S_SUBSECTION_TYPE in cvinfo.h + public enum DebugSymbolsSubsectionType : uint + { + Symbols = 0xF1, + Lines, + StringTable, + FileChecksums, + FrameData, + InlineeLines, + CrossScopeImports, + CrossScopeExports, + + ILLines, + FunctionMDTokenMap, + TypeMDTokenMap, + MergedAssemblyInput, + + CoffSymbolRva, + } + + // Matches LEAF_ENUM_e in cvinfo.h + public enum LeafRecordType + { + // Leaf indices starting records referenced from symbol records + LF_MODIFIER_16t = 0x0001, + LF_POINTER_16t = 0x0002, + LF_ARRAY_16t = 0x0003, + LF_CLASS_16t = 0x0004, + LF_STRUCTURE_16t = 0x0005, + LF_UNION_16t = 0x0006, + LF_ENUM_16t = 0x0007, + LF_PROCEDURE_16t = 0x0008, + LF_MFUNCTION_16t = 0x0009, + LF_VTSHAPE = 0x000A, + LF_COBOL0_16t = 0x000B, + LF_COBOL1 = 0x000C, + LF_BARRAY_16t = 0x000D, + LF_LABEL = 0x000E, + LF_NULL = 0x000F, + LF_NOTTRAN = 0x0010, + LF_DIMARRAY_16t = 0x0011, + LF_VFTPATH_16t = 0x0012, + LF_PRECOMP_16t = 0x0013, // not referenced from symbol + LF_ENDPRECOMP = 0x0014, // not referenced from symbol + LF_OEM_16t = 0x0015, // oem definable type string + LF_TYPESERVER_ST = 0x0016, // not referenced from symbol + + // leaf indices starting records referenced only from type records + LF_SKIP_16t = 0x0200, + LF_ARGLIST_16t = 0x0201, + LF_DEFARG_16t = 0x0202, + LF_LIST = 0x0203, + LF_FIELDLIST_16t = 0x0204, + LF_DERIVED_16t = 0x0205, + LF_BITFIELD_16t = 0x0206, + LF_METHODLIST_16t = 0x0207, + LF_DIMCONU_16t = 0x0208, + LF_DIMCONLU_16t = 0x0209, + LF_DIMVARU_16t = 0x020A, + LF_DIMVARLU_16t = 0x020B, + LF_REFSYM = 0x020C, + + LF_BCLASS_16t = 0x0400, + LF_VBCLASS_16t = 0x0401, + LF_IVBCLASS_16t = 0x0402, + LF_ENUMERATE_ST = 0x0403, + LF_FRIENDFCN_16t = 0x0404, + LF_INDEX_16t = 0x0405, + LF_MEMBER_16t = 0x0406, + LF_STMEMBER_16t = 0x0407, + LF_METHOD_16t = 0x0408, + LF_NESTTYPE_16t = 0x0409, + LF_VFUNCTAB_16t = 0x040A, + LF_FRIENDCLS_16t = 0x040B, + LF_ONEMETHOD_16t = 0x040C, + LF_VFUNCOFF_16t = 0x040D, + + // 32-bit type index versions of leaves, all have the 0x1000 bit set + LF_TI16_MAX = 0x1000, + + LF_MODIFIER = 0x1001, + LF_POINTER = 0x1002, + LF_ARRAY_ST = 0x1003, + LF_CLASS_ST = 0x1004, + LF_STRUCTURE_ST = 0x1005, + LF_UNION_ST = 0x1006, + LF_ENUM_ST = 0x1007, + LF_PROCEDURE = 0x1008, + LF_MFUNCTION = 0x1009, + LF_COBOL0 = 0x100A, + LF_BARRAY = 0x100B, + LF_DIMARRAY_ST = 0x100C, + LF_VFTPATH = 0x100D, + LF_PRECOMP_ST = 0x100E, // not referenced from symbol + LF_OEM = 0x100F, // oem definable type string + LF_ALIAS_ST = 0x1010, // alias (typedef) type + LF_OEM2 = 0x1011, // oem definable type string + + // leaf indices starting records but referenced only from type records + LF_SKIP = 0x1200, + LF_ARGLIST = 0x1201, + LF_DEFARG_ST = 0x1202, + LF_FIELDLIST = 0x1203, + LF_DERIVED = 0x1204, + LF_BITFIELD = 0x1205, + LF_METHODLIST = 0x1206, + LF_DIMCONU = 0x1207, + LF_DIMCONLU = 0x1208, + LF_DIMVARU = 0x1209, + LF_DIMVARLU = 0x120A, + + LF_BCLASS = 0x1400, + LF_VBCLASS = 0x1401, + LF_IVBCLASS = 0x1402, + LF_FRIENDFCN_ST = 0x1403, + LF_INDEX = 0x1404, + LF_MEMBER_ST = 0x1405, + LF_STMEMBER_ST = 0x1406, + LF_METHOD_ST = 0x1407, + LF_NESTTYPE_ST = 0x1408, + LF_VFUNCTAB = 0x1409, + LF_FRIENDCLS = 0x140A, + LF_ONEMETHOD_ST = 0x140B, + LF_VFUNCOFF = 0x140C, + LF_NESTTYPEEX_ST = 0x140D, + LF_MEMBERMODIFY_ST = 0x140E, + LF_MANAGED_ST = 0x140F, + + // Types w/ SZ names + LF_ST_MAX = 0x1500, + + LF_TYPESERVER = 0x1501, // not referenced from symbol + LF_ENUMERATE = 0x1502, + LF_ARRAY = 0x1503, + LF_CLASS = 0x1504, + LF_STRUCTURE = 0x1505, + LF_UNION = 0x1506, + LF_ENUM = 0x1507, + LF_DIMARRAY = 0x1508, + LF_PRECOMP = 0x1509, // not referenced from symbol + LF_ALIAS = 0x150A, // alias (typedef) type + LF_DEFARG = 0x150B, + LF_FRIENDFCN = 0x150C, + LF_MEMBER = 0x150D, + LF_STMEMBER = 0x150E, + LF_METHOD = 0x150F, + LF_NESTTYPE = 0x1510, + LF_ONEMETHOD = 0x1511, + LF_NESTTYPEEX = 0x1512, + LF_MEMBERMODIFY = 0x1513, + LF_MANAGED = 0x1514, + LF_TYPESERVER2 = 0x1515, + + LF_STRIDED_ARRAY = 0x1516, // same as LF_ARRAY, but with stride between adjacent elements + LF_HLSL = 0x1517, + LF_MODIFIER_EX = 0x1518, + LF_INTERFACE = 0x1519, + LF_BINTERFACE = 0x151A, + LF_VECTOR = 0x151B, + LF_MATRIX = 0x151C, + + LF_VFTABLE = 0x151D, // a virtual function table + LF_ENDOFLEAFRECORD = LF_VFTABLE, + + LF_TYPE_LAST, // one greater than the last type record + LF_TYPE_MAX = LF_TYPE_LAST - 1, + + LF_FUNC_ID = 0x1601, // global func ID + LF_MFUNC_ID = 0x1602, // member func ID + LF_BUILDINFO = 0x1603, // build info: tool, version, command line, src/pdb file + LF_SUBSTR_LIST = 0x1604, // similar to LF_ARGLIST, for list of sub strings + LF_STRING_ID = 0x1605, // string ID + + LF_UDT_SRC_LINE = 0x1606, // source and line on where an UDT is defined (only generated by compiler) + + LF_UDT_MOD_SRC_LINE = 0x1607, // module, source and line on where an UDT is defined (only generated by linker) + + LF_ID_LAST, // one greater than the last ID record + LF_ID_MAX = LF_ID_LAST - 1, + + LF_NUMERIC = 0x8000, + LF_CHAR = 0x8000, + LF_SHORT = 0x8001, + LF_USHORT = 0x8002, + LF_LONG = 0x8003, + LF_ULONG = 0x8004, + LF_REAL32 = 0x8005, + LF_REAL64 = 0x8006, + LF_REAL80 = 0x8007, + LF_REAL128 = 0x8008, + LF_QUADWORD = 0x8009, + LF_UQUADWORD = 0x800A, + LF_REAL48 = 0x800B, + LF_COMPLEX32 = 0x800C, + LF_COMPLEX64 = 0x800D, + LF_COMPLEX80 = 0x800E, + LF_COMPLEX128 = 0x800F, + LF_VARSTRING = 0x8010, + + LF_OCTWORD = 0x8017, + LF_UOCTWORD = 0x8018, + + LF_DECIMAL = 0x8019, + LF_DATE = 0x801A, + LF_UTF8STRING = 0x801B, + + LF_REAL16 = 0x801C, + + LF_PAD0 = 0xF0, + LF_PAD1 = 0xF1, + LF_PAD2 = 0xF2, + LF_PAD3 = 0xF3, + LF_PAD4 = 0xF4, + LF_PAD5 = 0xF5, + LF_PAD6 = 0xF6, + LF_PAD7 = 0xF7, + LF_PAD8 = 0xF8, + LF_PAD9 = 0xF9, + LF_PAD10 = 0xFA, + LF_PAD11 = 0xFB, + LF_PAD12 = 0xFC, + LF_PAD13 = 0xFD, + LF_PAD14 = 0xFE, + LF_PAD15 = 0xFF, + } + + // Matches CV_HREG_e in cvinfo.h + public enum CodeViewRegister : ushort + { + // Register subset shared by all processor types, + // must not overlap with any of the ranges below, hence the high values + CV_ALLREG_ERR = 30000, + CV_ALLREG_TEB = 30001, + CV_ALLREG_TIMER = 30002, + CV_ALLREG_EFAD1 = 30003, + CV_ALLREG_EFAD2 = 30004, + CV_ALLREG_EFAD3 = 30005, + CV_ALLREG_VFRAME = 30006, + CV_ALLREG_HANDLE = 30007, + CV_ALLREG_PARAMS = 30008, + CV_ALLREG_LOCALS = 30009, + CV_ALLREG_TID = 30010, + CV_ALLREG_ENV = 30011, + CV_ALLREG_CMDLN = 30012, + + // Register set for the Intel 80x86 and ix86 processor series + // (plus PCODE registers) + CV_REG_NONE = 0, + CV_REG_AL = 1, + CV_REG_CL = 2, + CV_REG_DL = 3, + CV_REG_BL = 4, + CV_REG_AH = 5, + CV_REG_CH = 6, + CV_REG_DH = 7, + CV_REG_BH = 8, + CV_REG_AX = 9, + CV_REG_CX = 10, + CV_REG_DX = 11, + CV_REG_BX = 12, + CV_REG_SP = 13, + CV_REG_BP = 14, + CV_REG_SI = 15, + CV_REG_DI = 16, + CV_REG_EAX = 17, + CV_REG_ECX = 18, + CV_REG_EDX = 19, + CV_REG_EBX = 20, + CV_REG_ESP = 21, + CV_REG_EBP = 22, + CV_REG_ESI = 23, + CV_REG_EDI = 24, + CV_REG_ES = 25, + CV_REG_CS = 26, + CV_REG_SS = 27, + CV_REG_DS = 28, + CV_REG_FS = 29, + CV_REG_GS = 30, + CV_REG_IP = 31, + CV_REG_FLAGS = 32, + CV_REG_EIP = 33, + CV_REG_EFLAGS = 34, + CV_REG_TEMP = 40, // PCODE Temp + CV_REG_TEMPH = 41, // PCODE TempH + CV_REG_QUOTE = 42, // PCODE Quote + CV_REG_PCDR3 = 43, // PCODE reserved + CV_REG_PCDR4 = 44, // PCODE reserved + CV_REG_PCDR5 = 45, // PCODE reserved + CV_REG_PCDR6 = 46, // PCODE reserved + CV_REG_PCDR7 = 47, // PCODE reserved + CV_REG_CR0 = 80, // CR0 -- control registers + CV_REG_CR1 = 81, + CV_REG_CR2 = 82, + CV_REG_CR3 = 83, + CV_REG_CR4 = 84, // Pentium + CV_REG_DR0 = 90, // Debug register + CV_REG_DR1 = 91, + CV_REG_DR2 = 92, + CV_REG_DR3 = 93, + CV_REG_DR4 = 94, + CV_REG_DR5 = 95, + CV_REG_DR6 = 96, + CV_REG_DR7 = 97, + CV_REG_GDTR = 110, + CV_REG_GDTL = 111, + CV_REG_IDTR = 112, + CV_REG_IDTL = 113, + CV_REG_LDTR = 114, + CV_REG_TR = 115, + + CV_REG_PSEUDO1 = 116, + CV_REG_PSEUDO2 = 117, + CV_REG_PSEUDO3 = 118, + CV_REG_PSEUDO4 = 119, + CV_REG_PSEUDO5 = 120, + CV_REG_PSEUDO6 = 121, + CV_REG_PSEUDO7 = 122, + CV_REG_PSEUDO8 = 123, + CV_REG_PSEUDO9 = 124, + + CV_REG_ST0 = 128, + CV_REG_ST1 = 129, + CV_REG_ST2 = 130, + CV_REG_ST3 = 131, + CV_REG_ST4 = 132, + CV_REG_ST5 = 133, + CV_REG_ST6 = 134, + CV_REG_ST7 = 135, + CV_REG_CTRL = 136, + CV_REG_STAT = 137, + CV_REG_TAG = 138, + CV_REG_FPIP = 139, + CV_REG_FPCS = 140, + CV_REG_FPDO = 141, + CV_REG_FPDS = 142, + CV_REG_ISEM = 143, + CV_REG_FPEIP = 144, + CV_REG_FPEDO = 145, + + CV_REG_MM0 = 146, + CV_REG_MM1 = 147, + CV_REG_MM2 = 148, + CV_REG_MM3 = 149, + CV_REG_MM4 = 150, + CV_REG_MM5 = 151, + CV_REG_MM6 = 152, + CV_REG_MM7 = 153, + + // KATMAI registers + CV_REG_XMM0 = 154, + CV_REG_XMM1 = 155, + CV_REG_XMM2 = 156, + CV_REG_XMM3 = 157, + CV_REG_XMM4 = 158, + CV_REG_XMM5 = 159, + CV_REG_XMM6 = 160, + CV_REG_XMM7 = 161, + + // KATMAI sub-registers + CV_REG_XMM00 = 162, + CV_REG_XMM01 = 163, + CV_REG_XMM02 = 164, + CV_REG_XMM03 = 165, + CV_REG_XMM10 = 166, + CV_REG_XMM11 = 167, + CV_REG_XMM12 = 168, + CV_REG_XMM13 = 169, + CV_REG_XMM20 = 170, + CV_REG_XMM21 = 171, + CV_REG_XMM22 = 172, + CV_REG_XMM23 = 173, + CV_REG_XMM30 = 174, + CV_REG_XMM31 = 175, + CV_REG_XMM32 = 176, + CV_REG_XMM33 = 177, + CV_REG_XMM40 = 178, + CV_REG_XMM41 = 179, + CV_REG_XMM42 = 180, + CV_REG_XMM43 = 181, + CV_REG_XMM50 = 182, + CV_REG_XMM51 = 183, + CV_REG_XMM52 = 184, + CV_REG_XMM53 = 185, + CV_REG_XMM60 = 186, + CV_REG_XMM61 = 187, + CV_REG_XMM62 = 188, + CV_REG_XMM63 = 189, + CV_REG_XMM70 = 190, + CV_REG_XMM71 = 191, + CV_REG_XMM72 = 192, + CV_REG_XMM73 = 193, + + CV_REG_XMM0L = 194, + CV_REG_XMM1L = 195, + CV_REG_XMM2L = 196, + CV_REG_XMM3L = 197, + CV_REG_XMM4L = 198, + CV_REG_XMM5L = 199, + CV_REG_XMM6L = 200, + CV_REG_XMM7L = 201, + + CV_REG_XMM0H = 202, + CV_REG_XMM1H = 203, + CV_REG_XMM2H = 204, + CV_REG_XMM3H = 205, + CV_REG_XMM4H = 206, + CV_REG_XMM5H = 207, + CV_REG_XMM6H = 208, + CV_REG_XMM7H = 209, + + // XMM status register + CV_REG_MXCSR = 211, + + // EDX:EAX pair + CV_REG_EDXEAX = 212, + + // XMM sub-registers (WNI integer) + CV_REG_EMM0L = 220, + CV_REG_EMM1L = 221, + CV_REG_EMM2L = 222, + CV_REG_EMM3L = 223, + CV_REG_EMM4L = 224, + CV_REG_EMM5L = 225, + CV_REG_EMM6L = 226, + CV_REG_EMM7L = 227, + + CV_REG_EMM0H = 228, + CV_REG_EMM1H = 229, + CV_REG_EMM2H = 230, + CV_REG_EMM3H = 231, + CV_REG_EMM4H = 232, + CV_REG_EMM5H = 233, + CV_REG_EMM6H = 234, + CV_REG_EMM7H = 235, + + // Do not change the order of these regs, first one must be even too + CV_REG_MM00 = 236, + CV_REG_MM01 = 237, + CV_REG_MM10 = 238, + CV_REG_MM11 = 239, + CV_REG_MM20 = 240, + CV_REG_MM21 = 241, + CV_REG_MM30 = 242, + CV_REG_MM31 = 243, + CV_REG_MM40 = 244, + CV_REG_MM41 = 245, + CV_REG_MM50 = 246, + CV_REG_MM51 = 247, + CV_REG_MM60 = 248, + CV_REG_MM61 = 249, + CV_REG_MM70 = 250, + CV_REG_MM71 = 251, + + // AVX registers + CV_REG_YMM0 = 252, + CV_REG_YMM1 = 253, + CV_REG_YMM2 = 254, + CV_REG_YMM3 = 255, + CV_REG_YMM4 = 256, + CV_REG_YMM5 = 257, + CV_REG_YMM6 = 258, + CV_REG_YMM7 = 259, + + CV_REG_YMM0H = 260, + CV_REG_YMM1H = 261, + CV_REG_YMM2H = 262, + CV_REG_YMM3H = 263, + CV_REG_YMM4H = 264, + CV_REG_YMM5H = 265, + CV_REG_YMM6H = 266, + CV_REG_YMM7H = 267, + + // AVX integer registers + CV_REG_YMM0I0 = 268, + CV_REG_YMM0I1 = 269, + CV_REG_YMM0I2 = 270, + CV_REG_YMM0I3 = 271, + CV_REG_YMM1I0 = 272, + CV_REG_YMM1I1 = 273, + CV_REG_YMM1I2 = 274, + CV_REG_YMM1I3 = 275, + CV_REG_YMM2I0 = 276, + CV_REG_YMM2I1 = 277, + CV_REG_YMM2I2 = 278, + CV_REG_YMM2I3 = 279, + CV_REG_YMM3I0 = 280, + CV_REG_YMM3I1 = 281, + CV_REG_YMM3I2 = 282, + CV_REG_YMM3I3 = 283, + CV_REG_YMM4I0 = 284, + CV_REG_YMM4I1 = 285, + CV_REG_YMM4I2 = 286, + CV_REG_YMM4I3 = 287, + CV_REG_YMM5I0 = 288, + CV_REG_YMM5I1 = 289, + CV_REG_YMM5I2 = 290, + CV_REG_YMM5I3 = 291, + CV_REG_YMM6I0 = 292, + CV_REG_YMM6I1 = 293, + CV_REG_YMM6I2 = 294, + CV_REG_YMM6I3 = 295, + CV_REG_YMM7I0 = 296, + CV_REG_YMM7I1 = 297, + CV_REG_YMM7I2 = 298, + CV_REG_YMM7I3 = 299, + + // AVX floating-point single precise registers + CV_REG_YMM0F0 = 300, + CV_REG_YMM0F1 = 301, + CV_REG_YMM0F2 = 302, + CV_REG_YMM0F3 = 303, + CV_REG_YMM0F4 = 304, + CV_REG_YMM0F5 = 305, + CV_REG_YMM0F6 = 306, + CV_REG_YMM0F7 = 307, + CV_REG_YMM1F0 = 308, + CV_REG_YMM1F1 = 309, + CV_REG_YMM1F2 = 310, + CV_REG_YMM1F3 = 311, + CV_REG_YMM1F4 = 312, + CV_REG_YMM1F5 = 313, + CV_REG_YMM1F6 = 314, + CV_REG_YMM1F7 = 315, + CV_REG_YMM2F0 = 316, + CV_REG_YMM2F1 = 317, + CV_REG_YMM2F2 = 318, + CV_REG_YMM2F3 = 319, + CV_REG_YMM2F4 = 320, + CV_REG_YMM2F5 = 321, + CV_REG_YMM2F6 = 322, + CV_REG_YMM2F7 = 323, + CV_REG_YMM3F0 = 324, + CV_REG_YMM3F1 = 325, + CV_REG_YMM3F2 = 326, + CV_REG_YMM3F3 = 327, + CV_REG_YMM3F4 = 328, + CV_REG_YMM3F5 = 329, + CV_REG_YMM3F6 = 330, + CV_REG_YMM3F7 = 331, + CV_REG_YMM4F0 = 332, + CV_REG_YMM4F1 = 333, + CV_REG_YMM4F2 = 334, + CV_REG_YMM4F3 = 335, + CV_REG_YMM4F4 = 336, + CV_REG_YMM4F5 = 337, + CV_REG_YMM4F6 = 338, + CV_REG_YMM4F7 = 339, + CV_REG_YMM5F0 = 340, + CV_REG_YMM5F1 = 341, + CV_REG_YMM5F2 = 342, + CV_REG_YMM5F3 = 343, + CV_REG_YMM5F4 = 344, + CV_REG_YMM5F5 = 345, + CV_REG_YMM5F6 = 346, + CV_REG_YMM5F7 = 347, + CV_REG_YMM6F0 = 348, + CV_REG_YMM6F1 = 349, + CV_REG_YMM6F2 = 350, + CV_REG_YMM6F3 = 351, + CV_REG_YMM6F4 = 352, + CV_REG_YMM6F5 = 353, + CV_REG_YMM6F6 = 354, + CV_REG_YMM6F7 = 355, + CV_REG_YMM7F0 = 356, + CV_REG_YMM7F1 = 357, + CV_REG_YMM7F2 = 358, + CV_REG_YMM7F3 = 359, + CV_REG_YMM7F4 = 360, + CV_REG_YMM7F5 = 361, + CV_REG_YMM7F6 = 362, + CV_REG_YMM7F7 = 363, + + // AVX floating-point double precise registers + CV_REG_YMM0D0 = 364, + CV_REG_YMM0D1 = 365, + CV_REG_YMM0D2 = 366, + CV_REG_YMM0D3 = 367, + CV_REG_YMM1D0 = 368, + CV_REG_YMM1D1 = 369, + CV_REG_YMM1D2 = 370, + CV_REG_YMM1D3 = 371, + CV_REG_YMM2D0 = 372, + CV_REG_YMM2D1 = 373, + CV_REG_YMM2D2 = 374, + CV_REG_YMM2D3 = 375, + CV_REG_YMM3D0 = 376, + CV_REG_YMM3D1 = 377, + CV_REG_YMM3D2 = 378, + CV_REG_YMM3D3 = 379, + CV_REG_YMM4D0 = 380, + CV_REG_YMM4D1 = 381, + CV_REG_YMM4D2 = 382, + CV_REG_YMM4D3 = 383, + CV_REG_YMM5D0 = 384, + CV_REG_YMM5D1 = 385, + CV_REG_YMM5D2 = 386, + CV_REG_YMM5D3 = 387, + CV_REG_YMM6D0 = 388, + CV_REG_YMM6D1 = 389, + CV_REG_YMM6D2 = 390, + CV_REG_YMM6D3 = 391, + CV_REG_YMM7D0 = 392, + CV_REG_YMM7D1 = 393, + CV_REG_YMM7D2 = 394, + CV_REG_YMM7D3 = 395, + + CV_REG_BND0 = 396, + CV_REG_BND1 = 397, + CV_REG_BND2 = 398, + CV_REG_BND3 = 399, + + // Register set for the ARM processor. + CV_ARM_NOREG = CV_REG_NONE, + + CV_ARM_R0 = 10, + CV_ARM_R1 = 11, + CV_ARM_R2 = 12, + CV_ARM_R3 = 13, + CV_ARM_R4 = 14, + CV_ARM_R5 = 15, + CV_ARM_R6 = 16, + CV_ARM_R7 = 17, + CV_ARM_R8 = 18, + CV_ARM_R9 = 19, + CV_ARM_R10 = 20, + CV_ARM_R11 = 21, // Frame pointer, if allocated + CV_ARM_R12 = 22, + CV_ARM_SP = 23, // Stack pointer + CV_ARM_LR = 24, // Link Register + CV_ARM_PC = 25, // Program counter + CV_ARM_CPSR = 26, // Current program status register + + CV_ARM_ACC0 = 27, // DSP co-processor 0 40 bit accumulator + + // Registers for ARM VFP10 support + CV_ARM_FPSCR = 40, + CV_ARM_FPEXC = 41, + + CV_ARM_FS0 = 50, + CV_ARM_FS1 = 51, + CV_ARM_FS2 = 52, + CV_ARM_FS3 = 53, + CV_ARM_FS4 = 54, + CV_ARM_FS5 = 55, + CV_ARM_FS6 = 56, + CV_ARM_FS7 = 57, + CV_ARM_FS8 = 58, + CV_ARM_FS9 = 59, + CV_ARM_FS10 = 60, + CV_ARM_FS11 = 61, + CV_ARM_FS12 = 62, + CV_ARM_FS13 = 63, + CV_ARM_FS14 = 64, + CV_ARM_FS15 = 65, + CV_ARM_FS16 = 66, + CV_ARM_FS17 = 67, + CV_ARM_FS18 = 68, + CV_ARM_FS19 = 69, + CV_ARM_FS20 = 70, + CV_ARM_FS21 = 71, + CV_ARM_FS22 = 72, + CV_ARM_FS23 = 73, + CV_ARM_FS24 = 74, + CV_ARM_FS25 = 75, + CV_ARM_FS26 = 76, + CV_ARM_FS27 = 77, + CV_ARM_FS28 = 78, + CV_ARM_FS29 = 79, + CV_ARM_FS30 = 80, + CV_ARM_FS31 = 81, + + // ARM VFP Floating Point Extra control registers + CV_ARM_FPEXTRA0 = 90, + CV_ARM_FPEXTRA1 = 91, + CV_ARM_FPEXTRA2 = 92, + CV_ARM_FPEXTRA3 = 93, + CV_ARM_FPEXTRA4 = 94, + CV_ARM_FPEXTRA5 = 95, + CV_ARM_FPEXTRA6 = 96, + CV_ARM_FPEXTRA7 = 97, + + // XSCALE Concan co-processor registers + CV_ARM_WR0 = 128, + CV_ARM_WR1 = 129, + CV_ARM_WR2 = 130, + CV_ARM_WR3 = 131, + CV_ARM_WR4 = 132, + CV_ARM_WR5 = 133, + CV_ARM_WR6 = 134, + CV_ARM_WR7 = 135, + CV_ARM_WR8 = 136, + CV_ARM_WR9 = 137, + CV_ARM_WR10 = 138, + CV_ARM_WR11 = 139, + CV_ARM_WR12 = 140, + CV_ARM_WR13 = 141, + CV_ARM_WR14 = 142, + CV_ARM_WR15 = 143, + + // XSCALE Concan co-processor control registers + CV_ARM_WCID = 144, + CV_ARM_WCON = 145, + CV_ARM_WCSSF = 146, + CV_ARM_WCASF = 147, + CV_ARM_WC4 = 148, + CV_ARM_WC5 = 149, + CV_ARM_WC6 = 150, + CV_ARM_WC7 = 151, + CV_ARM_WCGR0 = 152, + CV_ARM_WCGR1 = 153, + CV_ARM_WCGR2 = 154, + CV_ARM_WCGR3 = 155, + CV_ARM_WC12 = 156, + CV_ARM_WC13 = 157, + CV_ARM_WC14 = 158, + CV_ARM_WC15 = 159, + + // ARM VFPv3/Neon extended floating Point + CV_ARM_FS32 = 200, + CV_ARM_FS33 = 201, + CV_ARM_FS34 = 202, + CV_ARM_FS35 = 203, + CV_ARM_FS36 = 204, + CV_ARM_FS37 = 205, + CV_ARM_FS38 = 206, + CV_ARM_FS39 = 207, + CV_ARM_FS40 = 208, + CV_ARM_FS41 = 209, + CV_ARM_FS42 = 210, + CV_ARM_FS43 = 211, + CV_ARM_FS44 = 212, + CV_ARM_FS45 = 213, + CV_ARM_FS46 = 214, + CV_ARM_FS47 = 215, + CV_ARM_FS48 = 216, + CV_ARM_FS49 = 217, + CV_ARM_FS50 = 218, + CV_ARM_FS51 = 219, + CV_ARM_FS52 = 220, + CV_ARM_FS53 = 221, + CV_ARM_FS54 = 222, + CV_ARM_FS55 = 223, + CV_ARM_FS56 = 224, + CV_ARM_FS57 = 225, + CV_ARM_FS58 = 226, + CV_ARM_FS59 = 227, + CV_ARM_FS60 = 228, + CV_ARM_FS61 = 229, + CV_ARM_FS62 = 230, + CV_ARM_FS63 = 231, + + // ARM double-precision floating point + CV_ARM_ND0 = 300, + CV_ARM_ND1 = 301, + CV_ARM_ND2 = 302, + CV_ARM_ND3 = 303, + CV_ARM_ND4 = 304, + CV_ARM_ND5 = 305, + CV_ARM_ND6 = 306, + CV_ARM_ND7 = 307, + CV_ARM_ND8 = 308, + CV_ARM_ND9 = 309, + CV_ARM_ND10 = 310, + CV_ARM_ND11 = 311, + CV_ARM_ND12 = 312, + CV_ARM_ND13 = 313, + CV_ARM_ND14 = 314, + CV_ARM_ND15 = 315, + CV_ARM_ND16 = 316, + CV_ARM_ND17 = 317, + CV_ARM_ND18 = 318, + CV_ARM_ND19 = 319, + CV_ARM_ND20 = 320, + CV_ARM_ND21 = 321, + CV_ARM_ND22 = 322, + CV_ARM_ND23 = 323, + CV_ARM_ND24 = 324, + CV_ARM_ND25 = 325, + CV_ARM_ND26 = 326, + CV_ARM_ND27 = 327, + CV_ARM_ND28 = 328, + CV_ARM_ND29 = 329, + CV_ARM_ND30 = 330, + CV_ARM_ND31 = 331, + + // ARM extended precision floating point + CV_ARM_NQ0 = 400, + CV_ARM_NQ1 = 401, + CV_ARM_NQ2 = 402, + CV_ARM_NQ3 = 403, + CV_ARM_NQ4 = 404, + CV_ARM_NQ5 = 405, + CV_ARM_NQ6 = 406, + CV_ARM_NQ7 = 407, + CV_ARM_NQ8 = 408, + CV_ARM_NQ9 = 409, + CV_ARM_NQ10 = 410, + CV_ARM_NQ11 = 411, + CV_ARM_NQ12 = 412, + CV_ARM_NQ13 = 413, + CV_ARM_NQ14 = 414, + CV_ARM_NQ15 = 415, + + // Register set for ARM64 + CV_ARM64_NOREG = CV_REG_NONE, + + // General purpose 32-bit integer registers + CV_ARM64_W0 = 10, + CV_ARM64_W1 = 11, + CV_ARM64_W2 = 12, + CV_ARM64_W3 = 13, + CV_ARM64_W4 = 14, + CV_ARM64_W5 = 15, + CV_ARM64_W6 = 16, + CV_ARM64_W7 = 17, + CV_ARM64_W8 = 18, + CV_ARM64_W9 = 19, + CV_ARM64_W10 = 20, + CV_ARM64_W11 = 21, + CV_ARM64_W12 = 22, + CV_ARM64_W13 = 23, + CV_ARM64_W14 = 24, + CV_ARM64_W15 = 25, + CV_ARM64_W16 = 26, + CV_ARM64_W17 = 27, + CV_ARM64_W18 = 28, + CV_ARM64_W19 = 29, + CV_ARM64_W20 = 30, + CV_ARM64_W21 = 31, + CV_ARM64_W22 = 32, + CV_ARM64_W23 = 33, + CV_ARM64_W24 = 34, + CV_ARM64_W25 = 35, + CV_ARM64_W26 = 36, + CV_ARM64_W27 = 37, + CV_ARM64_W28 = 38, + CV_ARM64_W29 = 39, + CV_ARM64_W30 = 40, + CV_ARM64_WZR = 41, + + // General purpose 64-bit integer registers + CV_ARM64_X0 = 50, + CV_ARM64_X1 = 51, + CV_ARM64_X2 = 52, + CV_ARM64_X3 = 53, + CV_ARM64_X4 = 54, + CV_ARM64_X5 = 55, + CV_ARM64_X6 = 56, + CV_ARM64_X7 = 57, + CV_ARM64_X8 = 58, + CV_ARM64_X9 = 59, + CV_ARM64_X10 = 60, + CV_ARM64_X11 = 61, + CV_ARM64_X12 = 62, + CV_ARM64_X13 = 63, + CV_ARM64_X14 = 64, + CV_ARM64_X15 = 65, + CV_ARM64_IP0 = 66, + CV_ARM64_IP1 = 67, + CV_ARM64_X18 = 68, + CV_ARM64_X19 = 69, + CV_ARM64_X20 = 70, + CV_ARM64_X21 = 71, + CV_ARM64_X22 = 72, + CV_ARM64_X23 = 73, + CV_ARM64_X24 = 74, + CV_ARM64_X25 = 75, + CV_ARM64_X26 = 76, + CV_ARM64_X27 = 77, + CV_ARM64_X28 = 78, + CV_ARM64_FP = 79, + CV_ARM64_LR = 80, + CV_ARM64_SP = 81, + CV_ARM64_ZR = 82, + CV_ARM64_PC = 83, + + // status registers + CV_ARM64_NZCV = 90, + CV_ARM64_CPSR = 91, + + // 32-bit floating point registers + CV_ARM64_S0 = 100, + CV_ARM64_S1 = 101, + CV_ARM64_S2 = 102, + CV_ARM64_S3 = 103, + CV_ARM64_S4 = 104, + CV_ARM64_S5 = 105, + CV_ARM64_S6 = 106, + CV_ARM64_S7 = 107, + CV_ARM64_S8 = 108, + CV_ARM64_S9 = 109, + CV_ARM64_S10 = 110, + CV_ARM64_S11 = 111, + CV_ARM64_S12 = 112, + CV_ARM64_S13 = 113, + CV_ARM64_S14 = 114, + CV_ARM64_S15 = 115, + CV_ARM64_S16 = 116, + CV_ARM64_S17 = 117, + CV_ARM64_S18 = 118, + CV_ARM64_S19 = 119, + CV_ARM64_S20 = 120, + CV_ARM64_S21 = 121, + CV_ARM64_S22 = 122, + CV_ARM64_S23 = 123, + CV_ARM64_S24 = 124, + CV_ARM64_S25 = 125, + CV_ARM64_S26 = 126, + CV_ARM64_S27 = 127, + CV_ARM64_S28 = 128, + CV_ARM64_S29 = 129, + CV_ARM64_S30 = 130, + CV_ARM64_S31 = 131, + + // 64-bit floating point registers + CV_ARM64_D0 = 140, + CV_ARM64_D1 = 141, + CV_ARM64_D2 = 142, + CV_ARM64_D3 = 143, + CV_ARM64_D4 = 144, + CV_ARM64_D5 = 145, + CV_ARM64_D6 = 146, + CV_ARM64_D7 = 147, + CV_ARM64_D8 = 148, + CV_ARM64_D9 = 149, + CV_ARM64_D10 = 150, + CV_ARM64_D11 = 151, + CV_ARM64_D12 = 152, + CV_ARM64_D13 = 153, + CV_ARM64_D14 = 154, + CV_ARM64_D15 = 155, + CV_ARM64_D16 = 156, + CV_ARM64_D17 = 157, + CV_ARM64_D18 = 158, + CV_ARM64_D19 = 159, + CV_ARM64_D20 = 160, + CV_ARM64_D21 = 161, + CV_ARM64_D22 = 162, + CV_ARM64_D23 = 163, + CV_ARM64_D24 = 164, + CV_ARM64_D25 = 165, + CV_ARM64_D26 = 166, + CV_ARM64_D27 = 167, + CV_ARM64_D28 = 168, + CV_ARM64_D29 = 169, + CV_ARM64_D30 = 170, + CV_ARM64_D31 = 171, + + // 128-bit SIMD registers + CV_ARM64_Q0 = 180, + CV_ARM64_Q1 = 181, + CV_ARM64_Q2 = 182, + CV_ARM64_Q3 = 183, + CV_ARM64_Q4 = 184, + CV_ARM64_Q5 = 185, + CV_ARM64_Q6 = 186, + CV_ARM64_Q7 = 187, + CV_ARM64_Q8 = 188, + CV_ARM64_Q9 = 189, + CV_ARM64_Q10 = 190, + CV_ARM64_Q11 = 191, + CV_ARM64_Q12 = 192, + CV_ARM64_Q13 = 193, + CV_ARM64_Q14 = 194, + CV_ARM64_Q15 = 195, + CV_ARM64_Q16 = 196, + CV_ARM64_Q17 = 197, + CV_ARM64_Q18 = 198, + CV_ARM64_Q19 = 199, + CV_ARM64_Q20 = 200, + CV_ARM64_Q21 = 201, + CV_ARM64_Q22 = 202, + CV_ARM64_Q23 = 203, + CV_ARM64_Q24 = 204, + CV_ARM64_Q25 = 205, + CV_ARM64_Q26 = 206, + CV_ARM64_Q27 = 207, + CV_ARM64_Q28 = 208, + CV_ARM64_Q29 = 209, + CV_ARM64_Q30 = 210, + CV_ARM64_Q31 = 211, + + // Floating point status register + CV_ARM64_FPSR = 220, + + // AMD64 registers + CV_AMD64_AL = 1, + CV_AMD64_CL = 2, + CV_AMD64_DL = 3, + CV_AMD64_BL = 4, + CV_AMD64_AH = 5, + CV_AMD64_CH = 6, + CV_AMD64_DH = 7, + CV_AMD64_BH = 8, + CV_AMD64_AX = 9, + CV_AMD64_CX = 10, + CV_AMD64_DX = 11, + CV_AMD64_BX = 12, + CV_AMD64_SP = 13, + CV_AMD64_BP = 14, + CV_AMD64_SI = 15, + CV_AMD64_DI = 16, + CV_AMD64_EAX = 17, + CV_AMD64_ECX = 18, + CV_AMD64_EDX = 19, + CV_AMD64_EBX = 20, + CV_AMD64_ESP = 21, + CV_AMD64_EBP = 22, + CV_AMD64_ESI = 23, + CV_AMD64_EDI = 24, + CV_AMD64_ES = 25, + CV_AMD64_CS = 26, + CV_AMD64_SS = 27, + CV_AMD64_DS = 28, + CV_AMD64_FS = 29, + CV_AMD64_GS = 30, + CV_AMD64_FLAGS = 32, + CV_AMD64_RIP = 33, + CV_AMD64_EFLAGS = 34, + + // Control registers + CV_AMD64_CR0 = 80, + CV_AMD64_CR1 = 81, + CV_AMD64_CR2 = 82, + CV_AMD64_CR3 = 83, + CV_AMD64_CR4 = 84, + CV_AMD64_CR8 = 88, + + // Debug registers + CV_AMD64_DR0 = 90, + CV_AMD64_DR1 = 91, + CV_AMD64_DR2 = 92, + CV_AMD64_DR3 = 93, + CV_AMD64_DR4 = 94, + CV_AMD64_DR5 = 95, + CV_AMD64_DR6 = 96, + CV_AMD64_DR7 = 97, + CV_AMD64_DR8 = 98, + CV_AMD64_DR9 = 99, + CV_AMD64_DR10 = 100, + CV_AMD64_DR11 = 101, + CV_AMD64_DR12 = 102, + CV_AMD64_DR13 = 103, + CV_AMD64_DR14 = 104, + CV_AMD64_DR15 = 105, + + CV_AMD64_GDTR = 110, + CV_AMD64_GDTL = 111, + CV_AMD64_IDTR = 112, + CV_AMD64_IDTL = 113, + CV_AMD64_LDTR = 114, + CV_AMD64_TR = 115, + + CV_AMD64_ST0 = 128, + CV_AMD64_ST1 = 129, + CV_AMD64_ST2 = 130, + CV_AMD64_ST3 = 131, + CV_AMD64_ST4 = 132, + CV_AMD64_ST5 = 133, + CV_AMD64_ST6 = 134, + CV_AMD64_ST7 = 135, + CV_AMD64_CTRL = 136, + CV_AMD64_STAT = 137, + CV_AMD64_TAG = 138, + CV_AMD64_FPIP = 139, + CV_AMD64_FPCS = 140, + CV_AMD64_FPDO = 141, + CV_AMD64_FPDS = 142, + CV_AMD64_ISEM = 143, + CV_AMD64_FPEIP = 144, + CV_AMD64_FPEDO = 145, + + CV_AMD64_MM0 = 146, + CV_AMD64_MM1 = 147, + CV_AMD64_MM2 = 148, + CV_AMD64_MM3 = 149, + CV_AMD64_MM4 = 150, + CV_AMD64_MM5 = 151, + CV_AMD64_MM6 = 152, + CV_AMD64_MM7 = 153, + + // KATMAI registers + CV_AMD64_XMM0 = 154, + CV_AMD64_XMM1 = 155, + CV_AMD64_XMM2 = 156, + CV_AMD64_XMM3 = 157, + CV_AMD64_XMM4 = 158, + CV_AMD64_XMM5 = 159, + CV_AMD64_XMM6 = 160, + CV_AMD64_XMM7 = 161, + + // KATMAI sub-registers + CV_AMD64_XMM0_0 = 162, + CV_AMD64_XMM0_1 = 163, + CV_AMD64_XMM0_2 = 164, + CV_AMD64_XMM0_3 = 165, + CV_AMD64_XMM1_0 = 166, + CV_AMD64_XMM1_1 = 167, + CV_AMD64_XMM1_2 = 168, + CV_AMD64_XMM1_3 = 169, + CV_AMD64_XMM2_0 = 170, + CV_AMD64_XMM2_1 = 171, + CV_AMD64_XMM2_2 = 172, + CV_AMD64_XMM2_3 = 173, + CV_AMD64_XMM3_0 = 174, + CV_AMD64_XMM3_1 = 175, + CV_AMD64_XMM3_2 = 176, + CV_AMD64_XMM3_3 = 177, + CV_AMD64_XMM4_0 = 178, + CV_AMD64_XMM4_1 = 179, + CV_AMD64_XMM4_2 = 180, + CV_AMD64_XMM4_3 = 181, + CV_AMD64_XMM5_0 = 182, + CV_AMD64_XMM5_1 = 183, + CV_AMD64_XMM5_2 = 184, + CV_AMD64_XMM5_3 = 185, + CV_AMD64_XMM6_0 = 186, + CV_AMD64_XMM6_1 = 187, + CV_AMD64_XMM6_2 = 188, + CV_AMD64_XMM6_3 = 189, + CV_AMD64_XMM7_0 = 190, + CV_AMD64_XMM7_1 = 191, + CV_AMD64_XMM7_2 = 192, + CV_AMD64_XMM7_3 = 193, + + CV_AMD64_XMM0L = 194, + CV_AMD64_XMM1L = 195, + CV_AMD64_XMM2L = 196, + CV_AMD64_XMM3L = 197, + CV_AMD64_XMM4L = 198, + CV_AMD64_XMM5L = 199, + CV_AMD64_XMM6L = 200, + CV_AMD64_XMM7L = 201, + + CV_AMD64_XMM0H = 202, + CV_AMD64_XMM1H = 203, + CV_AMD64_XMM2H = 204, + CV_AMD64_XMM3H = 205, + CV_AMD64_XMM4H = 206, + CV_AMD64_XMM5H = 207, + CV_AMD64_XMM6H = 208, + CV_AMD64_XMM7H = 209, + + // XMM status register + CV_AMD64_MXCSR = 211, + + // XMM sub-registers (WNI integer) + CV_AMD64_EMM0L = 220, + CV_AMD64_EMM1L = 221, + CV_AMD64_EMM2L = 222, + CV_AMD64_EMM3L = 223, + CV_AMD64_EMM4L = 224, + CV_AMD64_EMM5L = 225, + CV_AMD64_EMM6L = 226, + CV_AMD64_EMM7L = 227, + + CV_AMD64_EMM0H = 228, + CV_AMD64_EMM1H = 229, + CV_AMD64_EMM2H = 230, + CV_AMD64_EMM3H = 231, + CV_AMD64_EMM4H = 232, + CV_AMD64_EMM5H = 233, + CV_AMD64_EMM6H = 234, + CV_AMD64_EMM7H = 235, + + // Do not change the order of these regs, first one must be even too + CV_AMD64_MM00 = 236, + CV_AMD64_MM01 = 237, + CV_AMD64_MM10 = 238, + CV_AMD64_MM11 = 239, + CV_AMD64_MM20 = 240, + CV_AMD64_MM21 = 241, + CV_AMD64_MM30 = 242, + CV_AMD64_MM31 = 243, + CV_AMD64_MM40 = 244, + CV_AMD64_MM41 = 245, + CV_AMD64_MM50 = 246, + CV_AMD64_MM51 = 247, + CV_AMD64_MM60 = 248, + CV_AMD64_MM61 = 249, + CV_AMD64_MM70 = 250, + CV_AMD64_MM71 = 251, + + // Extended KATMAI registers + CV_AMD64_XMM8 = 252, + CV_AMD64_XMM9 = 253, + CV_AMD64_XMM10 = 254, + CV_AMD64_XMM11 = 255, + CV_AMD64_XMM12 = 256, + CV_AMD64_XMM13 = 257, + CV_AMD64_XMM14 = 258, + CV_AMD64_XMM15 = 259, + + // KATMAI sub-registers + CV_AMD64_XMM8_0 = 260, + CV_AMD64_XMM8_1 = 261, + CV_AMD64_XMM8_2 = 262, + CV_AMD64_XMM8_3 = 263, + CV_AMD64_XMM9_0 = 264, + CV_AMD64_XMM9_1 = 265, + CV_AMD64_XMM9_2 = 266, + CV_AMD64_XMM9_3 = 267, + CV_AMD64_XMM10_0 = 268, + CV_AMD64_XMM10_1 = 269, + CV_AMD64_XMM10_2 = 270, + CV_AMD64_XMM10_3 = 271, + CV_AMD64_XMM11_0 = 272, + CV_AMD64_XMM11_1 = 273, + CV_AMD64_XMM11_2 = 274, + CV_AMD64_XMM11_3 = 275, + CV_AMD64_XMM12_0 = 276, + CV_AMD64_XMM12_1 = 277, + CV_AMD64_XMM12_2 = 278, + CV_AMD64_XMM12_3 = 279, + CV_AMD64_XMM13_0 = 280, + CV_AMD64_XMM13_1 = 281, + CV_AMD64_XMM13_2 = 282, + CV_AMD64_XMM13_3 = 283, + CV_AMD64_XMM14_0 = 284, + CV_AMD64_XMM14_1 = 285, + CV_AMD64_XMM14_2 = 286, + CV_AMD64_XMM14_3 = 287, + CV_AMD64_XMM15_0 = 288, + CV_AMD64_XMM15_1 = 289, + CV_AMD64_XMM15_2 = 290, + CV_AMD64_XMM15_3 = 291, + + CV_AMD64_XMM8L = 292, + CV_AMD64_XMM9L = 293, + CV_AMD64_XMM10L = 294, + CV_AMD64_XMM11L = 295, + CV_AMD64_XMM12L = 296, + CV_AMD64_XMM13L = 297, + CV_AMD64_XMM14L = 298, + CV_AMD64_XMM15L = 299, + + CV_AMD64_XMM8H = 300, + CV_AMD64_XMM9H = 301, + CV_AMD64_XMM10H = 302, + CV_AMD64_XMM11H = 303, + CV_AMD64_XMM12H = 304, + CV_AMD64_XMM13H = 305, + CV_AMD64_XMM14H = 306, + CV_AMD64_XMM15H = 307, + + // XMM sub-registers (WNI integer) + CV_AMD64_EMM8L = 308, + CV_AMD64_EMM9L = 309, + CV_AMD64_EMM10L = 310, + CV_AMD64_EMM11L = 311, + CV_AMD64_EMM12L = 312, + CV_AMD64_EMM13L = 313, + CV_AMD64_EMM14L = 314, + CV_AMD64_EMM15L = 315, + + CV_AMD64_EMM8H = 316, + CV_AMD64_EMM9H = 317, + CV_AMD64_EMM10H = 318, + CV_AMD64_EMM11H = 319, + CV_AMD64_EMM12H = 320, + CV_AMD64_EMM13H = 321, + CV_AMD64_EMM14H = 322, + CV_AMD64_EMM15H = 323, + + // Low byte forms of some standard registers + CV_AMD64_SIL = 324, + CV_AMD64_DIL = 325, + CV_AMD64_BPL = 326, + CV_AMD64_SPL = 327, + + // 64-bit regular registers + CV_AMD64_RAX = 328, + CV_AMD64_RBX = 329, + CV_AMD64_RCX = 330, + CV_AMD64_RDX = 331, + CV_AMD64_RSI = 332, + CV_AMD64_RDI = 333, + CV_AMD64_RBP = 334, + CV_AMD64_RSP = 335, + + // 64-bit integer registers with 8-, 16-, and 32-bit forms (B, W, and D) + CV_AMD64_R8 = 336, + CV_AMD64_R9 = 337, + CV_AMD64_R10 = 338, + CV_AMD64_R11 = 339, + CV_AMD64_R12 = 340, + CV_AMD64_R13 = 341, + CV_AMD64_R14 = 342, + CV_AMD64_R15 = 343, + + CV_AMD64_R8B = 344, + CV_AMD64_R9B = 345, + CV_AMD64_R10B = 346, + CV_AMD64_R11B = 347, + CV_AMD64_R12B = 348, + CV_AMD64_R13B = 349, + CV_AMD64_R14B = 350, + CV_AMD64_R15B = 351, + + CV_AMD64_R8W = 352, + CV_AMD64_R9W = 353, + CV_AMD64_R10W = 354, + CV_AMD64_R11W = 355, + CV_AMD64_R12W = 356, + CV_AMD64_R13W = 357, + CV_AMD64_R14W = 358, + CV_AMD64_R15W = 359, + + CV_AMD64_R8D = 360, + CV_AMD64_R9D = 361, + CV_AMD64_R10D = 362, + CV_AMD64_R11D = 363, + CV_AMD64_R12D = 364, + CV_AMD64_R13D = 365, + CV_AMD64_R14D = 366, + CV_AMD64_R15D = 367, + + // AVX registers 256 bits + CV_AMD64_YMM0 = 368, + CV_AMD64_YMM1 = 369, + CV_AMD64_YMM2 = 370, + CV_AMD64_YMM3 = 371, + CV_AMD64_YMM4 = 372, + CV_AMD64_YMM5 = 373, + CV_AMD64_YMM6 = 374, + CV_AMD64_YMM7 = 375, + CV_AMD64_YMM8 = 376, + CV_AMD64_YMM9 = 377, + CV_AMD64_YMM10 = 378, + CV_AMD64_YMM11 = 379, + CV_AMD64_YMM12 = 380, + CV_AMD64_YMM13 = 381, + CV_AMD64_YMM14 = 382, + CV_AMD64_YMM15 = 383, + + // AVX registers upper 128 bits + CV_AMD64_YMM0H = 384, + CV_AMD64_YMM1H = 385, + CV_AMD64_YMM2H = 386, + CV_AMD64_YMM3H = 387, + CV_AMD64_YMM4H = 388, + CV_AMD64_YMM5H = 389, + CV_AMD64_YMM6H = 390, + CV_AMD64_YMM7H = 391, + CV_AMD64_YMM8H = 392, + CV_AMD64_YMM9H = 393, + CV_AMD64_YMM10H = 394, + CV_AMD64_YMM11H = 395, + CV_AMD64_YMM12H = 396, + CV_AMD64_YMM13H = 397, + CV_AMD64_YMM14H = 398, + CV_AMD64_YMM15H = 399, + + // Lower/upper 8 bytes of XMM registers. Unlike CV_AMD64_XMM, these + // values reprsesent the bit patterns of the registers as 64-bit integers, not + // the representation of these registers as a double. + CV_AMD64_XMM0IL = 400, + CV_AMD64_XMM1IL = 401, + CV_AMD64_XMM2IL = 402, + CV_AMD64_XMM3IL = 403, + CV_AMD64_XMM4IL = 404, + CV_AMD64_XMM5IL = 405, + CV_AMD64_XMM6IL = 406, + CV_AMD64_XMM7IL = 407, + CV_AMD64_XMM8IL = 408, + CV_AMD64_XMM9IL = 409, + CV_AMD64_XMM10IL = 410, + CV_AMD64_XMM11IL = 411, + CV_AMD64_XMM12IL = 412, + CV_AMD64_XMM13IL = 413, + CV_AMD64_XMM14IL = 414, + CV_AMD64_XMM15IL = 415, + + CV_AMD64_XMM0IH = 416, + CV_AMD64_XMM1IH = 417, + CV_AMD64_XMM2IH = 418, + CV_AMD64_XMM3IH = 419, + CV_AMD64_XMM4IH = 420, + CV_AMD64_XMM5IH = 421, + CV_AMD64_XMM6IH = 422, + CV_AMD64_XMM7IH = 423, + CV_AMD64_XMM8IH = 424, + CV_AMD64_XMM9IH = 425, + CV_AMD64_XMM10IH = 426, + CV_AMD64_XMM11IH = 427, + CV_AMD64_XMM12IH = 428, + CV_AMD64_XMM13IH = 429, + CV_AMD64_XMM14IH = 430, + CV_AMD64_XMM15IH = 431, + + // AVX integer registers + CV_AMD64_YMM0I0 = 432, + CV_AMD64_YMM0I1 = 433, + CV_AMD64_YMM0I2 = 434, + CV_AMD64_YMM0I3 = 435, + CV_AMD64_YMM1I0 = 436, + CV_AMD64_YMM1I1 = 437, + CV_AMD64_YMM1I2 = 438, + CV_AMD64_YMM1I3 = 439, + CV_AMD64_YMM2I0 = 440, + CV_AMD64_YMM2I1 = 441, + CV_AMD64_YMM2I2 = 442, + CV_AMD64_YMM2I3 = 443, + CV_AMD64_YMM3I0 = 444, + CV_AMD64_YMM3I1 = 445, + CV_AMD64_YMM3I2 = 446, + CV_AMD64_YMM3I3 = 447, + CV_AMD64_YMM4I0 = 448, + CV_AMD64_YMM4I1 = 449, + CV_AMD64_YMM4I2 = 450, + CV_AMD64_YMM4I3 = 451, + CV_AMD64_YMM5I0 = 452, + CV_AMD64_YMM5I1 = 453, + CV_AMD64_YMM5I2 = 454, + CV_AMD64_YMM5I3 = 455, + CV_AMD64_YMM6I0 = 456, + CV_AMD64_YMM6I1 = 457, + CV_AMD64_YMM6I2 = 458, + CV_AMD64_YMM6I3 = 459, + CV_AMD64_YMM7I0 = 460, + CV_AMD64_YMM7I1 = 461, + CV_AMD64_YMM7I2 = 462, + CV_AMD64_YMM7I3 = 463, + CV_AMD64_YMM8I0 = 464, + CV_AMD64_YMM8I1 = 465, + CV_AMD64_YMM8I2 = 466, + CV_AMD64_YMM8I3 = 467, + CV_AMD64_YMM9I0 = 468, + CV_AMD64_YMM9I1 = 469, + CV_AMD64_YMM9I2 = 470, + CV_AMD64_YMM9I3 = 471, + CV_AMD64_YMM10I0 = 472, + CV_AMD64_YMM10I1 = 473, + CV_AMD64_YMM10I2 = 474, + CV_AMD64_YMM10I3 = 475, + CV_AMD64_YMM11I0 = 476, + CV_AMD64_YMM11I1 = 477, + CV_AMD64_YMM11I2 = 478, + CV_AMD64_YMM11I3 = 479, + CV_AMD64_YMM12I0 = 480, + CV_AMD64_YMM12I1 = 481, + CV_AMD64_YMM12I2 = 482, + CV_AMD64_YMM12I3 = 483, + CV_AMD64_YMM13I0 = 484, + CV_AMD64_YMM13I1 = 485, + CV_AMD64_YMM13I2 = 486, + CV_AMD64_YMM13I3 = 487, + CV_AMD64_YMM14I0 = 488, + CV_AMD64_YMM14I1 = 489, + CV_AMD64_YMM14I2 = 490, + CV_AMD64_YMM14I3 = 491, + CV_AMD64_YMM15I0 = 492, + CV_AMD64_YMM15I1 = 493, + CV_AMD64_YMM15I2 = 494, + CV_AMD64_YMM15I3 = 495, + + // AVX floating-point single precise registers + CV_AMD64_YMM0F0 = 496, + CV_AMD64_YMM0F1 = 497, + CV_AMD64_YMM0F2 = 498, + CV_AMD64_YMM0F3 = 499, + CV_AMD64_YMM0F4 = 500, + CV_AMD64_YMM0F5 = 501, + CV_AMD64_YMM0F6 = 502, + CV_AMD64_YMM0F7 = 503, + CV_AMD64_YMM1F0 = 504, + CV_AMD64_YMM1F1 = 505, + CV_AMD64_YMM1F2 = 506, + CV_AMD64_YMM1F3 = 507, + CV_AMD64_YMM1F4 = 508, + CV_AMD64_YMM1F5 = 509, + CV_AMD64_YMM1F6 = 510, + CV_AMD64_YMM1F7 = 511, + CV_AMD64_YMM2F0 = 512, + CV_AMD64_YMM2F1 = 513, + CV_AMD64_YMM2F2 = 514, + CV_AMD64_YMM2F3 = 515, + CV_AMD64_YMM2F4 = 516, + CV_AMD64_YMM2F5 = 517, + CV_AMD64_YMM2F6 = 518, + CV_AMD64_YMM2F7 = 519, + CV_AMD64_YMM3F0 = 520, + CV_AMD64_YMM3F1 = 521, + CV_AMD64_YMM3F2 = 522, + CV_AMD64_YMM3F3 = 523, + CV_AMD64_YMM3F4 = 524, + CV_AMD64_YMM3F5 = 525, + CV_AMD64_YMM3F6 = 526, + CV_AMD64_YMM3F7 = 527, + CV_AMD64_YMM4F0 = 528, + CV_AMD64_YMM4F1 = 529, + CV_AMD64_YMM4F2 = 530, + CV_AMD64_YMM4F3 = 531, + CV_AMD64_YMM4F4 = 532, + CV_AMD64_YMM4F5 = 533, + CV_AMD64_YMM4F6 = 534, + CV_AMD64_YMM4F7 = 535, + CV_AMD64_YMM5F0 = 536, + CV_AMD64_YMM5F1 = 537, + CV_AMD64_YMM5F2 = 538, + CV_AMD64_YMM5F3 = 539, + CV_AMD64_YMM5F4 = 540, + CV_AMD64_YMM5F5 = 541, + CV_AMD64_YMM5F6 = 542, + CV_AMD64_YMM5F7 = 543, + CV_AMD64_YMM6F0 = 544, + CV_AMD64_YMM6F1 = 545, + CV_AMD64_YMM6F2 = 546, + CV_AMD64_YMM6F3 = 547, + CV_AMD64_YMM6F4 = 548, + CV_AMD64_YMM6F5 = 549, + CV_AMD64_YMM6F6 = 550, + CV_AMD64_YMM6F7 = 551, + CV_AMD64_YMM7F0 = 552, + CV_AMD64_YMM7F1 = 553, + CV_AMD64_YMM7F2 = 554, + CV_AMD64_YMM7F3 = 555, + CV_AMD64_YMM7F4 = 556, + CV_AMD64_YMM7F5 = 557, + CV_AMD64_YMM7F6 = 558, + CV_AMD64_YMM7F7 = 559, + CV_AMD64_YMM8F0 = 560, + CV_AMD64_YMM8F1 = 561, + CV_AMD64_YMM8F2 = 562, + CV_AMD64_YMM8F3 = 563, + CV_AMD64_YMM8F4 = 564, + CV_AMD64_YMM8F5 = 565, + CV_AMD64_YMM8F6 = 566, + CV_AMD64_YMM8F7 = 567, + CV_AMD64_YMM9F0 = 568, + CV_AMD64_YMM9F1 = 569, + CV_AMD64_YMM9F2 = 570, + CV_AMD64_YMM9F3 = 571, + CV_AMD64_YMM9F4 = 572, + CV_AMD64_YMM9F5 = 573, + CV_AMD64_YMM9F6 = 574, + CV_AMD64_YMM9F7 = 575, + CV_AMD64_YMM10F0 = 576, + CV_AMD64_YMM10F1 = 577, + CV_AMD64_YMM10F2 = 578, + CV_AMD64_YMM10F3 = 579, + CV_AMD64_YMM10F4 = 580, + CV_AMD64_YMM10F5 = 581, + CV_AMD64_YMM10F6 = 582, + CV_AMD64_YMM10F7 = 583, + CV_AMD64_YMM11F0 = 584, + CV_AMD64_YMM11F1 = 585, + CV_AMD64_YMM11F2 = 586, + CV_AMD64_YMM11F3 = 587, + CV_AMD64_YMM11F4 = 588, + CV_AMD64_YMM11F5 = 589, + CV_AMD64_YMM11F6 = 590, + CV_AMD64_YMM11F7 = 591, + CV_AMD64_YMM12F0 = 592, + CV_AMD64_YMM12F1 = 593, + CV_AMD64_YMM12F2 = 594, + CV_AMD64_YMM12F3 = 595, + CV_AMD64_YMM12F4 = 596, + CV_AMD64_YMM12F5 = 597, + CV_AMD64_YMM12F6 = 598, + CV_AMD64_YMM12F7 = 599, + CV_AMD64_YMM13F0 = 600, + CV_AMD64_YMM13F1 = 601, + CV_AMD64_YMM13F2 = 602, + CV_AMD64_YMM13F3 = 603, + CV_AMD64_YMM13F4 = 604, + CV_AMD64_YMM13F5 = 605, + CV_AMD64_YMM13F6 = 606, + CV_AMD64_YMM13F7 = 607, + CV_AMD64_YMM14F0 = 608, + CV_AMD64_YMM14F1 = 609, + CV_AMD64_YMM14F2 = 610, + CV_AMD64_YMM14F3 = 611, + CV_AMD64_YMM14F4 = 612, + CV_AMD64_YMM14F5 = 613, + CV_AMD64_YMM14F6 = 614, + CV_AMD64_YMM14F7 = 615, + CV_AMD64_YMM15F0 = 616, + CV_AMD64_YMM15F1 = 617, + CV_AMD64_YMM15F2 = 618, + CV_AMD64_YMM15F3 = 619, + CV_AMD64_YMM15F4 = 620, + CV_AMD64_YMM15F5 = 621, + CV_AMD64_YMM15F6 = 622, + CV_AMD64_YMM15F7 = 623, + + // AVX floating-point double precise registers + CV_AMD64_YMM0D0 = 624, + CV_AMD64_YMM0D1 = 625, + CV_AMD64_YMM0D2 = 626, + CV_AMD64_YMM0D3 = 627, + CV_AMD64_YMM1D0 = 628, + CV_AMD64_YMM1D1 = 629, + CV_AMD64_YMM1D2 = 630, + CV_AMD64_YMM1D3 = 631, + CV_AMD64_YMM2D0 = 632, + CV_AMD64_YMM2D1 = 633, + CV_AMD64_YMM2D2 = 634, + CV_AMD64_YMM2D3 = 635, + CV_AMD64_YMM3D0 = 636, + CV_AMD64_YMM3D1 = 637, + CV_AMD64_YMM3D2 = 638, + CV_AMD64_YMM3D3 = 639, + CV_AMD64_YMM4D0 = 640, + CV_AMD64_YMM4D1 = 641, + CV_AMD64_YMM4D2 = 642, + CV_AMD64_YMM4D3 = 643, + CV_AMD64_YMM5D0 = 644, + CV_AMD64_YMM5D1 = 645, + CV_AMD64_YMM5D2 = 646, + CV_AMD64_YMM5D3 = 647, + CV_AMD64_YMM6D0 = 648, + CV_AMD64_YMM6D1 = 649, + CV_AMD64_YMM6D2 = 650, + CV_AMD64_YMM6D3 = 651, + CV_AMD64_YMM7D0 = 652, + CV_AMD64_YMM7D1 = 653, + CV_AMD64_YMM7D2 = 654, + CV_AMD64_YMM7D3 = 655, + CV_AMD64_YMM8D0 = 656, + CV_AMD64_YMM8D1 = 657, + CV_AMD64_YMM8D2 = 658, + CV_AMD64_YMM8D3 = 659, + CV_AMD64_YMM9D0 = 660, + CV_AMD64_YMM9D1 = 661, + CV_AMD64_YMM9D2 = 662, + CV_AMD64_YMM9D3 = 663, + CV_AMD64_YMM10D0 = 664, + CV_AMD64_YMM10D1 = 665, + CV_AMD64_YMM10D2 = 666, + CV_AMD64_YMM10D3 = 667, + CV_AMD64_YMM11D0 = 668, + CV_AMD64_YMM11D1 = 669, + CV_AMD64_YMM11D2 = 670, + CV_AMD64_YMM11D3 = 671, + CV_AMD64_YMM12D0 = 672, + CV_AMD64_YMM12D1 = 673, + CV_AMD64_YMM12D2 = 674, + CV_AMD64_YMM12D3 = 675, + CV_AMD64_YMM13D0 = 676, + CV_AMD64_YMM13D1 = 677, + CV_AMD64_YMM13D2 = 678, + CV_AMD64_YMM13D3 = 679, + CV_AMD64_YMM14D0 = 680, + CV_AMD64_YMM14D1 = 681, + CV_AMD64_YMM14D2 = 682, + CV_AMD64_YMM14D3 = 683, + CV_AMD64_YMM15D0 = 684, + CV_AMD64_YMM15D1 = 685, + CV_AMD64_YMM15D2 = 686, + CV_AMD64_YMM15D3 = 687, + } + + // Matches CV_access_e + public enum CodeViewMemberAccess : byte + { + CV_private = 1, + CV_protected = 2, + CV_public = 3, + } + + // Matches CV_methodprop_e + public enum CodeViewMethodKind : byte + { + CV_MTvanilla = 0, + CV_MTvirtual = 1, + CV_MTstatic = 2, + CV_MTfriend = 3, + CV_MTintro = 4, + CV_MTpurevirt = 5, + CV_MTpureintro = 6, + } + + public enum CodeViewSymbolDefinition : ushort + { + S_COMPILE = 0x0001, // Compile flags symbol + S_REGISTER_16t = 0x0002, // Register variable + S_CONSTANT_16t = 0x0003, // constant symbol + S_UDT_16t = 0x0004, // User defined type + S_SSEARCH = 0x0005, // Start Search + S_END = 0x0006, // Block, procedure, "with" or thunk end + S_SKIP = 0x0007, // Reserve symbol space in $$Symbols table + S_CVRESERVE = 0x0008, // Reserved symbol for CV internal use + S_OBJNAME_ST = 0x0009, // path to object file name + S_ENDARG = 0x000A, // end of argument/return list + S_COBOLUDT_16t = 0x000B, // special UDT for cobol that does not symbol pack + S_MANYREG_16t = 0x000C, // multiple register variable + S_RETURN = 0x000D, // return description symbol + S_ENTRYTHIS = 0x000E, // description of this pointer on entry + + S_BPREL16 = 0x0100, // BP-relative + S_LDATA16 = 0x0101, // Module-local symbol + S_GDATA16 = 0x0102, // Global data symbol + S_PUB16 = 0x0103, // a public symbol + S_LPROC16 = 0x0104, // Local procedure start + S_GPROC16 = 0x0105, // Global procedure start + S_THUNK16 = 0x0106, // Thunk Start + S_BLOCK16 = 0x0107, // block start + S_WITH16 = 0x0108, // with start + S_LABEL16 = 0x0109, // code label + S_CEXMODEL16 = 0x010A, // change execution model + S_VFTABLE16 = 0x010B, // address of virtual function table + S_REGREL16 = 0x010C, // register relative address + + S_BPREL32_16t = 0x0200, // BP-relative + S_LDATA32_16t = 0x0201, // Module-local symbol + S_GDATA32_16t = 0x0202, // Global data symbol + S_PUB32_16t = 0x0203, // a public symbol (CV internal reserved) + S_LPROC32_16t = 0x0204, // Local procedure start + S_GPROC32_16t = 0x0205, // Global procedure start + S_THUNK32_ST = 0x0206, // Thunk Start + S_BLOCK32_ST = 0x0207, // block start + S_WITH32_ST = 0x0208, // with start + S_LABEL32_ST = 0x0209, // code label + S_CEXMODEL32 = 0x020A, // change execution model + S_VFTABLE32_16t = 0x020B, // address of virtual function table + S_REGREL32_16t = 0x020C, // register relative address + S_LTHREAD32_16t = 0x020D, // local thread storage + S_GTHREAD32_16t = 0x020E, // global thread storage + S_SLINK32 = 0x020F, // static link for MIPS EH implementation + + S_LPROCMIPS_16t = 0x0300, // Local procedure start + S_GPROCMIPS_16t = 0x0301, // Global procedure start + + // if these ref symbols have names following then the names are in ST format + S_PROCREF_ST = 0x0400, // Reference to a procedure + S_DATAREF_ST = 0x0401, // Reference to data + S_ALIGN = 0x0402, // Used for page alignment of symbols + + S_LPROCREF_ST = 0x0403, // Local Reference to a procedure + S_OEM = 0x0404, // OEM defined symbol + + // sym records with 32-bit types embedded instead of 16-bit + // all have 0x1000 bit set for easy identification + // only do the 32-bit target versions since we don't really + // care about 16-bit ones anymore. + S_TI16_MAX = 0x1000, + + S_REGISTER_ST = 0x1001, // Register variable + S_CONSTANT_ST = 0x1002, // constant symbol + S_UDT_ST = 0x1003, // User defined type + S_COBOLUDT_ST = 0x1004, // special UDT for cobol that does not symbol pack + S_MANYREG_ST = 0x1005, // multiple register variable + S_BPREL32_ST = 0x1006, // BP-relative + S_LDATA32_ST = 0x1007, // Module-local symbol + S_GDATA32_ST = 0x1008, // Global data symbol + S_PUB32_ST = 0x1009, // a public symbol (CV internal reserved) + S_LPROC32_ST = 0x100A, // Local procedure start + S_GPROC32_ST = 0x100B, // Global procedure start + S_VFTABLE32 = 0x100C, // address of virtual function table + S_REGREL32_ST = 0x100D, // register relative address + S_LTHREAD32_ST = 0x100E, // local thread storage + S_GTHREAD32_ST = 0x100F, // global thread storage + + S_LPROCMIPS_ST = 0x1010, // Local procedure start + S_GPROCMIPS_ST = 0x1011, // Global procedure start + + S_FRAMEPROC = 0x1012, // extra frame and proc information + S_COMPILE2_ST = 0x1013, // extended compile flags and info + + // New symbols necessary for 16-bit enumerates of IA64 registers + // and IA64 specific symbols + S_MANYREG2_ST = 0x1014, // multiple register variable + S_LPROCIA64_ST = 0x1015, // Local procedure start (IA64) + S_GPROCIA64_ST = 0x1016, // Global procedure start (IA64) + + // Local symbols for IL + S_LOCALSLOT_ST = 0x1017, // local IL sym with field for local slot index + S_PARAMSLOT_ST = 0x1018, // local IL sym with field for parameter slot index + + S_ANNOTATION = 0x1019, // Annotation string literals + + // Symbols to support managed code debugging + S_GMANPROC_ST = 0x101A, // Global proc + S_LMANPROC_ST = 0x101B, // Local proc + S_RESERVED1 = 0x101C, // reserved + S_RESERVED2 = 0x101D, // reserved + S_RESERVED3 = 0x101E, // reserved + S_RESERVED4 = 0x101F, // reserved + S_LMANDATA_ST = 0x1020, + S_GMANDATA_ST = 0x1021, + S_MANFRAMEREL_ST = 0x1022, + S_MANREGISTER_ST = 0x1023, + S_MANSLOT_ST = 0x1024, + S_MANMANYREG_ST = 0x1025, + S_MANREGREL_ST = 0x1026, + S_MANMANYREG2_ST = 0x1027, + S_MANTYPREF = 0x1028, // Index for type referenced by name from metadata + S_UNAMESPACE_ST = 0x1029, // Using namespace + + // Symbols w/ SZ name fields. All name fields contain utf8 encoded strings. + S_ST_MAX = 0x1100, // starting point for SZ name symbols + + S_OBJNAME = 0x1101, // path to object file name + S_THUNK32 = 0x1102, // Thunk Start + S_BLOCK32 = 0x1103, // block start + S_WITH32 = 0x1104, // with start + S_LABEL32 = 0x1105, // code label + S_REGISTER = 0x1106, // Register variable + S_CONSTANT = 0x1107, // constant symbol + S_UDT = 0x1108, // User defined type + S_COBOLUDT = 0x1109, // special UDT for cobol that does not symbol pack + S_MANYREG = 0x110A, // multiple register variable + S_BPREL32 = 0x110B, // BP-relative + S_LDATA32 = 0x110C, // Module-local symbol + S_GDATA32 = 0x110D, // Global data symbol + S_PUB32 = 0x110E, // a public symbol (CV internal reserved) + S_LPROC32 = 0x110F, // Local procedure start + S_GPROC32 = 0x1110, // Global procedure start + S_REGREL32 = 0x1111, // register relative address + S_LTHREAD32 = 0x1112, // local thread storage + S_GTHREAD32 = 0x1113, // global thread storage + + S_LPROCMIPS = 0x1114, // Local procedure start + S_GPROCMIPS = 0x1115, // Global procedure start + S_COMPILE2 = 0x1116, // extended compile flags and info + S_MANYREG2 = 0x1117, // multiple register variable + S_LPROCIA64 = 0x1118, // Local procedure start (IA64) + S_GPROCIA64 = 0x1119, // Global procedure start (IA64) + S_LOCALSLOT = 0x111A, // local IL sym with field for local slot index + S_SLOT = S_LOCALSLOT, // alias for LOCALSLOT + S_PARAMSLOT = 0x111B, // local IL sym with field for parameter slot index + + // symbols to support managed code debugging + S_LMANDATA = 0x111C, + S_GMANDATA = 0x111D, + S_MANFRAMEREL = 0x111E, + S_MANREGISTER = 0x111F, + S_MANSLOT = 0x1120, + S_MANMANYREG = 0x1121, + S_MANREGREL = 0x1122, + S_MANMANYREG2 = 0x1123, + S_UNAMESPACE = 0x1124, // Using namespace + + // ref symbols with name fields + S_PROCREF = 0x1125, // Reference to a procedure + S_DATAREF = 0x1126, // Reference to data + S_LPROCREF = 0x1127, // Local Reference to a procedure + S_ANNOTATIONREF = 0x1128, // Reference to an S_ANNOTATION symbol + S_TOKENREF = 0x1129, // Reference to one of the many MANPROCSYM's + + // continuation of managed symbols + S_GMANPROC = 0x112A, // Global proc + S_LMANPROC = 0x112B, // Local proc + + // short, light-weight thunks + S_TRAMPOLINE = 0x112C, // trampoline thunks + S_MANCONSTANT = 0x112D, // constants with metadata type info + + // native attributed local/parms + S_ATTR_FRAMEREL = 0x112E, // relative to virtual frame ptr + S_ATTR_REGISTER = 0x112F, // stored in a register + S_ATTR_REGREL = 0x1130, // relative to register (alternate frame ptr) + S_ATTR_MANYREG = 0x1131, // stored in >1 register + + // Separated code (from the compiler) support + S_SEPCODE = 0x1132, + + S_LOCAL_2005 = 0x1133, // defines a local symbol in optimized code + S_DEFRANGE_2005 = 0x1134, // defines a single range of addresses in which symbol can be evaluated + S_DEFRANGE2_2005 = 0x1135, // defines ranges of addresses in which symbol can be evaluated + + S_SECTION = 0x1136, // A COFF section in a PE executable + S_COFFGROUP = 0x1137, // A COFF group + S_EXPORT = 0x1138, // A export + + S_CALLSITEINFO = 0x1139, // Indirect call site information + S_FRAMECOOKIE = 0x113A, // Security cookie information + + S_DISCARDED = 0x113B, // Discarded by LINK /OPT:REF (experimental, see richards) + + S_COMPILE3 = 0x113C, // Replacement for S_COMPILE2 + S_ENVBLOCK = 0x113D, // Environment block split off from S_COMPILE2 + + S_LOCAL = 0x113E, // defines a local symbol in optimized code + S_DEFRANGE = 0x113F, // defines a single range of addresses in which symbol can be evaluated + S_DEFRANGE_SUBFIELD = 0x1140, // ranges for a subfield + + S_DEFRANGE_REGISTER = 0x1141, // ranges for en-registered symbol + S_DEFRANGE_FRAMEPOINTER_REL = 0x1142, // range for stack symbol. + S_DEFRANGE_SUBFIELD_REGISTER = 0x1143, // ranges for en-registered field of symbol + S_DEFRANGE_FRAMEPOINTER_REL_FULL_SCOPE = 0x1144, // range for stack symbol span valid full scope of function body, gap might apply. + S_DEFRANGE_REGISTER_REL = 0x1145, // range for symbol address as register + offset. + + // S_PROC symbols that reference ID instead of type + S_LPROC32_ID = 0x1146, + S_GPROC32_ID = 0x1147, + S_LPROCMIPS_ID = 0x1148, + S_GPROCMIPS_ID = 0x1149, + S_LPROCIA64_ID = 0x114A, + S_GPROCIA64_ID = 0x114B, + + S_BUILDINFO = 0x114C, // build information. + S_INLINESITE = 0x114D, // inlined function callsite. + S_INLINESITE_END = 0x114E, + S_PROC_ID_END = 0x114F, + + S_DEFRANGE_HLSL = 0x1150, + S_GDATA_HLSL = 0x1151, + S_LDATA_HLSL = 0x1152, + + S_FILESTATIC = 0x1153, + + S_ARMSWITCHTABLE = 0x1159, + S_CALLEES = 0x115A, + S_CALLERS = 0x115B, + S_POGODATA = 0x115C, + S_INLINESITE2 = 0x115D, // extended inline site information + + S_HEAPALLOCSITE = 0x115E, // heap allocation site + + S_MOD_TYPEREF = 0x115F, // only generated at link time + + S_REF_MINIPDB = 0x1160, // only generated at link time for mini PDB + S_PDBMAP = 0x1161, // only generated at link time for mini PDB + + S_GDATA_HLSL32 = 0x1162, + S_LDATA_HLSL32 = 0x1163, + + S_GDATA_HLSL32_EX = 0x1164, + S_LDATA_HLSL32_EX = 0x1165, + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/CodeView/CodeViewSymbolsBuilder.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/CodeView/CodeViewSymbolsBuilder.cs new file mode 100644 index 0000000000000..e8faabae51939 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/CodeView/CodeViewSymbolsBuilder.cs @@ -0,0 +1,434 @@ +// 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.Text; + +using ILCompiler.DependencyAnalysis; +using Internal.JitInterface; +using Internal.TypeSystem; +using Internal.TypeSystem.TypesDebugInfo; + +using static ILCompiler.ObjectWriter.CodeViewNative; +using static ILCompiler.ObjectWriter.CodeViewNative.CodeViewRegister; +using static ILCompiler.ObjectWriter.CodeViewNative.CodeViewSymbolDefinition; + +namespace ILCompiler.ObjectWriter +{ + /// Builder for the CodeView .debug$S section. + /// + /// The .debug$S section in CodeView contains information about methods, + /// their parameters, stack layout, and line mapping. + /// + /// The section is divided into logical chunks known as subsections for + /// different kind of information (eg. Symbols, Lines). Unlike the type + /// section () this section does need + /// relocations. + /// + /// The builder emits the subsections linearly into the section writer as + /// the records are produced. Similarly to MSVC we output separate symbol + /// subsection and line subsection for each method. + /// + /// File table (and related string table) are constructed through and appended at the very end of + /// the section. + /// + internal sealed class CodeViewSymbolsBuilder + { + private readonly TargetArchitecture _targetArchitecture; + private readonly SectionWriter _sectionWriter; + + public CodeViewSymbolsBuilder(TargetArchitecture targetArchitecture, SectionWriter sectionWriter) + { + _targetArchitecture = targetArchitecture; + _sectionWriter = sectionWriter; + + // Write CodeView version header + Span versionBuffer = stackalloc byte[sizeof(uint)]; + BinaryPrimitives.WriteUInt32LittleEndian(versionBuffer, 4); + sectionWriter.Write(versionBuffer); + } + + // Maps an ICorDebugInfo register number to the corresponding CodeView + // register number + private CodeViewRegister GetCVRegNum(uint regNum) + { + switch (_targetArchitecture) + { + case TargetArchitecture.X86: + return regNum switch + { + 0u => CV_REG_EAX, + 1u => CV_REG_ECX, + 2u => CV_REG_EDX, + 3u => CV_REG_EBX, + 4u => CV_REG_ESP, + 5u => CV_REG_EBP, + 6u => CV_REG_ESI, + 7u => CV_REG_EDI, + // TODO: Floating point + _ => CV_REG_NONE, + }; + + case TargetArchitecture.X64: + return regNum switch + { + 0u => CV_AMD64_RAX, + 1u => CV_AMD64_RCX, + 2u => CV_AMD64_RDX, + 3u => CV_AMD64_RBX, + 4u => CV_AMD64_RSP, + 5u => CV_AMD64_RBP, + 6u => CV_AMD64_RSI, + 7u => CV_AMD64_RDI, + 8u => CV_AMD64_R8, + 9u => CV_AMD64_R9, + 10u => CV_AMD64_R10, + 11u => CV_AMD64_R11, + 12u => CV_AMD64_R12, + 13u => CV_AMD64_R13, + 14u => CV_AMD64_R14, + 15u => CV_AMD64_R15, + // TODO: Floating point + _ => CV_REG_NONE, + }; + + case TargetArchitecture.ARM64: + // X0-X28, FP, LR, SP have same order + if (regNum <= 32) + return (CodeViewRegister)(regNum + (uint)CV_ARM64_X0); + // TODO: Floating point + return CV_REG_NONE; + + default: + return CV_REG_NONE; + } + } + + public void EmitSubprogramInfo( + string methodName, + int methodPCLength, + uint methodTypeIndex, + IEnumerable<(DebugVarInfoMetadata, uint)> debugVars, + IEnumerable debugEHClauseInfos) + { + using var symbolSubsection = GetSubsection(DebugSymbolsSubsectionType.Symbols); + + // TODO: Do we need those? + _ = methodTypeIndex; + _ = debugEHClauseInfos; + + using (var recordWriter = symbolSubsection.StartRecord(S_GPROC32_ID)) + { + recordWriter.Write((uint)0); // pointer to the parent + recordWriter.Write((uint)0); // pointer to this blocks end + recordWriter.Write((uint)0); // pointer to next symbol + recordWriter.Write((uint)methodPCLength); + recordWriter.Write((uint)0); // Debug start offset + recordWriter.Write((uint)methodPCLength); // Debug end offset + recordWriter.Write((uint)0); // Type index or ID + recordWriter.EmitSymbolReference(RelocType.IMAGE_REL_SECREL, methodName); + recordWriter.EmitSymbolReference(RelocType.IMAGE_REL_SECTION, methodName); + recordWriter.Write((byte)0); // Proc flags + recordWriter.Write(methodName); + } + + foreach (var (debugVar, typeIndex) in debugVars) + { + using (var recordWriter = symbolSubsection.StartRecord(S_LOCAL)) + { + recordWriter.Write(typeIndex); + recordWriter.Write((ushort)(debugVar.IsParameter ? 1 : 0)); // TODO: Flags + recordWriter.Write(debugVar.Name); // TODO: Names (this, etc.) + } + + foreach (var range in debugVar.DebugVarInfo.Ranges) + { + uint rangeLength = range.EndOffset - range.StartOffset; + + // Limit the range length to the maximum range expressible in CodeView. + // If this proves to be a problem we can emit additional records to + // describe the continued range of the variable. + if (rangeLength > 0xF000) + { + rangeLength = 0xF000; + } + + switch (range.VarLoc.LocationType) + { + case VarLocType.VLT_REG: + case VarLocType.VLT_REG_FP: + CodeViewRegister cvRegNum = GetCVRegNum((uint)range.VarLoc.B); + if (cvRegNum != CV_REG_NONE) + { + using (var recordWriter = symbolSubsection.StartRecord(S_DEFRANGE_REGISTER)) + { + recordWriter.Write((ushort)cvRegNum); + recordWriter.Write((ushort)0); // TODO: Attributes + recordWriter.EmitSymbolReference(RelocType.IMAGE_REL_SECREL, methodName, (int)range.StartOffset); + recordWriter.EmitSymbolReference(RelocType.IMAGE_REL_SECTION, methodName); + recordWriter.Write((ushort)rangeLength); + } + } + break; + + case VarLocType.VLT_STK: + // FIXME: Handle REGNUM_AMBIENT_SP + cvRegNum = GetCVRegNum((uint)range.VarLoc.B); + if (cvRegNum != CV_REG_NONE) + { + using (var recordWriter = symbolSubsection.StartRecord(S_DEFRANGE_REGISTER_REL)) + { + recordWriter.Write((ushort)cvRegNum); + // TODO: Flags, CV_OFFSET_PARENT_LENGTH_LIMIT + recordWriter.Write((ushort)0); + recordWriter.Write((uint)range.VarLoc.C); + recordWriter.EmitSymbolReference(RelocType.IMAGE_REL_SECREL, methodName, (int)range.StartOffset); + recordWriter.EmitSymbolReference(RelocType.IMAGE_REL_SECTION, methodName); + recordWriter.Write((ushort)rangeLength); + } + } + break; + + case VarLocType.VLT_REG_BYREF: + case VarLocType.VLT_STK_BYREF: + case VarLocType.VLT_REG_REG: + case VarLocType.VLT_REG_STK: + case VarLocType.VLT_STK_REG: + case VarLocType.VLT_STK2: + case VarLocType.VLT_FPSTK: + case VarLocType.VLT_FIXED_VA: + break; + + default: + Debug.Fail("Unknown variable location type"); + break; + } + } + } + + using (var recordWriter = symbolSubsection.StartRecord(S_PROC_ID_END)) + { + } + } + + public void EmitLineInfo( + CodeViewFileTableBuilder fileTableBuilder, + string methodName, + int methodPCLength, + IEnumerable sequencePoints) + { + using var lineSubsection = GetSubsection(DebugSymbolsSubsectionType.Lines); + + using (var recordWriter = lineSubsection.StartRecord()) + { + recordWriter.EmitSymbolReference(RelocType.IMAGE_REL_SECREL, methodName); + recordWriter.EmitSymbolReference(RelocType.IMAGE_REL_SECTION, methodName); + recordWriter.Write((ushort)0); // TODO: Flags (eg. have columns) + recordWriter.Write((uint)methodPCLength); + + string lastFileName = null; + uint fileIndex = 0; + List codes = new(); + + foreach (var sequencePoint in sequencePoints) + { + if (lastFileName is null || lastFileName != sequencePoint.FileName) + { + if (codes.Count > 0) + { + recordWriter.Write(fileIndex); + // Number of code pairs (ie. offset + sequence code) + recordWriter.Write((uint)(codes.Count / 2)); + // Record size including this header + recordWriter.Write((uint)(3 * sizeof(uint) + codes.Count * sizeof(uint))); + foreach (uint code in codes) + { + recordWriter.Write((uint)code); + } + codes.Clear(); + } + + fileIndex = fileTableBuilder.GetFileIndex(sequencePoint.FileName); + lastFileName = sequencePoint.FileName; + } + + codes.Add((uint)sequencePoint.NativeOffset); + codes.Add(0x80000000 | (uint)sequencePoint.LineNumber); + } + + if (codes.Count > 0) + { + recordWriter.Write(fileIndex); + recordWriter.Write((uint)(codes.Count / 2)); + recordWriter.Write((uint)(12 + 4 * codes.Count)); + foreach (uint code in codes) + { + recordWriter.Write((uint)code); + } + } + } + } + + public void WriteUserDefinedTypes(IList<(string, uint)> userDefinedTypes) + { + using var symbolSubsection = GetSubsection(DebugSymbolsSubsectionType.Symbols); + foreach (var (name, typeIndex) in userDefinedTypes) + { + using (var recordWriter = symbolSubsection.StartRecord(S_UDT)) + { + recordWriter.Write(typeIndex); + recordWriter.Write(name); + } + } + } + + private SubsectionWriter GetSubsection(DebugSymbolsSubsectionType subsectionKind) + { + return new SubsectionWriter(subsectionKind, _sectionWriter); + } + + private sealed class SubsectionWriter : IDisposable + { + private readonly DebugSymbolsSubsectionType _kind; + private readonly SectionWriter _sectionWriter; + internal uint _size; + internal readonly List _data = new(); + internal readonly List<(uint, RelocType, string)> _relocations = new(); + + public SubsectionWriter(DebugSymbolsSubsectionType kind, SectionWriter sectionWriter) + { + _kind = kind; + _sectionWriter = sectionWriter; + } + + public void Dispose() + { + Span subsectionHeader = stackalloc byte[sizeof(uint) + sizeof(uint)]; + BinaryPrimitives.WriteUInt32LittleEndian(subsectionHeader, (uint)_kind); + BinaryPrimitives.WriteUInt32LittleEndian(subsectionHeader.Slice(4), _size); + _sectionWriter.Write(subsectionHeader); + + foreach (var (offset, relocType, symbolName) in _relocations) + { + _sectionWriter.EmitRelocation( + (int)offset, + default, // NOTE: We know the data are unused for the relocation types used in debug section + relocType, + symbolName, + 0); + } + + foreach (byte[] data in _data) + { + _sectionWriter.Write(data); + } + + _sectionWriter.EmitAlignment(4); + } + + public RecordWriter StartRecord() + { + return new RecordWriter(this, false); + } + + public RecordWriter StartRecord(CodeViewSymbolDefinition recordType) + { + RecordWriter writer = new RecordWriter(this, true); + writer.Write((ushort)recordType); + return writer; + } + } + + private ref struct RecordWriter + { + private readonly SubsectionWriter _subsectionWriter; + private readonly ArrayBufferWriter _bufferWriter; + private readonly bool _hasLengthPrefix; + + public RecordWriter(SubsectionWriter subsectionWriter, bool hasLengthPrefix) + { + _subsectionWriter = subsectionWriter; + _bufferWriter = new(); + _hasLengthPrefix = hasLengthPrefix; + } + + public void Dispose() + { + if (_hasLengthPrefix) + { + byte[] lengthBuffer = new byte[sizeof(ushort)]; + BinaryPrimitives.WriteUInt16LittleEndian(lengthBuffer, (ushort)(_bufferWriter.WrittenCount)); + _subsectionWriter._data.Add(lengthBuffer); + _subsectionWriter._size += sizeof(ushort); + } + + // Add data + _subsectionWriter._data.Add(_bufferWriter.WrittenSpan.ToArray()); + _subsectionWriter._size += (uint)_bufferWriter.WrittenCount; + + _bufferWriter.Clear(); + } + + public void Write(byte value) + { + _bufferWriter.GetSpan(1)[0] = value; + _bufferWriter.Advance(1); + } + + public void Write(ushort value) + { + BinaryPrimitives.WriteUInt16LittleEndian(_bufferWriter.GetSpan(sizeof(ushort)), value); + _bufferWriter.Advance(sizeof(ushort)); + } + + public void Write(uint value) + { + BinaryPrimitives.WriteUInt32LittleEndian(_bufferWriter.GetSpan(sizeof(uint)), value); + _bufferWriter.Advance(sizeof(uint)); + } + + public void Write(ulong value) + { + BinaryPrimitives.WriteUInt64LittleEndian(_bufferWriter.GetSpan(sizeof(ulong)), value); + _bufferWriter.Advance(sizeof(ulong)); + } + + public void Write(string value) + { + int byteCount = Encoding.UTF8.GetByteCount(value) + 1; + Encoding.UTF8.GetBytes(value, _bufferWriter.GetSpan(byteCount)); + _bufferWriter.Advance(byteCount); + } + + public void EmitSymbolReference( + RelocType relocType, + string symbolName, + int addend = 0) + { + _subsectionWriter._relocations.Add(( + _subsectionWriter._size + + (uint)(_hasLengthPrefix ? sizeof(ushort) : 0) + + (uint)_bufferWriter.WrittenCount, + relocType, symbolName)); + + switch (relocType) + { + case RelocType.IMAGE_REL_SECTION: + Write((ushort)0); + break; + case RelocType.IMAGE_REL_SECREL: + Write((uint)addend); + break; + default: + throw new NotSupportedException("Unsupported relocation"); + } + } + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/CodeView/CodeViewTypesBuilder.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/CodeView/CodeViewTypesBuilder.cs new file mode 100644 index 0000000000000..c5aa7f8b4ee2b --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/CodeView/CodeViewTypesBuilder.cs @@ -0,0 +1,533 @@ +// 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.Text; + +using ILCompiler.DependencyAnalysis; +using Internal.JitInterface; +using Internal.TypeSystem; +using Internal.TypeSystem.TypesDebugInfo; + +using static ILCompiler.ObjectWriter.CodeViewNative; +using static ILCompiler.ObjectWriter.CodeViewNative.CodeViewPointer; +using static ILCompiler.ObjectWriter.CodeViewNative.CodeViewPropertyFlags; +using static ILCompiler.ObjectWriter.CodeViewNative.CodeViewType; +using static ILCompiler.ObjectWriter.CodeViewNative.LeafRecordType; + +namespace ILCompiler.ObjectWriter +{ + /// Builder for the CodeView .debug$T section. + /// + /// The .debug$T section in CodeView contains type (enum, struct, class) + /// descriptions. The section is composed of records that are prefixed + /// with their type and length. Each record is assigned a type index + /// representing its position in the stream. The type indexes start at + /// 0x1000 and increase by one for each record. Elementary types, such + /// as uint, short, or float, have preassigned indexes below 0x1000, + /// represented by the enumeration. + /// + /// The maximum record size is limited to sizeof(ushort) due to the + /// layout of the record header. List records (eg. field list) can + /// be split into multiple records that are chained together. + /// + internal sealed class CodeViewTypesBuilder : ITypesDebugInfoWriter + { + private readonly NameMangler _nameMangler; + private readonly SectionWriter _sectionWriter; + private readonly int _targetPointerSize; + + private readonly uint _classVTableTypeIndex; + private readonly uint _vfuncTabTypeIndex; + private readonly List<(string, uint)> _userDefinedTypes = new(); + + private uint _nextTypeIndex = 0x1000; + + public IList<(string, uint)> UserDefinedTypes => _userDefinedTypes; + + public CodeViewTypesBuilder(NameMangler nameMangler, int targetPointerSize, SectionWriter sectionWriter) + { + _nameMangler = nameMangler; + _sectionWriter = sectionWriter; + _targetPointerSize = targetPointerSize; + + // Write CodeView version header + Span versionBuffer = stackalloc byte[sizeof(uint)]; + BinaryPrimitives.WriteUInt32LittleEndian(versionBuffer, 4); + _sectionWriter.Write(versionBuffer); + + // We pretend that the MethodTable pointer in System.Object is VTable shape. + // We use the same "Vtable" for all types because the vtable shape debug + // record is not expressive enough to capture our vtable shape (where the + // vtable slots don't start at the beginning of the vtable). + using (LeafRecordWriter record = StartLeafRecord(LF_VTSHAPE)) + { + record.Write((ushort)0); // Number of entries in vfunctable + } + _classVTableTypeIndex = _nextTypeIndex++; + + using (LeafRecordWriter record = StartLeafRecord(LF_POINTER)) + { + record.Write(_classVTableTypeIndex); + record.Write((uint)((targetPointerSize == 8 ? CV_PTR_64 : CV_PTR_NEAR32) | CV_PTR_MODE_LVREF)); + } + _vfuncTabTypeIndex = _nextTypeIndex++; + } + + public uint GetPrimitiveTypeIndex(TypeDesc type) + { + Debug.Assert(type.IsPrimitive, "it is not a primitive type"); + return GetPrimitiveTypeIndex(type.Category); + } + + private uint GetPrimitiveTypeIndex(TypeFlags typeFlags) + { + // CodeView uses predefined codes for simple types + return (uint)(typeFlags switch + { + TypeFlags.Boolean => T_BOOL08, + TypeFlags.Char => T_WCHAR, + TypeFlags.SByte => T_INT1, + TypeFlags.Byte => T_UINT1, + TypeFlags.Int16 => T_INT2, + TypeFlags.UInt16 => T_UINT2, + TypeFlags.Int32 => T_INT4, + TypeFlags.UInt32 => T_UINT4, + TypeFlags.Int64 => T_INT8, + TypeFlags.UInt64 => T_UINT8, + TypeFlags.IntPtr => _targetPointerSize == 8 ? T_INT8 : T_INT4, + TypeFlags.UIntPtr => _targetPointerSize == 8 ? T_UINT8 : T_UINT4, + TypeFlags.Single => T_REAL32, + TypeFlags.Double => T_REAL64, + _ => T_NOTYPE, + }); + } + + public uint GetPointerTypeIndex(PointerTypeDescriptor pointerDescriptor) + { + using (LeafRecordWriter record = StartLeafRecord(LF_POINTER)) + { + record.Write(pointerDescriptor.ElementType); + record.Write((uint)( + (pointerDescriptor.Is64Bit == 1 ? CV_PTR_64 : CV_PTR_NEAR32) | + (pointerDescriptor.IsReference == 1 ? CV_PTR_MODE_LVREF : CV_PTR_MODE_PTR) | + (pointerDescriptor.IsConst == 1 ? CV_PTR_IS_CONST : 0))); + } + + return _nextTypeIndex++; + } + + public uint GetArrayTypeIndex( + ClassTypeDescriptor classDescriptor, + ArrayTypeDescriptor arrayDescriptor) + { + uint memberCount = 0; + uint offset = 0; + + using (LeafRecordWriter arrayRecord = StartLeafRecord(LF_ARRAY)) + { + arrayRecord.Write(arrayDescriptor.ElementType); + arrayRecord.Write(T_INT4); + arrayRecord.Write(arrayDescriptor.Size); + arrayRecord.Write(""); + } + + uint arrayRecordTypeIndex = _nextTypeIndex++; + + using (LeafRecordWriter fieldListRecord = StartLeafRecord(LF_FIELDLIST)) + { + if (classDescriptor.BaseClassId != 0) + { + fieldListRecord.StartListEntry(LF_BCLASS); + fieldListRecord.Write((ushort)0); // Attributes + fieldListRecord.Write(classDescriptor.BaseClassId); + fieldListRecord.WriteEncodedInteger(0); // Offset + fieldListRecord.EndListEntry(); + memberCount++; + offset += (uint)_targetPointerSize; + } + + fieldListRecord.StartListEntry(LF_MEMBER); + fieldListRecord.Write((ushort)CodeViewMemberAccess.CV_public); + fieldListRecord.Write(T_INT4); + fieldListRecord.WriteEncodedInteger(offset); + fieldListRecord.Write("count"); + fieldListRecord.EndListEntry(); + memberCount++; + offset += (uint)_targetPointerSize; + + if (arrayDescriptor.IsMultiDimensional == 1) + { + for (uint i = 0; i < arrayDescriptor.Rank; ++i) + { + fieldListRecord.StartListEntry(LF_MEMBER); + fieldListRecord.Write((ushort)CodeViewMemberAccess.CV_public); + fieldListRecord.Write(T_INT4); + fieldListRecord.WriteEncodedInteger(offset); + fieldListRecord.Write($"length{i}"); + fieldListRecord.EndListEntry(); + memberCount++; + offset += 4; + } + + for (uint i = 0; i < arrayDescriptor.Rank; ++i) + { + fieldListRecord.StartListEntry(LF_MEMBER); + fieldListRecord.Write((ushort)CodeViewMemberAccess.CV_public); + fieldListRecord.Write(T_INT4); + fieldListRecord.WriteEncodedInteger(offset); + fieldListRecord.Write($"bounds{i}"); + fieldListRecord.EndListEntry(); + memberCount++; + offset += 4; + } + } + + fieldListRecord.StartListEntry(LF_MEMBER); + fieldListRecord.Write((ushort)CodeViewMemberAccess.CV_public); + fieldListRecord.Write(arrayRecordTypeIndex); + fieldListRecord.WriteEncodedInteger(offset); + fieldListRecord.Write("values"); + fieldListRecord.EndListEntry(); + memberCount++; + } + + uint fieldListTypeIndex = _nextTypeIndex++; + + Debug.Assert(classDescriptor.IsStruct == 0); + using (LeafRecordWriter record = StartLeafRecord(LF_CLASS)) + { + Debug.Assert(memberCount <= ushort.MaxValue); + Debug.Assert(arrayDescriptor.Size <= ushort.MaxValue); + record.Write((ushort)memberCount); // Number of elements in class + record.Write((ushort)0); // Class options (CodeViewPropertyFlags) + record.Write(fieldListTypeIndex); // Field descriptor index + record.Write((uint)0); // Derived-from descriptor index + record.Write((uint)0); // Vtshape descriptor index + record.Write((ushort)arrayDescriptor.Size); // Size + record.Write(classDescriptor.Name); + } + + uint typeIndex = _nextTypeIndex++; + _userDefinedTypes.Add((classDescriptor.Name, typeIndex)); + + return typeIndex; + } + + public uint GetEnumTypeIndex( + EnumTypeDescriptor typeDescriptor, + EnumRecordTypeDescriptor[] typeRecords) + { + using (LeafRecordWriter fieldListRecord = StartLeafRecord(LF_FIELDLIST)) + { + foreach (EnumRecordTypeDescriptor record in typeRecords) + { + fieldListRecord.StartListEntry(LF_ENUMERATE); + fieldListRecord.Write((ushort)CodeViewMemberAccess.CV_public); + fieldListRecord.WriteEncodedInteger(record.Value); + fieldListRecord.Write(record.Name); + fieldListRecord.EndListEntry(); + } + } + + uint fieldListTypeIndex = _nextTypeIndex++; + + using (LeafRecordWriter record = StartLeafRecord(LF_ENUM)) + { + Debug.Assert(typeRecords.Length <= ushort.MaxValue); + record.Write((ushort)typeRecords.Length); // Number of elements in class + record.Write((ushort)0); // Class options (CodeViewPropertyFlags) + record.Write(typeDescriptor.ElementType); + record.Write(fieldListTypeIndex); + record.Write(typeDescriptor.Name); + } + + uint typeIndex = _nextTypeIndex++; + _userDefinedTypes.Add((typeDescriptor.Name, typeIndex)); + + return typeIndex; + } + + public uint GetClassTypeIndex(ClassTypeDescriptor classDescriptor) + { + using (LeafRecordWriter record = StartLeafRecord(classDescriptor.IsStruct == 1 ? LF_STRUCTURE : LF_CLASS)) + { + record.Write((ushort)0); // Number of elements in class + record.Write((ushort)CV_PROP_FORWARD_REFERENCE); // Class options (CodeViewPropertyFlags) + record.Write((uint)0); // Field descriptor index + record.Write((uint)0); // Derived-from descriptor index + record.Write((uint)0); // Vtshape descriptor index + record.Write((ushort)0); // Size + record.Write(classDescriptor.Name); + } + + return _nextTypeIndex++; + } + + public uint GetCompleteClassTypeIndex( + ClassTypeDescriptor classTypeDescriptor, + ClassFieldsTypeDescriptor classFieldsTypeDescriptor, + DataFieldDescriptor[] fields, + StaticDataFieldDescriptor[] statics) + { + uint memberCount = 0; + + using (LeafRecordWriter fieldListRecord = StartLeafRecord(LF_FIELDLIST)) + { + if (classTypeDescriptor.BaseClassId != 0) + { + fieldListRecord.StartListEntry(LF_BCLASS); + fieldListRecord.Write((ushort)0); // Attributes + fieldListRecord.Write(classTypeDescriptor.BaseClassId); + fieldListRecord.WriteEncodedInteger(0); // Offset + fieldListRecord.EndListEntry(); + memberCount++; + } + else if (classTypeDescriptor.IsStruct == 0) + { + fieldListRecord.StartListEntry(LF_VFUNCTAB); + fieldListRecord.Write((ushort)0); // Padding + fieldListRecord.Write(_vfuncTabTypeIndex); + fieldListRecord.EndListEntry(); + memberCount++; + } + + foreach (DataFieldDescriptor desc in fields) + { + if (desc.Offset == 0xFFFFFFFF) + { + fieldListRecord.StartListEntry(LF_STMEMBER); + fieldListRecord.Write((ushort)CodeViewMemberAccess.CV_public); + fieldListRecord.Write(desc.FieldTypeIndex); + fieldListRecord.Write(desc.Name); + fieldListRecord.EndListEntry(); + } + else + { + fieldListRecord.StartListEntry(LF_MEMBER); + fieldListRecord.Write((ushort)CodeViewMemberAccess.CV_public); + fieldListRecord.Write(desc.FieldTypeIndex); + fieldListRecord.WriteEncodedInteger(desc.Offset); + fieldListRecord.Write(desc.Name); + fieldListRecord.EndListEntry(); + } + memberCount++; + } + } + + uint fieldListTypeIndex = _nextTypeIndex++; + + using (LeafRecordWriter record = StartLeafRecord(classTypeDescriptor.IsStruct == 1 ? LF_STRUCTURE : LF_CLASS)) + { + Debug.Assert(memberCount <= ushort.MaxValue); + record.Write((ushort)memberCount); // Number of elements in class + record.Write((ushort)0); // Class options (CodeViewPropertyFlags) + record.Write(fieldListTypeIndex); // Field descriptor index + record.Write((uint)0); // Derived-from descriptor index + record.Write((uint)0); // Vtshape descriptor index + record.WriteEncodedInteger(classFieldsTypeDescriptor.Size); // Size + record.Write(classTypeDescriptor.Name); + } + + uint typeIndex = _nextTypeIndex++; + _userDefinedTypes.Add((classTypeDescriptor.Name, typeIndex)); + + return typeIndex; + } + + public uint GetMemberFunctionTypeIndex(MemberFunctionTypeDescriptor memberDescriptor, uint[] argumentTypes) + { + using (LeafRecordWriter fieldListRecord = StartLeafRecord(LF_ARGLIST)) + { + fieldListRecord.Write((uint)argumentTypes.Length); + foreach (uint argumentType in argumentTypes) + { + fieldListRecord.Write(argumentType); + } + } + + uint argumentListTypeIndex = _nextTypeIndex++; + + using (LeafRecordWriter record = StartLeafRecord(LF_MFUNCTION)) + { + Debug.Assert(memberDescriptor.NumberOfArguments <= ushort.MaxValue); + record.Write(memberDescriptor.ReturnType); + record.Write(memberDescriptor.ContainingClass); + record.Write(memberDescriptor.TypeIndexOfThisPointer); + record.Write((byte)memberDescriptor.CallingConvention); + // TODO: Evaluate if we should mark constructors + record.Write((byte)0); // Function options (CV_funcattr_t) + record.Write((ushort)memberDescriptor.NumberOfArguments); + record.Write(argumentListTypeIndex); + record.Write((uint)memberDescriptor.ThisAdjust); + } + + return _nextTypeIndex++; + } + + public uint GetMemberFunctionId(MemberFunctionIdTypeDescriptor memberIdDescriptor) + { + using (LeafRecordWriter record = StartLeafRecord(LF_MFUNC_ID)) + { + record.Write(memberIdDescriptor.ParentClass); + record.Write(memberIdDescriptor.MemberFunction); + record.Write(memberIdDescriptor.Name); + } + + return _nextTypeIndex++; + } + + public string GetMangledName(TypeDesc type) + { + return _nameMangler.GetMangledTypeName(type); + } + + private LeafRecordWriter StartLeafRecord(LeafRecordType leafRecordType) + { + return new LeafRecordWriter(this, leafRecordType); + } + + private ref struct LeafRecordWriter + { + private CodeViewTypesBuilder _debugTypesBuilder; + private LeafRecordType _leafRecordType; + private ArrayBufferWriter _bufferWriter; + private int _lastListMemberStart; + + public LeafRecordWriter(CodeViewTypesBuilder debugTypesBuilder, LeafRecordType leafRecordType) + { + _debugTypesBuilder = debugTypesBuilder; + _leafRecordType = leafRecordType; + _bufferWriter = new(); + Write((ushort)_leafRecordType); + } + + public void Dispose() + { + int length = sizeof(ushort) + _bufferWriter.WrittenCount; + int padding = ((length + 3) & ~3) - length; + Debug.Assert(length <= ushort.MaxValue); + Span lengthBuffer = stackalloc byte[sizeof(ushort)]; + BinaryPrimitives.WriteUInt16LittleEndian(lengthBuffer, (ushort)(length + padding - sizeof(ushort))); + _debugTypesBuilder._sectionWriter.Write(lengthBuffer); + _debugTypesBuilder._sectionWriter.Write(_bufferWriter.WrittenSpan); + _debugTypesBuilder._sectionWriter.WritePadding(padding); + _bufferWriter.Clear(); + } + + public void Write(byte value) + { + _bufferWriter.GetSpan(1)[0] = value; + _bufferWriter.Advance(1); + } + + public void Write(ushort value) + { + BinaryPrimitives.WriteUInt16LittleEndian(_bufferWriter.GetSpan(sizeof(ushort)), value); + _bufferWriter.Advance(sizeof(ushort)); + } + + public void Write(uint value) + { + BinaryPrimitives.WriteUInt32LittleEndian(_bufferWriter.GetSpan(sizeof(uint)), value); + _bufferWriter.Advance(sizeof(uint)); + } + + public void Write(ulong value) + { + BinaryPrimitives.WriteUInt64LittleEndian(_bufferWriter.GetSpan(sizeof(ulong)), value); + _bufferWriter.Advance(sizeof(ulong)); + } + + public void Write(string value) + { + int byteCount = Encoding.UTF8.GetByteCount(value) + 1; + Encoding.UTF8.GetBytes(value, _bufferWriter.GetSpan(byteCount)); + _bufferWriter.Advance(byteCount); + } + + public void WriteEncodedInteger(ulong value) + { + if (value < (ushort)LF_NUMERIC) + { + Write((ushort)value); + } + else if (value <= ushort.MaxValue) + { + Write((ushort)LF_USHORT); + Write((ushort)value); + } + else if (value <= uint.MaxValue) + { + Write((ushort)LF_ULONG); + Write((uint)value); + } + else + { + Write((ushort)LF_UQUADWORD); + Write(value); + } + } + + public void Write(CodeViewType value) => Write((uint)value); + + private void WritePadding() + { + int paddingLength = ((_bufferWriter.WrittenCount - 2 + 3) & ~3) - (_bufferWriter.WrittenCount - 2); + Span padding = _bufferWriter.GetSpan(paddingLength); + for (int i = 0; i < paddingLength; i++) + { + padding[i] = (byte)(LF_PAD0 + paddingLength - i); + } + _bufferWriter.Advance(paddingLength); + } + + public void StartListEntry(LeafRecordType recordType) + { + Debug.Assert(_leafRecordType == LF_FIELDLIST || _leafRecordType == LF_METHODLIST); + + _lastListMemberStart = _bufferWriter.WrittenCount; + Write((ushort)recordType); + } + + public void EndListEntry() + { + Debug.Assert(_leafRecordType == LF_FIELDLIST || _leafRecordType == LF_METHODLIST); + Debug.Assert(_lastListMemberStart > 0); + + WritePadding(); + + // If the current list record overflows the maximum list length then emit a + // LF_FIELDLIST/LF_METHODLIST leaf record now and start a new one. The new one + // has LF_INDEX as the first element to chain the lists together. + if (_bufferWriter.WrittenCount > short.MaxValue - sizeof(ushort)) + { + // At least one record was already written in the current list. + Debug.Assert(_lastListMemberStart > sizeof(ushort)); + + // Flush the current record up to _lastListMemberStart and write LF_INDEX to reference it. + int length = sizeof(ushort) + _lastListMemberStart; + int padding = ((length + 3) & ~3) - length; + _debugTypesBuilder._sectionWriter.WriteLittleEndian((ushort)(length + padding - sizeof(ushort))); + _debugTypesBuilder._sectionWriter.Write(_bufferWriter.WrittenSpan.Slice(0, _lastListMemberStart)); + _debugTypesBuilder._sectionWriter.WritePadding(padding); + byte[] overflow = _bufferWriter.WrittenSpan.Slice(_lastListMemberStart).ToArray(); + _bufferWriter.Clear(); + + Write((ushort)_leafRecordType); + Write((ushort)LF_INDEX); + Write((ushort)0); // Padding + Write((uint)_debugTypesBuilder._nextTypeIndex++); + overflow.CopyTo(_bufferWriter.GetSpan(overflow.Length)); + _bufferWriter.Advance(overflow.Length); + _lastListMemberStart = _bufferWriter.WrittenCount; + } + } + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/CoffObjectWriter.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/CoffObjectWriter.cs new file mode 100644 index 0000000000000..1f18dfc83d33d --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/CoffObjectWriter.cs @@ -0,0 +1,1097 @@ +// 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.TypeSystem; +using Internal.TypeSystem.TypesDebugInfo; +using static ILCompiler.DependencyAnalysis.RelocType; +using static ILCompiler.ObjectWriter.CoffObjectWriter.CoffRelocationType; + +namespace ILCompiler.ObjectWriter +{ + /// + /// COFF object file format writer for Windows targets. + /// + /// + /// The PE/COFF object format is described in the official specifciation at + /// https://learn.microsoft.com/en-us/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. + /// + internal sealed class CoffObjectWriter : ObjectWriter + { + private sealed record SectionDefinition(CoffSectionHeader Header, Stream Stream, List Relocations, string ComdatName, string SymbolName); + + private readonly Machine _machine; + private readonly List _sections = new(); + + // Symbol table + private readonly List _symbols = new(); + private readonly Dictionary _symbolNameToIndex = new(StringComparer.Ordinal); + private readonly Dictionary _sectionNumberToComdatAuxRecord = new(); + private readonly HashSet _referencedMethods = new(); + + // Exception handling + private SectionWriter _xdataSectionWriter; + private SectionWriter _pdataSectionWriter; + + // Debugging + private SectionWriter _debugTypesSectionWriter; + private SectionWriter _debugSymbolSectionWriter; + private CodeViewFileTableBuilder _debugFileTableBuilder; + private CodeViewSymbolsBuilder _debugSymbolsBuilder; + private CodeViewTypesBuilder _debugTypesBuilder; + + private static readonly ObjectNodeSection PDataSection = new ObjectNodeSection("pdata", SectionType.ReadOnly); + private static readonly ObjectNodeSection GfidsSection = new ObjectNodeSection(".gfids$y", SectionType.ReadOnly); + private static readonly ObjectNodeSection DebugTypesSection = new ObjectNodeSection(".debug$T", SectionType.ReadOnly); + private static readonly ObjectNodeSection DebugSymbolSection = new ObjectNodeSection(".debug$S", SectionType.ReadOnly); + + public CoffObjectWriter(NodeFactory factory, ObjectWritingOptions options) + : base(factory, options) + { + _machine = factory.Target.Architecture switch + { + TargetArchitecture.X86 => Machine.I386, + TargetArchitecture.X64 => Machine.Amd64, + TargetArchitecture.ARM64 => Machine.Arm64, + _ => throw new NotSupportedException("Unsupported architecture") + }; + } + + private protected override void CreateSection(ObjectNodeSection section, string comdatName, string symbolName, Stream sectionStream) + { + var sectionHeader = new CoffSectionHeader + { + Name = + section == ObjectNodeSection.TLSSection ? ".tls$" : + section == ObjectNodeSection.HydrationTargetSection ? "hydrated" : + (section.Name.StartsWith(".") ? section.Name : "." + section.Name), + SectionCharacteristics = section.Type switch + { + SectionType.ReadOnly => + SectionCharacteristics.MemRead | SectionCharacteristics.ContainsInitializedData, + SectionType.Writeable => + SectionCharacteristics.MemRead | SectionCharacteristics.MemWrite | + SectionCharacteristics.ContainsInitializedData, + SectionType.Executable => + SectionCharacteristics.MemRead | SectionCharacteristics.MemExecute | + SectionCharacteristics.ContainsCode, + SectionType.Uninitialized => + SectionCharacteristics.MemRead | SectionCharacteristics.MemWrite | + SectionCharacteristics.ContainsUninitializedData, + _ => 0 + } + }; + + if (section == DebugTypesSection) + { + sectionHeader.SectionCharacteristics = + SectionCharacteristics.MemRead | SectionCharacteristics.ContainsInitializedData | + SectionCharacteristics.MemDiscardable; + } + + if (comdatName is not null) + { + sectionHeader.SectionCharacteristics |= SectionCharacteristics.LinkerComdat; + + // We find the defining section of the COMDAT symbol. That one is marked + // as "ANY" selection type. All the other ones are marked as associated. + bool isPrimary = Equals(comdatName, symbolName); + uint sectionIndex = (uint)_sections.Count + 1u; + uint definingSectionIndex = isPrimary ? sectionIndex : ((CoffSymbol)_symbols[(int)_symbolNameToIndex[comdatName]]).SectionIndex; + + var auxRecord = new CoffSectionSymbol + { + // SizeOfRawData, NumberOfRelocations, NumberOfLineNumbers + // CheckSum will be filled later in EmitObjectFile + + Number = definingSectionIndex, + Selection = isPrimary ? + CoffComdatSelect.IMAGE_COMDAT_SELECT_ANY : + CoffComdatSelect.IMAGE_COMDAT_SELECT_ASSOCIATIVE, + }; + + _sectionNumberToComdatAuxRecord[_sections.Count] = auxRecord; + _symbols.Add(new CoffSymbol + { + Name = sectionHeader.Name, + Value = 0, + SectionIndex = sectionIndex, + StorageClass = CoffSymbolClass.IMAGE_SYM_CLASS_STATIC, + NumberOfAuxiliaryRecords = 1, + }); + _symbols.Add(auxRecord); + + if (symbolName is not null) + { + _symbolNameToIndex.Add(symbolName, (uint)_symbols.Count); + _symbols.Add(new CoffSymbol + { + Name = symbolName, + Value = 0, + SectionIndex = sectionIndex, + StorageClass = isPrimary ? CoffSymbolClass.IMAGE_SYM_CLASS_EXTERNAL : CoffSymbolClass.IMAGE_SYM_CLASS_STATIC, + }); + } + } + + _sections.Add(new SectionDefinition(sectionHeader, sectionStream, new List(), comdatName, symbolName)); + } + + protected internal override void UpdateSectionAlignment(int sectionIndex, int alignment) + { + Debug.Assert(alignment > 0 && BitOperations.IsPow2((uint)alignment)); + int minimumAlignment = (BitOperations.Log2((uint)alignment) + 1) << 20; + int currentAlignment = (int)(_sections[sectionIndex].Header.SectionCharacteristics & SectionCharacteristics.AlignMask); + + if (currentAlignment < minimumAlignment) + { + _sections[sectionIndex].Header.SectionCharacteristics = + (_sections[sectionIndex].Header.SectionCharacteristics & ~SectionCharacteristics.AlignMask) | + (SectionCharacteristics)minimumAlignment; + } + } + + protected internal override unsafe void EmitRelocation( + int sectionIndex, + long offset, + Span data, + RelocType relocType, + string symbolName, + long addend) + { + if (relocType is IMAGE_REL_BASED_RELPTR32) + { + addend += 4; + } + + if (addend != 0) + { + fixed (byte *pData = data) + { + long inlineValue = Relocation.ReadValue(relocType, (void*)pData); + Relocation.WriteValue(relocType, (void*)pData, inlineValue + addend); + } + } + + base.EmitRelocation(sectionIndex, offset, data, relocType, symbolName, 0); + } + + private protected override void EmitReferencedMethod(string symbolName) + { + _referencedMethods.Add(symbolName); + } + + private protected override void EmitSymbolTable( + IDictionary definedSymbols, + SortedSet undefinedSymbols) + { + foreach (var (symbolName, symbolDefinition) in definedSymbols) + { + if (_symbolNameToIndex.TryGetValue(symbolName, out uint symbolIndex)) + { + // Update value for COMDAT symbols + ((CoffSymbol)_symbols[(int)symbolIndex]).Value = (uint)symbolDefinition.Value; + } + else + { + _symbolNameToIndex.Add(symbolName, (uint)_symbols.Count); + _symbols.Add(new CoffSymbol + { + Name = symbolName, + Value = (uint)symbolDefinition.Value, + SectionIndex = (uint)(1 + symbolDefinition.SectionIndex), + StorageClass = CoffSymbolClass.IMAGE_SYM_CLASS_EXTERNAL, + }); + } + } + + foreach (var symbolName in undefinedSymbols) + { + _symbolNameToIndex.Add(symbolName, (uint)_symbols.Count); + _symbols.Add(new CoffSymbol + { + Name = symbolName, + StorageClass = CoffSymbolClass.IMAGE_SYM_CLASS_EXTERNAL, + }); + } + + if (_options.HasFlag(ObjectWritingOptions.ControlFlowGuard)) + { + // Create section with control flow guard symbols + SectionWriter gfidsSectionWriter = GetOrCreateSection(GfidsSection); + + foreach (var symbolName in _referencedMethods) + { + gfidsSectionWriter.WriteLittleEndian(_symbolNameToIndex[symbolName]); + } + + // Emit the feat.00 symbol that controls various linker behaviors + _symbols.Add(new CoffSymbol + { + Name = "@feat.00", + StorageClass = CoffSymbolClass.IMAGE_SYM_CLASS_STATIC, + SectionIndex = uint.MaxValue, // IMAGE_SYM_ABSOLUTE + Value = 0x800, // cfGuardCF flags this object as control flow guard aware + }); + } + } + + private protected override void EmitRelocations(int sectionIndex, List relocationList) + { + CoffSectionHeader sectionHeader = _sections[sectionIndex].Header; + List coffRelocations = _sections[sectionIndex].Relocations; + if (relocationList.Count > 0) + { + if (relocationList.Count <= ushort.MaxValue) + { + sectionHeader.NumberOfRelocations = (ushort)relocationList.Count; + } + else + { + // Write an overflow relocation with the real count of relocations + sectionHeader.NumberOfRelocations = ushort.MaxValue; + sectionHeader.SectionCharacteristics |= SectionCharacteristics.LinkerNRelocOvfl; + coffRelocations.Add(new CoffRelocation { VirtualAddress = (uint)(relocationList.Count + 1) }); + } + + switch (_machine) + { + case Machine.I386: + foreach (var relocation in relocationList) + { + coffRelocations.Add(new CoffRelocation + { + VirtualAddress = (uint)relocation.Offset, + SymbolTableIndex = _symbolNameToIndex[relocation.SymbolName], + Type = relocation.Type switch + { + IMAGE_REL_BASED_ABSOLUTE => IMAGE_REL_I386_DIR32NB, + IMAGE_REL_BASED_ADDR32NB => IMAGE_REL_I386_DIR32NB, + IMAGE_REL_BASED_HIGHLOW => IMAGE_REL_I386_DIR32, + IMAGE_REL_BASED_REL32 => IMAGE_REL_I386_REL32, + IMAGE_REL_BASED_RELPTR32 => IMAGE_REL_I386_REL32, + IMAGE_REL_SECREL => IMAGE_REL_I386_SECREL, + IMAGE_REL_SECTION => IMAGE_REL_I386_SECTION, + _ => throw new NotSupportedException($"Unsupported relocation: {relocation.Type}") + }, + }); + } + break; + + case Machine.Amd64: + foreach (var relocation in relocationList) + { + coffRelocations.Add(new CoffRelocation + { + VirtualAddress = (uint)relocation.Offset, + SymbolTableIndex = _symbolNameToIndex[relocation.SymbolName], + Type = relocation.Type switch + { + IMAGE_REL_BASED_ABSOLUTE => IMAGE_REL_AMD64_ADDR32NB, + IMAGE_REL_BASED_ADDR32NB => IMAGE_REL_AMD64_ADDR32NB, + IMAGE_REL_BASED_HIGHLOW => IMAGE_REL_AMD64_ADDR32, + IMAGE_REL_BASED_DIR64 => IMAGE_REL_AMD64_ADDR64, + IMAGE_REL_BASED_REL32 => IMAGE_REL_AMD64_REL32, + IMAGE_REL_BASED_RELPTR32 => IMAGE_REL_AMD64_REL32, + IMAGE_REL_SECREL => IMAGE_REL_AMD64_SECREL, + IMAGE_REL_SECTION => IMAGE_REL_AMD64_SECTION, + _ => throw new NotSupportedException($"Unsupported relocation: {relocation.Type}") + }, + }); + } + break; + + case Machine.Arm64: + foreach (var relocation in relocationList) + { + coffRelocations.Add(new CoffRelocation + { + VirtualAddress = (uint)relocation.Offset, + SymbolTableIndex = _symbolNameToIndex[relocation.SymbolName], + Type = relocation.Type switch + { + IMAGE_REL_BASED_ABSOLUTE => IMAGE_REL_ARM64_ADDR32NB, + IMAGE_REL_BASED_ADDR32NB => IMAGE_REL_ARM64_ADDR32NB, + IMAGE_REL_BASED_HIGHLOW => IMAGE_REL_ARM64_ADDR32, + IMAGE_REL_BASED_DIR64 => IMAGE_REL_ARM64_ADDR64, + IMAGE_REL_BASED_REL32 => IMAGE_REL_ARM64_REL32, + IMAGE_REL_BASED_RELPTR32 => IMAGE_REL_ARM64_REL32, + IMAGE_REL_BASED_ARM64_BRANCH26 => IMAGE_REL_ARM64_BRANCH26, + IMAGE_REL_BASED_ARM64_PAGEBASE_REL21 => IMAGE_REL_ARM64_PAGEBASE_REL21, + IMAGE_REL_BASED_ARM64_PAGEOFFSET_12A => IMAGE_REL_ARM64_PAGEOFFSET_12A, + IMAGE_REL_SECREL => IMAGE_REL_ARM64_SECREL, + IMAGE_REL_SECTION => IMAGE_REL_ARM64_SECTION, + _ => throw new NotSupportedException($"Unsupported relocation: {relocation.Type}") + }, + }); + } + break; + + default: + throw new NotSupportedException("Unsupported architecture"); + } + } + } + + private protected override void EmitUnwindInfo( + SectionWriter sectionWriter, + INodeWithCodeInfo nodeWithCodeInfo, + string currentSymbolName) + { + if (nodeWithCodeInfo.FrameInfos is FrameInfo[] frameInfos && + nodeWithCodeInfo is ISymbolDefinitionNode) + { + SectionWriter xdataSectionWriter; + SectionWriter pdataSectionWriter; + bool shareSymbol = ShouldShareSymbol((ObjectNode)nodeWithCodeInfo); + + for (int i = 0; i < frameInfos.Length; i++) + { + FrameInfo frameInfo = frameInfos[i]; + + int start = frameInfo.StartOffset; + int end = frameInfo.EndOffset; + byte[] blob = frameInfo.BlobData; + + string unwindSymbolName = $"_unwind{i}{currentSymbolName}"; + + if (shareSymbol) + { + // Ideally we would use `currentSymbolName` here and produce an + // associative COMDAT symbol but link.exe cannot always handle that + // and produces errors about duplicate symbols that point into the + // associative section, so we are stuck with one section per each + // unwind symbol. + xdataSectionWriter = GetOrCreateSection(ObjectNodeSection.XDataSection, currentSymbolName, unwindSymbolName); + pdataSectionWriter = GetOrCreateSection(PDataSection, currentSymbolName, null); + } + else + { + xdataSectionWriter = _xdataSectionWriter; + 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 x64 has the End symbol + if (_machine == Machine.Amd64) + { + pdataSectionWriter.EmitSymbolReference(IMAGE_REL_BASED_ADDR32NB, currentSymbolName, end); + } + // Unwind info pointer + pdataSectionWriter.EmitSymbolReference(IMAGE_REL_BASED_ADDR32NB, unwindSymbolName, 0); + } + } + } + + private protected override void EmitObjectFile(string objectFilePath) + { + using var outputFileStream = new FileStream(objectFilePath, FileMode.Create); + var stringTable = new CoffStringTable(); + var coffHeader = new CoffHeader + { + Machine = _machine, + NumberOfSections = (uint)_sections.Count, + NumberOfSymbols = (uint)_symbols.Count, + }; + + // Calculate size of section data and assign offsets + uint dataOffset = (uint)(coffHeader.Size + _sections.Count * CoffSectionHeader.Size); + int sectionIndex = 0; + foreach (SectionDefinition section in _sections) + { + section.Header.SizeOfRawData = (uint)section.Stream.Length; + + // Section content + if (section.Header.SectionCharacteristics.HasFlag(SectionCharacteristics.ContainsUninitializedData)) + { + section.Header.PointerToRawData = 0; + } + else + { + section.Header.PointerToRawData = dataOffset; + dataOffset += section.Header.SizeOfRawData; + } + + // Section relocations + section.Header.PointerToRelocations = section.Relocations.Count > 0 ? dataOffset : 0; + dataOffset += (uint)(section.Relocations.Count * CoffRelocation.Size); + + sectionIndex++; + } + + coffHeader.PointerToSymbolTable = dataOffset; + + // Write COFF header + coffHeader.Write(outputFileStream); + + // Write COFF section headers + sectionIndex = 0; + foreach (SectionDefinition section in _sections) + { + section.Header.Write(outputFileStream, stringTable); + + // Relocation code below assumes that addresses are 0-indexed + Debug.Assert(section.Header.VirtualAddress == 0); + + // Update COMDAT section symbol + if (_sectionNumberToComdatAuxRecord.TryGetValue(sectionIndex, out var auxRecord)) + { + auxRecord.SizeOfRawData = section.Header.SizeOfRawData; + auxRecord.NumberOfRelocations = section.Header.NumberOfRelocations; + auxRecord.NumberOfLineNumbers = section.Header.NumberOfLineNumbers; + + section.Stream.Position = 0; + auxRecord.CheckSum = JamCrc32.CalculateChecksum(section.Stream); + } + + sectionIndex++; + } + + // Writer section content and relocations + foreach (SectionDefinition section in _sections) + { + if (!section.Header.SectionCharacteristics.HasFlag(SectionCharacteristics.ContainsUninitializedData)) + { + Debug.Assert(outputFileStream.Position == section.Header.PointerToRawData); + section.Stream.Position = 0; + section.Stream.CopyTo(outputFileStream); + } + + if (section.Relocations.Count > 0) + { + foreach (var relocation in section.Relocations) + { + relocation.Write(outputFileStream); + } + } + } + + // Optimize the string table + foreach (var coffSymbolRecord in _symbols) + { + if (coffSymbolRecord is CoffSymbol coffSymbol) + { + stringTable.ReserveString(coffSymbol.Name); + } + } + + // Write symbol table + Debug.Assert(outputFileStream.Position == coffHeader.PointerToSymbolTable); + foreach (var coffSymbolRecord in _symbols) + { + coffSymbolRecord.Write(outputFileStream, stringTable, coffHeader.IsBigObj); + } + + // Write string table + stringTable.Write(outputFileStream); + } + + private protected override void CreateEhSections() + { + // Create .xdata and .pdata + _xdataSectionWriter = GetOrCreateSection(ObjectNodeSection.XDataSection); + _pdataSectionWriter = GetOrCreateSection(PDataSection); + } + + 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, + string 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, null); + 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()); + + if (hasSequencePoints) + { + debugSymbolsBuilder.EmitLineInfo( + _debugFileTableBuilder, + methodName, + methodSymbol.Size, + debugNode.GetNativeSequencePoints()); + } + } + + private protected override void EmitDebugSections(IDictionary definedSymbols) + { + _debugSymbolsBuilder.WriteUserDefinedTypes(_debugTypesBuilder.UserDefinedTypes); + _debugFileTableBuilder.Write(_debugSymbolSectionWriter); + } + + private struct CoffHeader + { + public Machine Machine { get; set; } + public uint NumberOfSections { get; set; } + public uint TimeDateStamp { get; set; } + public uint PointerToSymbolTable { get; set; } + public uint NumberOfSymbols { get; set; } + public ushort SizeOfOptionalHeader { get; set; } + public ushort Characteristics { get; set; } + + // Maximum number of section that can be handled Microsoft linker + // before it bails out. We automatically switch to big object file + // layout after that. + public bool IsBigObj => NumberOfSections > 65279; + + private static ReadOnlySpan BigObjMagic => new byte[] + { + 0xC7, 0xA1, 0xBA, 0xD1, 0xEE, 0xBA, 0xA9, 0x4B, + 0xAF, 0x20, 0xFA, 0xF6, 0x6A, 0xA4, 0xDC, 0xB8, + }; + + private const int RegularSize = + sizeof(ushort) + // Machine + sizeof(ushort) + // NumberOfSections + sizeof(uint) + // TimeDateStamp + sizeof(uint) + // PointerToSymbolTable + sizeof(uint) + // NumberOfSymbols + sizeof(ushort) + // SizeOfOptionalHeader + sizeof(ushort); // Characteristics + + private const int BigObjSize = + sizeof(ushort) + // Signature 1 (Machine = Unknown) + sizeof(ushort) + // Signature 2 (NumberOfSections = 0xFFFF) + sizeof(ushort) + // Version (2) + sizeof(ushort) + // Machine + sizeof(uint) + // TimeDateStamp + 16 + // BigObjMagic + sizeof(uint) + // Reserved1 + sizeof(uint) + // Reserved2 + sizeof(uint) + // Reserved3 + sizeof(uint) + // Reserved4 + sizeof(uint) + // NumberOfSections + sizeof(uint) + // PointerToSymbolTable + sizeof(uint); // NumberOfSymbols + + public int Size => IsBigObj ? BigObjSize : RegularSize; + + public void Write(FileStream stream) + { + if (!IsBigObj) + { + Span buffer = stackalloc byte[RegularSize]; + + BinaryPrimitives.WriteInt16LittleEndian(buffer, (short)Machine); + BinaryPrimitives.WriteUInt16LittleEndian(buffer.Slice(2), (ushort)NumberOfSections); + BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(4), TimeDateStamp); + BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(8), PointerToSymbolTable); + BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(12), NumberOfSymbols); + BinaryPrimitives.WriteUInt16LittleEndian(buffer.Slice(16), SizeOfOptionalHeader); + BinaryPrimitives.WriteUInt16LittleEndian(buffer.Slice(18), Characteristics); + + stream.Write(buffer); + } + else + { + Span buffer = stackalloc byte[BigObjSize]; + + buffer.Clear(); + BinaryPrimitives.WriteUInt16LittleEndian(buffer.Slice(2), 0xFFFF); + BinaryPrimitives.WriteUInt16LittleEndian(buffer.Slice(4), 2); + BinaryPrimitives.WriteInt16LittleEndian(buffer.Slice(6), (short)Machine); + BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(8), TimeDateStamp); + BigObjMagic.CopyTo(buffer.Slice(12)); + BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(44), NumberOfSections); + BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(48), PointerToSymbolTable); + BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(52), NumberOfSymbols); + + Debug.Assert(SizeOfOptionalHeader == 0); + Debug.Assert(Characteristics == 0); + + stream.Write(buffer); + } + } + } + + private sealed class CoffSectionHeader + { + public string Name { get; set; } + public uint VirtualSize { get; set; } + public uint VirtualAddress { get; set; } + public uint SizeOfRawData { get; set; } + public uint PointerToRawData { get; set; } + public uint PointerToRelocations { get; set; } + public uint PointerToLineNumbers { get; set; } + public ushort NumberOfRelocations { get; set; } + public ushort NumberOfLineNumbers { get; set; } + public SectionCharacteristics SectionCharacteristics { get; set; } + + private const int NameSize = 8; + + public const int Size = + NameSize + // Name size + sizeof(uint) + // VirtualSize + sizeof(uint) + // VirtualAddress + sizeof(uint) + // SizeOfRawData + sizeof(uint) + // PointerToRawData + sizeof(uint) + // PointerToRelocations + sizeof(uint) + // PointerToLineNumbers + sizeof(ushort) + // NumberOfRelocations + sizeof(ushort) + // NumberOfLineNumbers + sizeof(uint); // SectionCharacteristics + + public void Write(FileStream stream, CoffStringTable stringTable) + { + Span buffer = stackalloc byte[Size]; + + var nameBytes = Encoding.UTF8.GetByteCount(Name); + if (nameBytes <= NameSize) + { + Encoding.UTF8.GetBytes(Name, buffer); + if (nameBytes < NameSize) + { + buffer.Slice(nameBytes, 8 - nameBytes).Clear(); + } + } + else + { + buffer.Clear(); + buffer[0] = (byte)'/'; + uint offset = stringTable.GetStringOffset(Name); + if (offset <= 9999999) + { + Span charBuffer = stackalloc char[16]; + int charsWritten; + offset.TryFormat(charBuffer, out charsWritten); + for (int i = 0; i < charsWritten; i++) + { + buffer[1 + i] = (byte)charBuffer[i]; + } + } + else + { + ReadOnlySpan s_base64Alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"u8; + // Maximum expressible offset is 64^6 which is less than uint.MaxValue + buffer[1] = (byte)'/'; + for (int i = 0; i < 6; i++) + { + buffer[7 - i] = s_base64Alphabet[(int)(offset % 64)]; + offset /= 64; + } + } + } + + BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(NameSize), VirtualSize); + BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(NameSize + 4), VirtualAddress); + BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(NameSize + 8), SizeOfRawData); + BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(NameSize + 12), PointerToRawData); + BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(NameSize + 16), PointerToRelocations); + BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(NameSize + 20), PointerToLineNumbers); + BinaryPrimitives.WriteUInt16LittleEndian(buffer.Slice(NameSize + 24), NumberOfRelocations); + BinaryPrimitives.WriteUInt16LittleEndian(buffer.Slice(NameSize + 26), NumberOfLineNumbers); + BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(NameSize + 28), (uint)SectionCharacteristics); + + stream.Write(buffer); + } + } + + internal enum CoffRelocationType + { + IMAGE_REL_I386_ABSOLUTE = 0, + IMAGE_REL_I386_DIR32 = 6, + IMAGE_REL_I386_DIR32NB = 7, + IMAGE_REL_I386_SECTION = 10, + IMAGE_REL_I386_SECREL = 11, + IMAGE_REL_I386_REL32 = 20, + + IMAGE_REL_AMD64_ABSOLUTE = 0, + IMAGE_REL_AMD64_ADDR64 = 1, + IMAGE_REL_AMD64_ADDR32 = 2, + IMAGE_REL_AMD64_ADDR32NB = 3, + IMAGE_REL_AMD64_REL32 = 4, + IMAGE_REL_AMD64_REL32_1 = 5, + IMAGE_REL_AMD64_REL32_2 = 6, + IMAGE_REL_AMD64_REL32_3 = 7, + IMAGE_REL_AMD64_REL32_4 = 8, + IMAGE_REL_AMD64_REL32_5 = 9, + IMAGE_REL_AMD64_SECTION = 10, + IMAGE_REL_AMD64_SECREL = 11, + IMAGE_REL_AMD64_SECREL7 = 12, + IMAGE_REL_AMD64_TOKEN = 13, + IMAGE_REL_AMD64_SREL32 = 14, + IMAGE_REL_AMD64_PAIR = 15, + IMAGE_REL_AMD64_SSPAN32 = 16, + + IMAGE_REL_ARM64_ABSOLUTE = 0, + IMAGE_REL_ARM64_ADDR32 = 1, + IMAGE_REL_ARM64_ADDR32NB = 2, + IMAGE_REL_ARM64_BRANCH26 = 3, + IMAGE_REL_ARM64_PAGEBASE_REL21 = 4, + IMAGE_REL_ARM64_REL21 = 5, + IMAGE_REL_ARM64_PAGEOFFSET_12A = 6, + IMAGE_REL_ARM64_PAGEOFFSET_12L = 7, + IMAGE_REL_ARM64_SECREL = 8, + IMAGE_REL_ARM64_SECREL_LOW12A = 9, + IMAGE_REL_ARM64_SECREL_HIGH12A = 10, + IMAGE_REL_ARM64_SECREL_LOW12L = 11, + IMAGE_REL_ARM64_TOKEN = 12, + IMAGE_REL_ARM64_SECTION = 13, + IMAGE_REL_ARM64_ADDR64 = 14, + IMAGE_REL_ARM64_BRANCH19 = 15, + IMAGE_REL_ARM64_BRANCH14 = 16, + IMAGE_REL_ARM64_REL32 = 17, + } + + private sealed class CoffRelocation + { + public uint VirtualAddress { get; set; } + public uint SymbolTableIndex { get; set; } + public CoffRelocationType Type { get; set; } + + public const int Size = + sizeof(uint) + // VirtualAddress + sizeof(uint) + // SymbolTableIndex + sizeof(ushort); // Type + + public void Write(FileStream stream) + { + Span buffer = stackalloc byte[Size]; + + BinaryPrimitives.WriteUInt32LittleEndian(buffer, VirtualAddress); + BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(4), SymbolTableIndex); + BinaryPrimitives.WriteUInt16LittleEndian(buffer.Slice(8), (ushort)Type); + + stream.Write(buffer); + } + } + + private abstract class CoffSymbolRecord + { + public abstract void Write(Stream stream, CoffStringTable stringTable, bool isBigObj); + } + + private enum CoffSymbolClass : byte + { + IMAGE_SYM_CLASS_EXTERNAL = 2, + IMAGE_SYM_CLASS_STATIC = 3, + IMAGE_SYM_CLASS_LABEL = 6, + } + + private sealed class CoffSymbol : CoffSymbolRecord + { + public string Name { get; set; } + public uint Value { get; set; } + public uint SectionIndex { get; set; } + public ushort Type { get; set; } + public CoffSymbolClass StorageClass { get; set; } + public byte NumberOfAuxiliaryRecords { get; set; } + + private const int NameSize = 8; + + private const int RegularSize = + NameSize + // Name size + sizeof(uint) + // Value + sizeof(ushort) + // Section index + sizeof(ushort) + // Type + sizeof(byte) + // Storage class + sizeof(byte); // Auxiliary symbol count + + private const int BigObjSize = + NameSize + // Name size + sizeof(uint) + // Value + sizeof(uint) + // Section index + sizeof(ushort) + // Type + sizeof(byte) + // Storage class + sizeof(byte); // Auxiliary symbol count + + public override void Write(Stream stream, CoffStringTable stringTable, bool isBigObj) + { + Span buffer = stackalloc byte[isBigObj ? BigObjSize : RegularSize]; + + int nameBytes = Encoding.UTF8.GetByteCount(Name); + if (nameBytes <= NameSize) + { + Encoding.UTF8.GetBytes(Name, buffer); + if (nameBytes < NameSize) + { + buffer.Slice(nameBytes, 8 - nameBytes).Clear(); + } + } + else + { + BinaryPrimitives.WriteUInt32LittleEndian(buffer, 0); + BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(4, 4), stringTable.GetStringOffset(Name)); + } + + BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(NameSize), Value); + int sliceIndex; + if (isBigObj) + { + BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(NameSize + 4), SectionIndex); + sliceIndex = NameSize + 8; + } + else + { + Debug.Assert(SectionIndex == uint.MaxValue || SectionIndex < ushort.MaxValue); + BinaryPrimitives.WriteUInt16LittleEndian(buffer.Slice(NameSize + 4), (ushort)SectionIndex); + sliceIndex = NameSize + 6; + } + BinaryPrimitives.WriteUInt16LittleEndian(buffer.Slice(sliceIndex), Type); + buffer[sliceIndex + 2] = (byte)StorageClass; + buffer[sliceIndex + 3] = NumberOfAuxiliaryRecords; + + stream.Write(buffer); + } + } + + private enum CoffComdatSelect + { + IMAGE_COMDAT_SELECT_NODUPLICATES = 1, + IMAGE_COMDAT_SELECT_ANY = 2, + IMAGE_COMDAT_SELECT_SAME_SIZE = 3, + IMAGE_COMDAT_SELECT_EXACT_MATCH = 4, + IMAGE_COMDAT_SELECT_ASSOCIATIVE = 5, + IMAGE_COMDAT_SELECT_LARGEST = 6, + } + + private sealed class CoffSectionSymbol : CoffSymbolRecord + { + public uint SizeOfRawData { get; set; } + public ushort NumberOfRelocations { get; set; } + public ushort NumberOfLineNumbers { get; set; } + public uint CheckSum { get; set; } + public uint Number { get; set; } + public CoffComdatSelect Selection { get; set; } + + private const int RegularSize = + sizeof(uint) + // SizeOfRawData + sizeof(ushort) + // NumberOfRelocations + sizeof(ushort) + // NumberOfLineNumbers + sizeof(uint) + // CheckSum + sizeof(ushort) + // Number + sizeof(byte) + // Selection + 3; // Reserved + + private const int BigObjSize = RegularSize + 2; + + public override void Write(Stream stream, CoffStringTable stringTable, bool isBigObj) + { + Span buffer = stackalloc byte[isBigObj ? BigObjSize : RegularSize]; + + buffer.Clear(); + BinaryPrimitives.WriteUInt32LittleEndian(buffer, SizeOfRawData); + BinaryPrimitives.WriteUInt16LittleEndian(buffer.Slice(4), NumberOfRelocations); + BinaryPrimitives.WriteUInt16LittleEndian(buffer.Slice(6), NumberOfLineNumbers); + BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(8), CheckSum); + BinaryPrimitives.WriteUInt16LittleEndian(buffer.Slice(12), (ushort)Number); + buffer[14] = (byte)Selection; + if (isBigObj) + { + BinaryPrimitives.WriteUInt16LittleEndian(buffer.Slice(16), (ushort)(Number >> 16)); + } + + stream.Write(buffer); + } + } + + private sealed class CoffStringTable : StringTableBuilder + { + public new uint Size => (uint)(base.Size + 4); + + public new uint GetStringOffset(string text) + { + return base.GetStringOffset(text) + 4; + } + + public new void Write(FileStream stream) + { + Span stringTableSize = stackalloc byte[4]; + BinaryPrimitives.WriteUInt32LittleEndian(stringTableSize, Size); + stream.Write(stringTableSize); + base.Write(stream); + } + } + + /// + /// Checksum algorithm used for COMDAT sections. This is similar to standard + /// CRC32 but starts with 0 as initial value instead of ~0. + /// + private static class JamCrc32 + { + public static uint CalculateChecksum(Stream stream) + { + // NOTE: + // This can be generated by Crc32ReflectedTable.Generate(0xEDB88320u); + // We embed the pre-generated version since it's small. + ReadOnlySpan table = + [ + 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, + 0xE963A535, 0x9E6495A3, 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, + 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, 0x1DB71064, 0x6AB020F2, + 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, + 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, + 0xFA0F3D63, 0x8D080DF5, 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, + 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, 0x35B5A8FA, 0x42B2986C, + 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, + 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, + 0xCFBA9599, 0xB8BDA50F, 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, + 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, 0x76DC4190, 0x01DB7106, + 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433, + 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, + 0x91646C97, 0xE6635C01, 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, + 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, 0x65B0D9C6, 0x12B7E950, + 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, + 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, + 0xA4D1C46D, 0xD3D6F4FB, 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, + 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, 0x5005713C, 0x270241AA, + 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F, + 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, + 0xB7BD5C3B, 0xC0BA6CAD, 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, + 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, 0xE3630B12, 0x94643B84, + 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, + 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, + 0x196C3671, 0x6E6B06E7, 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, + 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, 0xD6D6A3E8, 0xA1D1937E, + 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, + 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, + 0x316E8EEF, 0x4669BE79, 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, + 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, 0xC5BA3BBE, 0xB2BD0B28, + 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, + 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, + 0x72076785, 0x05005713, 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, + 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, 0x86D3D2D4, 0xF1D4E242, + 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, + 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, + 0x616BFFD3, 0x166CCF45, 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, + 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, 0xAED16A4A, 0xD9D65ADC, + 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, + 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, + 0x54DE5729, 0x23D967BF, 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, + 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D + ]; + + uint crc = 0; + Span buffer = stackalloc byte[4096]; + while (stream.Position < stream.Length) + { + int length = stream.Read(buffer); + for (int i = 0; i < length; i++) + { + crc = table[(byte)(crc ^ buffer[i])] ^ (crc >> 8); + } + } + return crc; + } + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/Dwarf/DwarfAbbrev.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/Dwarf/DwarfAbbrev.cs new file mode 100644 index 0000000000000..4475b239aad3c --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/Dwarf/DwarfAbbrev.cs @@ -0,0 +1,301 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using static ILCompiler.ObjectWriter.DwarfNative; + +namespace ILCompiler.ObjectWriter +{ + internal sealed class DwarfAbbrev + { + private readonly ushort[] _definition; + + public const ushort DW_FORM_size = 0xDEAD; // Dummy value + + private DwarfAbbrev(ushort[] definition) + { + _definition = definition; + } + + public ushort Tag => _definition[0]; + + public bool HasChildren => _definition[1] == DW_CHILDREN_yes; + + public void Write(SectionWriter writer, int targetPointerSize) + { + writer.WriteULEB128(Tag); + writer.WriteULEB128(HasChildren ? DW_CHILDREN_yes : DW_CHILDREN_no); + + for (int i = 2; i < _definition.Length; i++) + { + // Attribute + writer.WriteULEB128(_definition[i++]); + // Form + if (_definition[i] != DW_FORM_size) + { + writer.WriteULEB128(_definition[i]); + } + else if (targetPointerSize == 8) + { + writer.WriteULEB128(DW_FORM_data8); + } + else if (targetPointerSize == 4) + { + writer.WriteULEB128(DW_FORM_data4); + } + } + + writer.Write([0, 0]); + } + + public static readonly DwarfAbbrev CompileUnit = new([ + DW_TAG_compile_unit, DW_CHILDREN_yes, + DW_AT_producer, DW_FORM_strp, + DW_AT_language, DW_FORM_data2, + DW_AT_name, DW_FORM_strp, + DW_AT_comp_dir, DW_FORM_strp, + DW_AT_low_pc, DW_FORM_addr, + DW_AT_ranges, DW_FORM_sec_offset, + DW_AT_stmt_list, DW_FORM_sec_offset]); + + public static readonly DwarfAbbrev BaseType = new([ + DW_TAG_base_type, DW_CHILDREN_no, + DW_AT_name, DW_FORM_strp, + DW_AT_encoding, DW_FORM_data1, + DW_AT_byte_size, DW_FORM_data1]); + + public static readonly DwarfAbbrev EnumerationType = new([ + DW_TAG_enumeration_type, DW_CHILDREN_yes, + DW_AT_name, DW_FORM_strp, + DW_AT_type, DW_FORM_ref4, + DW_AT_byte_size, DW_FORM_data1]); + + public static readonly DwarfAbbrev EnumerationTypeNoChildren = new([ + DW_TAG_enumeration_type, DW_CHILDREN_no, + DW_AT_name, DW_FORM_strp, + DW_AT_type, DW_FORM_ref4, + DW_AT_byte_size, DW_FORM_data1]); + + public static readonly DwarfAbbrev Enumerator1 = new([ + DW_TAG_enumerator, DW_CHILDREN_no, + DW_AT_name, DW_FORM_strp, + DW_AT_const_value, DW_FORM_data1]); + + public static readonly DwarfAbbrev Enumerator2 = new([ + DW_TAG_enumerator, DW_CHILDREN_no, + DW_AT_name, DW_FORM_strp, + DW_AT_const_value, DW_FORM_data2]); + + public static readonly DwarfAbbrev Enumerator4 = new([ + DW_TAG_enumerator, DW_CHILDREN_no, + DW_AT_name, DW_FORM_strp, + DW_AT_const_value, DW_FORM_data4]); + + public static readonly DwarfAbbrev Enumerator8 = new([ + DW_TAG_enumerator, DW_CHILDREN_no, + DW_AT_name, DW_FORM_strp, + DW_AT_const_value, DW_FORM_data8]); + + public static readonly DwarfAbbrev TypeDef = new([ + DW_TAG_typedef, DW_CHILDREN_no, + DW_AT_name, DW_FORM_strp, + DW_AT_type, DW_FORM_ref4]); + + public static readonly DwarfAbbrev Subprogram = new([ + DW_TAG_subprogram, DW_CHILDREN_yes, + DW_AT_specification, DW_FORM_ref4, + DW_AT_low_pc, DW_FORM_addr, + DW_AT_high_pc, DW_FORM_size, + DW_AT_frame_base, DW_FORM_exprloc, + DW_AT_object_pointer, DW_FORM_ref4]); + + public static readonly DwarfAbbrev SubprogramNoChildren = new([ + DW_TAG_subprogram, DW_CHILDREN_no, + DW_AT_specification, DW_FORM_ref4, + DW_AT_low_pc, DW_FORM_addr, + DW_AT_high_pc, DW_FORM_size, + DW_AT_frame_base, DW_FORM_exprloc, + DW_AT_object_pointer, DW_FORM_ref4]); + + public static readonly DwarfAbbrev SubprogramStatic = new([ + DW_TAG_subprogram, DW_CHILDREN_yes, + DW_AT_specification, DW_FORM_ref4, + DW_AT_low_pc, DW_FORM_addr, + DW_AT_high_pc, DW_FORM_size, + DW_AT_frame_base, DW_FORM_exprloc]); + + public static readonly DwarfAbbrev SubprogramStaticNoChildren = new([ + DW_TAG_subprogram, DW_CHILDREN_no, + DW_AT_specification, DW_FORM_ref4, + DW_AT_low_pc, DW_FORM_addr, + DW_AT_high_pc, DW_FORM_size, + DW_AT_frame_base, DW_FORM_exprloc]); + + public static readonly DwarfAbbrev SubprogramSpec = new([ + DW_TAG_subprogram, DW_CHILDREN_yes, + DW_AT_name, DW_FORM_strp, + DW_AT_linkage_name, DW_FORM_strp, + DW_AT_decl_file, DW_FORM_data1, + DW_AT_decl_line, DW_FORM_data1, + DW_AT_type, DW_FORM_ref4, + DW_AT_external, DW_FORM_flag_present, + DW_AT_declaration, DW_FORM_flag_present, + DW_AT_object_pointer, DW_FORM_ref4]); + + public static readonly DwarfAbbrev SubprogramStaticSpec = new([ + DW_TAG_subprogram, DW_CHILDREN_yes, + DW_AT_name, DW_FORM_strp, + DW_AT_linkage_name, DW_FORM_strp, + DW_AT_decl_file, DW_FORM_data1, + DW_AT_decl_line, DW_FORM_data1, + DW_AT_type, DW_FORM_ref4, + DW_AT_external, DW_FORM_flag_present, + DW_AT_declaration, DW_FORM_flag_present]); + + public static readonly DwarfAbbrev SubprogramStaticNoChildrenSpec = new([ + DW_TAG_subprogram, DW_CHILDREN_no, + DW_AT_name, DW_FORM_strp, + DW_AT_linkage_name, DW_FORM_strp, + DW_AT_decl_file, DW_FORM_data1, + DW_AT_decl_line, DW_FORM_data1, + DW_AT_type, DW_FORM_ref4, + DW_AT_external, DW_FORM_flag_present, + DW_AT_declaration, DW_FORM_flag_present]); + + public static readonly DwarfAbbrev Variable = new([ + DW_TAG_variable, DW_CHILDREN_no, + DW_AT_name, DW_FORM_strp, + DW_AT_decl_file, DW_FORM_data1, + DW_AT_decl_line, DW_FORM_data1, + DW_AT_type, DW_FORM_ref4, + DW_AT_location, DW_FORM_exprloc]); + + public static readonly DwarfAbbrev VariableLoc = new([ + DW_TAG_variable, DW_CHILDREN_no, + DW_AT_name, DW_FORM_strp, + DW_AT_decl_file, DW_FORM_data1, + DW_AT_decl_line, DW_FORM_data1, + DW_AT_type, DW_FORM_ref4, + DW_AT_location, DW_FORM_sec_offset]); + + public static readonly DwarfAbbrev VariableStatic = new([ + DW_TAG_variable, DW_CHILDREN_no, + DW_AT_specification, DW_FORM_ref4, + DW_AT_location, DW_FORM_exprloc]); + + public static readonly DwarfAbbrev FormalParameter = new([ + DW_TAG_formal_parameter, DW_CHILDREN_no, + DW_AT_name, DW_FORM_strp, + DW_AT_decl_file, DW_FORM_data1, + DW_AT_decl_line, DW_FORM_data1, + DW_AT_type, DW_FORM_ref4, + DW_AT_location, DW_FORM_exprloc]); + + public static readonly DwarfAbbrev FormalParameterThis = new([ + DW_TAG_formal_parameter, DW_CHILDREN_no, + DW_AT_name, DW_FORM_strp, + DW_AT_decl_file, DW_FORM_data1, + DW_AT_decl_line, DW_FORM_data1, + DW_AT_type, DW_FORM_ref4, + DW_AT_location, DW_FORM_exprloc, + DW_AT_artificial, DW_FORM_flag_present]); + + public static readonly DwarfAbbrev FormalParameterLoc = new([ + DW_TAG_formal_parameter, DW_CHILDREN_no, + DW_AT_name, DW_FORM_strp, + DW_AT_decl_file, DW_FORM_data1, + DW_AT_decl_line, DW_FORM_data1, + DW_AT_type, DW_FORM_ref4, + DW_AT_location, DW_FORM_sec_offset]); + + public static readonly DwarfAbbrev FormalParameterThisLoc = new([ + DW_TAG_formal_parameter, DW_CHILDREN_no, + DW_AT_name, DW_FORM_strp, + DW_AT_decl_file, DW_FORM_data1, + DW_AT_decl_line, DW_FORM_data1, + DW_AT_type, DW_FORM_ref4, + DW_AT_location, DW_FORM_sec_offset, + DW_AT_artificial, DW_FORM_flag_present]); + + public static readonly DwarfAbbrev FormalParameterSpec = new([ + DW_TAG_formal_parameter, DW_CHILDREN_no, + DW_AT_type, DW_FORM_ref4]); + + public static readonly DwarfAbbrev FormalParameterThisSpec = new([ + DW_TAG_formal_parameter, DW_CHILDREN_no, + DW_AT_type, DW_FORM_ref4, + DW_AT_artificial, DW_FORM_flag_present]); + + public static readonly DwarfAbbrev ClassType = new([ + DW_TAG_class_type, DW_CHILDREN_yes, + DW_AT_name, DW_FORM_strp, + DW_AT_byte_size, DW_FORM_data4]); + + public static readonly DwarfAbbrev ClassTypeNoChildren = new([ + DW_TAG_class_type, DW_CHILDREN_no, + DW_AT_name, DW_FORM_strp, + DW_AT_byte_size, DW_FORM_data4]); + + public static readonly DwarfAbbrev ClassTypeDecl = new([ + DW_TAG_class_type, DW_CHILDREN_no, + DW_AT_name, DW_FORM_strp, + DW_AT_declaration, DW_FORM_flag_present]); + + public static readonly DwarfAbbrev ClassMember = new([ + DW_TAG_member, DW_CHILDREN_no, + DW_AT_name, DW_FORM_strp, + DW_AT_type, DW_FORM_ref4, + DW_AT_data_member_location, DW_FORM_data4]); + + public static readonly DwarfAbbrev ClassMemberStatic = new([ + DW_TAG_member, DW_CHILDREN_no, + DW_AT_name, DW_FORM_strp, + DW_AT_type, DW_FORM_ref4, + DW_AT_external, DW_FORM_flag_present, + DW_AT_declaration, DW_FORM_flag_present]); + + public static readonly DwarfAbbrev PointerType = new([ + DW_TAG_pointer_type, DW_CHILDREN_no, + DW_AT_type, DW_FORM_ref4, + DW_AT_byte_size, DW_FORM_data1]); + + public static readonly DwarfAbbrev ReferenceType = new([ + DW_TAG_reference_type, DW_CHILDREN_no, + DW_AT_type, DW_FORM_ref4, + DW_AT_byte_size, DW_FORM_data1]); + + public static readonly DwarfAbbrev ArrayType = new([ + DW_TAG_array_type, DW_CHILDREN_yes, + DW_AT_type, DW_FORM_ref4]); + + public static readonly DwarfAbbrev SubrangeType = new([ + DW_TAG_subrange_type, DW_CHILDREN_no, + DW_AT_upper_bound, DW_FORM_udata]); + + public static readonly DwarfAbbrev ClassInheritance = new([ + DW_TAG_inheritance, DW_CHILDREN_no, + DW_AT_type, DW_FORM_ref4, + DW_AT_data_member_location, DW_FORM_data1]); + + public static readonly DwarfAbbrev LexicalBlock = new([ + DW_TAG_lexical_block, DW_CHILDREN_yes, + DW_AT_low_pc, DW_FORM_addr, + DW_AT_high_pc, DW_FORM_size]); + + public static readonly DwarfAbbrev TryBlock = new([ + DW_TAG_try_block, DW_CHILDREN_no, + DW_AT_low_pc, DW_FORM_addr, + DW_AT_high_pc, DW_FORM_size]); + + public static readonly DwarfAbbrev CatchBlock = new([ + DW_TAG_catch_block, DW_CHILDREN_no, + DW_AT_low_pc, DW_FORM_addr, + DW_AT_high_pc, DW_FORM_size]); + + public static readonly DwarfAbbrev VoidType = new([ + DW_TAG_unspecified_type, DW_CHILDREN_no, + DW_AT_name, DW_FORM_strp]); + + public static readonly DwarfAbbrev VoidPointerType = new([ + DW_TAG_pointer_type, DW_CHILDREN_no]); + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/Dwarf/DwarfBuilder.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/Dwarf/DwarfBuilder.cs new file mode 100644 index 0000000000000..d79abeea30c5d --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/Dwarf/DwarfBuilder.cs @@ -0,0 +1,488 @@ +// 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.Linq; +using ILCompiler.DependencyAnalysis; +using Internal.TypeSystem; +using Internal.TypeSystem.TypesDebugInfo; +using static ILCompiler.ObjectWriter.DwarfNative; + +namespace ILCompiler.ObjectWriter +{ + internal sealed class DwarfBuilder : ITypesDebugInfoWriter + { + private record struct SectionInfo(string SectionSymbolName, ulong Size); + private record struct MemberFunctionTypeInfo(MemberFunctionTypeDescriptor MemberDescriptor, uint[] ArgumentTypes, bool IsStatic); + public delegate (string SectionSymbolName, long Address) ResolveStaticVariable(string name); + + private readonly NameMangler _nameMangler; + private readonly TargetArchitecture _architecture; + private readonly byte _targetPointerSize; + private readonly bool _useDwarf5; + private readonly int _frameRegister; + private readonly byte _minimumInstructionLength; + private readonly RelocType _codeRelocType; + + private readonly Dictionary _lineSequences = new(); + private readonly Dictionary _fileNameMap = new(); // fileName -> _fileNames index (1-based) + private readonly List _fileNames = new(); + + private readonly List _sections = new(); + + private readonly List _memberFunctionTypeInfos = new(); + private readonly List _memberFunctions = new(); + private readonly Dictionary _primitiveDwarfTypes = new(); + private readonly Dictionary<(uint, uint), uint> _simpleArrayDwarfTypes = new(); // (elementTypeIndex, size) -> arrayTypeIndex + private readonly List _staticFields = new(); + + private readonly List _dwarfTypes = new(); + private uint[] _dwarfTypeOffsets; + private readonly List _dwarfSubprograms = new(); + + public DwarfBuilder( + NameMangler nameMangler, + TargetDetails target, + bool useDwarf5) + { + _nameMangler = nameMangler; + _architecture = target.Architecture; + _useDwarf5 = useDwarf5; + _minimumInstructionLength = (byte)target.MinimumCodeAlignment; + + switch (target.Architecture) + { + case TargetArchitecture.ARM64: + _targetPointerSize = 8; + _frameRegister = 29; // FP + _codeRelocType = RelocType.IMAGE_REL_BASED_DIR64; + break; + + case TargetArchitecture.ARM: + _targetPointerSize = 4; + _frameRegister = 7; // R7 + _codeRelocType = RelocType.IMAGE_REL_BASED_HIGHLOW; + break; + + case TargetArchitecture.X64: + _targetPointerSize = 8; + _frameRegister = 6; // RBP + _codeRelocType = RelocType.IMAGE_REL_BASED_DIR64; + break; + + case TargetArchitecture.X86: + _targetPointerSize = 4; + _frameRegister = 5; // EBP + _codeRelocType = RelocType.IMAGE_REL_BASED_HIGHLOW; + break; + + default: + throw new NotSupportedException("Unsupported architecture"); + } + } + + public TargetArchitecture TargetArchitecture => _architecture; + public byte TargetPointerSize => _targetPointerSize; + public int FrameRegister => _frameRegister; + + public uint ResolveOffset(uint typeIndex) => typeIndex == 0 ? 0u : _dwarfTypeOffsets[typeIndex - 1]; + + public void Write( + SectionWriter infoSectionWriter, + SectionWriter stringSectionWriter, + SectionWriter abbrevSectionWriter, + SectionWriter locSectionWriter, + SectionWriter rangeSectionWriter, + SectionWriter lineSectionWriter, + SectionWriter arangeSectionWriter, + ResolveStaticVariable resolveStaticVariable) + { + WriteInfoTable( + infoSectionWriter, + stringSectionWriter, + abbrevSectionWriter, + locSectionWriter, + rangeSectionWriter, + resolveStaticVariable); + WriteLineInfoTable(lineSectionWriter); + WriteAddressRangeTable(arangeSectionWriter); + } + + public void WriteInfoTable( + SectionWriter infoSectionWriter, + SectionWriter stringSectionWriter, + SectionWriter abbrevSectionWriter, + SectionWriter locSectionWriter, + SectionWriter rangeSectionWriter, + ResolveStaticVariable resolveStaticVariable) + { + // Length + byte[] sizeBuffer = new byte[sizeof(uint)]; + infoSectionWriter.EmitData(sizeBuffer); + // Version + infoSectionWriter.WriteLittleEndian((ushort)(_useDwarf5 ? 5u : 4u)); + if (_useDwarf5) + { + // Unit type, Address Size + infoSectionWriter.Write([DW_UT_compile, _targetPointerSize]); + // Abbrev offset + infoSectionWriter.EmitSymbolReference(RelocType.IMAGE_REL_BASED_HIGHLOW, ".debug_abbrev", 0); + } + else + { + // Abbrev offset + infoSectionWriter.EmitSymbolReference(RelocType.IMAGE_REL_BASED_HIGHLOW, ".debug_abbrev", 0); + // Address Size + infoSectionWriter.Write([_targetPointerSize]); + } + + using (DwarfInfoWriter dwarfInfoWriter = new( + infoSectionWriter, + stringSectionWriter, + abbrevSectionWriter, + locSectionWriter, + rangeSectionWriter, + this, + _codeRelocType)) + { + dwarfInfoWriter.WriteStartDIE(DwarfAbbrev.CompileUnit); + + // DW_AT_producer + dwarfInfoWriter.WriteStringReference("NetRuntime"); + // DW_AT_language + dwarfInfoWriter.WriteUInt16(DW_LANG_C_plus_plus); + // DW_AT_name + dwarfInfoWriter.WriteStringReference("il.cpp"); + // DW_AT_comp_dir + dwarfInfoWriter.WriteStringReference("/_"); + // DW_AT_low_pc + dwarfInfoWriter.WriteAddressSize(0); + // DW_AT_ranges + dwarfInfoWriter.WriteStartRangeList(); + foreach (var sectionInfo in _sections) + { + dwarfInfoWriter.WriteRangeListEntry(sectionInfo.SectionSymbolName, 0, (uint)sectionInfo.Size); + } + dwarfInfoWriter.WriteEndRangeList(); + // DW_AT_stmt_list + dwarfInfoWriter.WriteLineReference(0); + + _dwarfTypeOffsets = new uint[_dwarfTypes.Count]; + + int typeIndex = 0; + foreach (DwarfInfo type in _dwarfTypes) + { + _dwarfTypeOffsets[typeIndex] = (uint)dwarfInfoWriter.Position; + type.Dump(dwarfInfoWriter); + typeIndex++; + } + + foreach (DwarfInfo subprogram in _dwarfSubprograms) + { + subprogram.Dump(dwarfInfoWriter); + } + + foreach (DwarfStaticVariableInfo staticField in _staticFields) + { + (string sectionSymbolName, long address) = resolveStaticVariable(staticField.Name); + if (sectionSymbolName is not null) + { + staticField.Dump(dwarfInfoWriter, sectionSymbolName, address); + } + } + + dwarfInfoWriter.WriteEndDIE(); + } + + // End of compile unit + infoSectionWriter.WriteByte(0); + + // Update the size + BinaryPrimitives.WriteUInt32LittleEndian(sizeBuffer, (uint)(infoSectionWriter.Position - sizeof(uint))); + } + + private void WriteLineInfoTable(SectionWriter lineSectionWriter) + { + using (var lineProgramTableWriter = new DwarfLineProgramTableWriter( + lineSectionWriter, + _fileNames, + _targetPointerSize, + _minimumInstructionLength, + _codeRelocType)) + { + foreach (DwarfLineSequenceWriter lineSequence in _lineSequences.Values) + { + lineProgramTableWriter.WriteLineSequence(lineSequence); + } + } + } + + private void WriteAddressRangeTable(SectionWriter arangeSectionWriter) + { + // Length + var sizeBuffer = new byte[sizeof(uint)]; + arangeSectionWriter.EmitData(sizeBuffer); + // Version + arangeSectionWriter.WriteLittleEndian(2); + // Debug Info Offset + arangeSectionWriter.EmitSymbolReference(RelocType.IMAGE_REL_BASED_HIGHLOW, ".debug_info", 0); + // Address size, Segment selector size + arangeSectionWriter.Write([_targetPointerSize, 0]); + // Ranges have to be aligned + arangeSectionWriter.EmitAlignment(_targetPointerSize * 2); + foreach (var sectionInfo in _sections) + { + arangeSectionWriter.EmitSymbolReference(_codeRelocType, sectionInfo.SectionSymbolName, 0); + switch (_targetPointerSize) + { + case 8: arangeSectionWriter.WriteLittleEndian(sectionInfo.Size); break; + case 4: arangeSectionWriter.WriteLittleEndian((uint)sectionInfo.Size); break; + default: throw new NotSupportedException(); + } + } + arangeSectionWriter.WritePadding(_targetPointerSize * 2); + // Update the size + BinaryPrimitives.WriteUInt32LittleEndian(sizeBuffer, (uint)(arangeSectionWriter.Position - sizeof(uint))); + } + + public uint GetPrimitiveTypeIndex(TypeDesc type) + { + Debug.Assert(type.IsPrimitive, "it is not a primitive type"); + return GetPrimitiveTypeIndex(type.Category); + } + + private uint GetPrimitiveTypeIndex(TypeFlags typeFlags) + { + if (_primitiveDwarfTypes.TryGetValue(typeFlags, out uint index)) + { + return index; + } + + _dwarfTypes.Add(new DwarfPrimitiveTypeInfo(typeFlags, _targetPointerSize)); + uint typeIndex = (uint)_dwarfTypes.Count; + _primitiveDwarfTypes.Add(typeFlags, typeIndex); + return typeIndex; + } + + public uint GetPointerTypeIndex(PointerTypeDescriptor pointerDescriptor) + { + uint voidTypeIndex = GetPrimitiveTypeIndex(TypeFlags.Void); + + // Creating a pointer to what DWARF considers Void type (DW_TAG_unspecified_type - + // per http://eagercon.com/dwarf/issues/minutes-001017.htm) leads to unhappiness + // since debuggers don't really know how to handle that. The Clang symbol parser + // in LLDB only handles DW_TAG_unspecified_type if it's named + // "nullptr_t" or "decltype(nullptr)". + // + // We resort to this kludge to generate the exact same debug info for void* that + // clang would generate (pointer type with no element type specified). + if (pointerDescriptor.ElementType == voidTypeIndex) + { + _dwarfTypes.Add(new DwarfVoidPtrTypeInfo()); + } + else + { + _dwarfTypes.Add(new DwarfPointerTypeInfo(pointerDescriptor)); + } + + return (uint)_dwarfTypes.Count; + } + + private uint GetSimpleArrayTypeIndex(uint elementIndex, uint size) + { + if (_simpleArrayDwarfTypes.TryGetValue((elementIndex, size), out uint index)) + { + return index; + } + + _dwarfTypes.Add(new DwarfSimpleArrayTypeInfo(elementIndex, size)); + uint typeIndex = (uint)_dwarfTypes.Count; + _simpleArrayDwarfTypes.Add((elementIndex, size), typeIndex); + + return typeIndex; + } + + public uint GetArrayTypeIndex( + ClassTypeDescriptor classDescriptor, + ArrayTypeDescriptor arrayDescriptor) + { + // Create corresponding class info + ClassTypeDescriptor arrayClassDescriptor = classDescriptor; + + List fieldDescriptors = new(); + ulong fieldOffset = _targetPointerSize; + + fieldDescriptors.Add(new DataFieldDescriptor + { + FieldTypeIndex = GetPrimitiveTypeIndex(TypeFlags.Int32), + Offset = fieldOffset, + Name = "m_NumComponents", + }); + fieldOffset += _targetPointerSize; + + if (arrayDescriptor.IsMultiDimensional != 0) + { + fieldDescriptors.Add(new DataFieldDescriptor + { + FieldTypeIndex = GetSimpleArrayTypeIndex(GetPrimitiveTypeIndex(TypeFlags.Int32), arrayDescriptor.Rank), + Offset = fieldOffset, + Name = "m_Bounds", + }); + fieldOffset += 2u * 4u * (ulong)arrayDescriptor.Rank; + } + + fieldDescriptors.Add(new DataFieldDescriptor + { + FieldTypeIndex = GetSimpleArrayTypeIndex(arrayDescriptor.ElementType, 0), + Offset = fieldOffset, + Name = "m_Data", + }); + + // We currently don't encode the size of the variable length data. The DWARF5 + // specification allows encoding variable length arrays through DW_AT_lower_bound, + // DW_AT_upper_bound, and DW_AT_count expressions. There's potentially room + // to improve the debugging information by a more substential restructuring. + arrayClassDescriptor.InstanceSize = fieldOffset; + + ClassFieldsTypeDescriptor fieldsTypeDesc = new ClassFieldsTypeDescriptor + { + Size = _targetPointerSize, + FieldsCount = arrayDescriptor.IsMultiDimensional != 0 ? 3 : 2, + }; + + return GetCompleteClassTypeIndex(arrayClassDescriptor, fieldsTypeDesc, fieldDescriptors.ToArray(), null); + } + + public uint GetEnumTypeIndex( + EnumTypeDescriptor typeDescriptor, + EnumRecordTypeDescriptor[] typeRecords) + { + byte byteSize = ((DwarfPrimitiveTypeInfo)_dwarfTypes[(int)typeDescriptor.ElementType - 1]).ByteSize; + _dwarfTypes.Add(new DwarfEnumTypeInfo(typeDescriptor, typeRecords, byteSize)); + return (uint)_dwarfTypes.Count; + } + + public uint GetClassTypeIndex(ClassTypeDescriptor classDescriptor) + { + _dwarfTypes.Add(new DwarfClassTypeInfo(classDescriptor)); + return (uint)_dwarfTypes.Count; + } + + public uint GetCompleteClassTypeIndex( + ClassTypeDescriptor classTypeDescriptor, + ClassFieldsTypeDescriptor classFieldsTypeDescriptor, + DataFieldDescriptor[] fields, + StaticDataFieldDescriptor[] statics) + { + var classInfo = new DwarfClassTypeInfo(classTypeDescriptor, classFieldsTypeDescriptor, fields, statics); + _dwarfTypes.Add(classInfo); + _staticFields.AddRange(classInfo.StaticVariables); + return (uint)_dwarfTypes.Count; + } + + public uint GetMemberFunctionTypeIndex(MemberFunctionTypeDescriptor memberDescriptor, uint[] argumentTypes) + { + _memberFunctionTypeInfos.Add(new MemberFunctionTypeInfo( + memberDescriptor, + argumentTypes, + memberDescriptor.TypeIndexOfThisPointer == GetPrimitiveTypeIndex(TypeFlags.Void))); + + return (uint)_memberFunctionTypeInfos.Count; + } + + public uint GetMemberFunctionId(MemberFunctionIdTypeDescriptor memberIdDescriptor) + { + MemberFunctionTypeInfo memberFunctionTypeInfo = _memberFunctionTypeInfos[(int)memberIdDescriptor.MemberFunction - 1]; + DwarfClassTypeInfo parentClass = (DwarfClassTypeInfo)_dwarfTypes[(int)(memberIdDescriptor.ParentClass - 1)]; + DwarfMemberFunction memberFunction = new DwarfMemberFunction( + memberIdDescriptor.Name, + memberFunctionTypeInfo.MemberDescriptor, + memberFunctionTypeInfo.ArgumentTypes, + memberFunctionTypeInfo.IsStatic); + + parentClass.AddMemberFunction(memberFunction); + _memberFunctions.Add(memberFunction); + + return (uint)_memberFunctions.Count; + } + + public string GetMangledName(TypeDesc type) + { + return _nameMangler.GetMangledTypeName(type); + } + + public void EmitSubprogramInfo( + string methodName, + string sectionSymbolName, + long methodAddress, + int methodPCLength, + uint methodTypeIndex, + IEnumerable<(DebugVarInfoMetadata, uint)> debugVars, + DebugEHClauseInfo[] debugEHClauseInfos) + { + if (methodTypeIndex == 0) + { + return; + } + + DwarfMemberFunction memberFunction = _memberFunctions[(int)methodTypeIndex - 1]; + memberFunction.LinkageName = methodName; + + _dwarfSubprograms.Add(new DwarfSubprogramInfo( + sectionSymbolName, + methodAddress, + methodPCLength, + memberFunction, + debugVars.ToArray(), + debugEHClauseInfos)); + } + + public void EmitLineInfo( + int sectionIndex, + string sectionSymbolName, + long methodAddress, + IEnumerable sequencePoints) + { + DwarfLineSequenceWriter lineSequence; + + // Create line sequence for every section so they can get the + // base address relocated properly. + if (!_lineSequences.TryGetValue(sectionIndex, out lineSequence)) + { + lineSequence = new DwarfLineSequenceWriter(sectionSymbolName, _minimumInstructionLength); + _lineSequences.Add(sectionIndex, lineSequence); + } + + int fileNameIndex = 0; + string lastFileName = null; + + foreach (NativeSequencePoint sequencePoint in sequencePoints) + { + if (lastFileName != sequencePoint.FileName) + { + if (!_fileNameMap.TryGetValue(sequencePoint.FileName, out fileNameIndex)) + { + var dwarfFileName = string.IsNullOrEmpty(sequencePoint.FileName) ? + new DwarfFileName("", null) : + new DwarfFileName(Path.GetFileName(sequencePoint.FileName), Path.GetDirectoryName(sequencePoint.FileName)); + _fileNames.Add(dwarfFileName); + fileNameIndex = _fileNames.Count; + _fileNameMap.Add(sequencePoint.FileName, fileNameIndex); + } + lastFileName = sequencePoint.FileName; + } + + lineSequence.EmitLineInfo(fileNameIndex, methodAddress, sequencePoint); + } + } + + public void EmitSectionInfo(string sectionSymbolName, ulong size) + { + _sections.Add(new SectionInfo(sectionSymbolName, size)); + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/Dwarf/DwarfCie.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/Dwarf/DwarfCie.cs new file mode 100644 index 0000000000000..f0bc413e2c6e7 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/Dwarf/DwarfCie.cs @@ -0,0 +1,75 @@ +// 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 Internal.TypeSystem; +using static ILCompiler.ObjectWriter.DwarfNative; + +namespace ILCompiler.ObjectWriter +{ + internal sealed class DwarfCie + { + public readonly byte PointerEncoding; + public readonly byte LsdaEncoding; + public readonly byte PersonalityEncoding; + public readonly string PersonalitySymbolName; + public readonly uint CodeAlignFactor; + public readonly int DataAlignFactor; + public readonly bool IsSignalFrame; + public readonly bool FdesHaveAugmentationData; + public readonly byte ReturnAddressRegister; + public readonly byte[] Instructions; + public readonly byte InitialCFAOffset; + + public DwarfCie(TargetArchitecture targetArchitecture) + { + IsSignalFrame = false; + + // Each FDE has LSDA pointer + FdesHaveAugmentationData = true; + + // Unused + PersonalityEncoding = 0; + PersonalitySymbolName = null; + + // NOTE: Apple linker only knows how to handle DW_EH_PE_pcrel in combination with + // DW_EH_PE_sdata4 or DW_EH_PE_ptr. + PointerEncoding = DW_EH_PE_pcrel | DW_EH_PE_sdata4; + LsdaEncoding = DW_EH_PE_pcrel | DW_EH_PE_sdata4; + + switch (targetArchitecture) + { + case TargetArchitecture.ARM64: + CodeAlignFactor = 1; + DataAlignFactor = -4; + ReturnAddressRegister = 30; // LR + Instructions = new byte[] + { + DW_CFA_def_cfa, + 31, // SP + 0, // Offset from SP + }; + InitialCFAOffset = 0; + break; + + case TargetArchitecture.X64: + CodeAlignFactor = 1; + DataAlignFactor = -8; + ReturnAddressRegister = 16; // RA + Instructions = new byte[] + { + DW_CFA_def_cfa, + 7, // RSP + 8, // Offset from RSP + DW_CFA_offset | 16, // RIP + 1, // RIP is at -8 + }; + InitialCFAOffset = 8; + break; + + default: + throw new NotSupportedException("Unsupported architecture"); + } + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/Dwarf/DwarfEhFrame.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/Dwarf/DwarfEhFrame.cs new file mode 100644 index 0000000000000..d0e147b28eb0d --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/Dwarf/DwarfEhFrame.cs @@ -0,0 +1,197 @@ +// 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.Buffers.Binary; + +using ILCompiler.DependencyAnalysis; +using Internal.Text; + +using static ILCompiler.ObjectWriter.DwarfNative; + +namespace ILCompiler.ObjectWriter +{ + internal sealed class DwarfEhFrame + { + private readonly SectionWriter _sectionWriter; + private readonly bool _is64Bit; + private readonly Dictionary _cieOffset = new(); + + public DwarfEhFrame(SectionWriter sectionWriter, bool is64Bit) + { + _sectionWriter = sectionWriter; + _is64Bit = is64Bit; + } + + public void AddCie(DwarfCie cie) + { + _cieOffset.Add(cie, (uint)_sectionWriter.Position); + WriteCie(cie); + } + + public void AddFde(in DwarfFde fde) + { + uint cieOffset; + + if (!_cieOffset.TryGetValue(fde.Cie, out cieOffset)) + { + AddCie(fde.Cie); + cieOffset = _cieOffset[fde.Cie]; + } + + WriteFde(fde, cieOffset); + } + + private void WriteCie(DwarfCie cie) + { + Utf8StringBuilder augmentationString = new Utf8StringBuilder(); + uint augmentationLength = 0; + + if (cie.FdesHaveAugmentationData) + { + augmentationString.Append('z'); + } + if (cie.PersonalitySymbolName != null) + { + augmentationString.Append('P'); + augmentationLength += 1u + AddressSize(cie.PersonalityEncoding); + } + if (cie.LsdaEncoding != 0) + { + augmentationString.Append('L'); + augmentationLength++; + } + if (cie.PointerEncoding != 0) + { + augmentationString.Append('R'); + augmentationLength++; + } + if (cie.IsSignalFrame) + { + augmentationString.Append('S'); + } + + uint length = + 4u + // Length + 4u + // CIE Offset (0) + 1u + // Version + (uint)augmentationString.Length + 1u + + DwarfHelper.SizeOfULEB128(cie.CodeAlignFactor) + + DwarfHelper.SizeOfSLEB128(cie.DataAlignFactor) + + DwarfHelper.SizeOfULEB128(cie.ReturnAddressRegister) + + (uint)(augmentationLength > 0 ? DwarfHelper.SizeOfULEB128(augmentationLength) + augmentationLength : 0) + + (uint)cie.Instructions.Length; + uint padding = ((length + 7u) & ~7u) - length; + + _sectionWriter.WriteLittleEndian(length + padding - 4u); + _sectionWriter.WriteLittleEndian(0); + + _sectionWriter.WriteByte(cie.ReturnAddressRegister < 0x7F ? (byte)1u : (byte)3u); // Version + _sectionWriter.Write(augmentationString.UnderlyingArray); + + _sectionWriter.WriteULEB128(cie.CodeAlignFactor); + _sectionWriter.WriteSLEB128(cie.DataAlignFactor); + _sectionWriter.WriteULEB128(cie.ReturnAddressRegister); + + _sectionWriter.WriteULEB128(augmentationLength); + if (cie.PersonalitySymbolName != null) + { + _sectionWriter.WriteByte(cie.PersonalityEncoding); + WriteAddress(cie.PersonalityEncoding, cie.PersonalitySymbolName); + } + if (cie.LsdaEncoding != 0) + { + _sectionWriter.WriteByte(cie.LsdaEncoding); + } + if (cie.PointerEncoding != 0) + { + _sectionWriter.WriteByte(cie.PointerEncoding); + } + + _sectionWriter.Write(cie.Instructions); + + _sectionWriter.WritePadding((int)padding); + } + + private void WriteFde(in DwarfFde fde, uint cieOffset) + { + uint augmentationLength = + fde.Cie.FdesHaveAugmentationData ? + 1u + // Length + (fde.Cie.PersonalityEncoding != 0 ? AddressSize(fde.Cie.PersonalityEncoding) : 0) + + (fde.Cie.LsdaEncoding != 0 ? AddressSize(fde.Cie.LsdaEncoding) : 0) : 0; + + uint length = + 4u + // Length + 4u + // CIE offset + AddressSize(fde.Cie.PointerEncoding) + // PC start + AddressSize(fde.Cie.PointerEncoding) + // PC end + augmentationLength + + (uint)fde.Instructions.Length; + uint padding = ((length + 7u) & ~7u) - length; + + _sectionWriter.WriteLittleEndian(length + padding - 4u); + _sectionWriter.WriteLittleEndian((uint)(_sectionWriter.Position - cieOffset)); + WriteAddress(fde.Cie.PointerEncoding, fde.PcStartSymbolName, fde.PcStartSymbolOffset); + WriteSize(fde.Cie.PointerEncoding, fde.PcLength); + + if (fde.Cie.FdesHaveAugmentationData) + { + _sectionWriter.WriteByte((byte)(augmentationLength - 1)); + if (fde.Cie.PersonalityEncoding != 0) + { + WriteAddress(fde.Cie.PersonalityEncoding, fde.PersonalitySymbolName); + } + if (fde.Cie.LsdaEncoding != 0) + { + WriteAddress(fde.Cie.LsdaEncoding, fde.LsdaSymbolName); + } + } + + _sectionWriter.Write(fde.Instructions); + _sectionWriter.WritePadding((int)padding); + } + + private uint AddressSize(byte encoding) + { + switch (encoding & 0xF) + { + case DW_EH_PE_ptr: return _is64Bit ? 8u : 4u; + case DW_EH_PE_sdata4: return 4u; + default: + throw new NotSupportedException(); + } + } + + private void WriteAddress(byte encoding, string symbolName, long symbolOffset = 0) + { + if (symbolName != null) + { + RelocType relocationType = encoding switch + { + DW_EH_PE_pcrel | DW_EH_PE_sdata4 => RelocType.IMAGE_REL_BASED_RELPTR32, + DW_EH_PE_absptr => RelocType.IMAGE_REL_BASED_DIR64, + _ => throw new NotSupportedException() + }; + _sectionWriter.EmitSymbolReference(relocationType, symbolName, symbolOffset); + } + else + { + _sectionWriter.WritePadding((int)AddressSize(encoding)); + } + } + + private void WriteSize(byte encoding, ulong size) + { + if (AddressSize(encoding) == 4) + { + _sectionWriter.WriteLittleEndian((uint)size); + } + else + { + _sectionWriter.WriteLittleEndian(size); + } + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/Dwarf/DwarfExpressionBuilder.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/Dwarf/DwarfExpressionBuilder.cs new file mode 100644 index 0000000000000..98fb159e04774 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/Dwarf/DwarfExpressionBuilder.cs @@ -0,0 +1,169 @@ +// 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 Internal.TypeSystem; +using static ILCompiler.ObjectWriter.DwarfNative; + +namespace ILCompiler.ObjectWriter +{ + internal ref struct DwarfExpressionBuilder + { + private readonly TargetArchitecture _architecture; + private readonly byte _targetPointerSize; + private readonly IBufferWriter _writer; + + public DwarfExpressionBuilder(TargetArchitecture architecture, byte targetPointerSize, IBufferWriter writer) + { + _architecture = architecture; + _targetPointerSize = targetPointerSize; + _writer = writer; + } + + public void OpReg(int register) => OpDwarfReg(DwarfRegNum(_architecture, register)); + + public void OpBReg(int register, int offset = 0) => OpBDwarfReg(DwarfRegNum(_architecture, register), offset); + + public void OpDwarfReg(int register) + { + if (register <= 31) + { + OpCode((byte)(DW_OP_reg0 + register)); + } + else + { + OpCode(DW_OP_regx); + AppendULEB128((ulong)register); + } + } + + public void OpBDwarfReg(int register, int offset = 0) + { + if (register <= 31) + { + OpCode((byte)(DW_OP_breg0 + register)); + } + else + { + OpCode(DW_OP_bregx); + AppendULEB128((ulong)register); + } + AppendSLEB128(offset); + } + + public void OpDeref() => OpCode(DW_OP_deref); + + public void OpPiece(uint size = 0) + { + OpCode(DW_OP_piece); + AppendULEB128(size == 0 ? (uint)_targetPointerSize : size); + } + + private void OpCode(byte opcode) + { + var b = _writer.GetSpan(1); + b[0] = opcode; + _writer.Advance(1); + } + + private void AppendULEB128(ulong value) => DwarfHelper.WriteULEB128(_writer, value); + + private void AppendSLEB128(long value) => DwarfHelper.WriteSLEB128(_writer, value); + + private enum RegNumX86 : int + { + REGNUM_EAX, + REGNUM_ECX, + REGNUM_EDX, + REGNUM_EBX, + REGNUM_ESP, + REGNUM_EBP, + REGNUM_ESI, + REGNUM_EDI, + REGNUM_COUNT, + REGNUM_FP = REGNUM_EBP, + REGNUM_SP = REGNUM_ESP + }; + + private enum RegNumAmd64 : int + { + REGNUM_RAX, + REGNUM_RCX, + REGNUM_RDX, + REGNUM_RBX, + REGNUM_RSP, + REGNUM_RBP, + REGNUM_RSI, + REGNUM_RDI, + REGNUM_R8, + REGNUM_R9, + REGNUM_R10, + REGNUM_R11, + REGNUM_R12, + REGNUM_R13, + REGNUM_R14, + REGNUM_R15, + REGNUM_COUNT, + REGNUM_SP = REGNUM_RSP, + REGNUM_FP = REGNUM_RBP + }; + + public static int DwarfRegNum(TargetArchitecture architecture, int regNum) + { + switch (architecture) + { + case TargetArchitecture.ARM64: + // Normal registers are directly mapped + if (regNum >= 33) + regNum = regNum - 33 + 64; // FP + return regNum; + + case TargetArchitecture.ARM: + // Normal registers are directly mapped + if (regNum >= 16) + regNum = ((regNum - 16) / 2) + 256; // FP + return regNum; + + case TargetArchitecture.X64: + return (RegNumAmd64)regNum switch + { + RegNumAmd64.REGNUM_RAX => 0, + RegNumAmd64.REGNUM_RDX => 1, + RegNumAmd64.REGNUM_RCX => 2, + RegNumAmd64.REGNUM_RBX => 3, + RegNumAmd64.REGNUM_RSI => 4, + RegNumAmd64.REGNUM_RDI => 5, + RegNumAmd64.REGNUM_RBP => 6, + RegNumAmd64.REGNUM_RSP => 7, + RegNumAmd64.REGNUM_R8 => 8, + RegNumAmd64.REGNUM_R9 => 9, + RegNumAmd64.REGNUM_R10 => 10, + RegNumAmd64.REGNUM_R11 => 11, + RegNumAmd64.REGNUM_R12 => 12, + RegNumAmd64.REGNUM_R13 => 13, + RegNumAmd64.REGNUM_R14 => 14, + RegNumAmd64.REGNUM_R15 => 15, + _ => regNum - (int)RegNumAmd64.REGNUM_COUNT + 17 // FP registers + }; + + case TargetArchitecture.X86: + return (RegNumX86)regNum switch + { + RegNumX86.REGNUM_EAX => 0, + RegNumX86.REGNUM_ECX => 1, + RegNumX86.REGNUM_EDX => 2, + RegNumX86.REGNUM_EBX => 3, + RegNumX86.REGNUM_ESP => 4, + RegNumX86.REGNUM_EBP => 5, + RegNumX86.REGNUM_ESI => 6, + RegNumX86.REGNUM_EDI => 7, + _ => regNum - (int)RegNumX86.REGNUM_COUNT + 32 // FP registers + }; + + default: + throw new NotSupportedException(); + } + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/Dwarf/DwarfFde.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/Dwarf/DwarfFde.cs new file mode 100644 index 0000000000000..ea3bcdc740ec0 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/Dwarf/DwarfFde.cs @@ -0,0 +1,124 @@ +// 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.Diagnostics; +using System.Buffers; +using static ILCompiler.ObjectWriter.DwarfNative; + +namespace ILCompiler.ObjectWriter +{ + internal readonly struct DwarfFde + { + public readonly DwarfCie Cie; + public readonly byte[] Instructions; + public readonly string PcStartSymbolName; + public readonly long PcStartSymbolOffset; + public readonly ulong PcLength; + public readonly string LsdaSymbolName; + public readonly string PersonalitySymbolName; + + public DwarfFde( + DwarfCie cie, + byte[] blobData, + string pcStartSymbolName, + long pcStartSymbolOffset, + ulong pcLength, + string lsdaSymbolName, + string personalitySymbolName) + { + Cie = cie; + Instructions = CfiCodeToInstructions(cie, blobData); + PcStartSymbolName = pcStartSymbolName; + PcStartSymbolOffset = pcStartSymbolOffset; + PcLength = pcLength; + LsdaSymbolName = lsdaSymbolName; + PersonalitySymbolName = personalitySymbolName; + } + + private enum CFI_OPCODE + { + CFI_ADJUST_CFA_OFFSET, // Offset is adjusted relative to the current one. + CFI_DEF_CFA_REGISTER, // New register is used to compute CFA + CFI_REL_OFFSET, // Register is saved at offset from the current CFA + CFI_DEF_CFA // Take address from register and add offset to it. + } + + /// + /// Convert JIT version of CFI blob into the the DWARF byte code form. + /// + private static byte[] CfiCodeToInstructions(DwarfCie cie, byte[] blobData) + { + int cfaOffset = cie.InitialCFAOffset; + var cfiCode = ArrayPool.Shared.Rent(4096); + int cfiCodeOffset = 0; + byte codeOffset = 0; + byte lastCodeOffset = 0; + int offset = 0; + while (offset < blobData.Length) + { + codeOffset = Math.Max(codeOffset, blobData[offset++]); + CFI_OPCODE opcode = (CFI_OPCODE)blobData[offset++]; + short dwarfReg = BitConverter.ToInt16(blobData, offset); + offset += sizeof(short); + int cfiOffset = BitConverter.ToInt32(blobData, offset); + offset += sizeof(int); + + if (codeOffset != lastCodeOffset) + { + // Advance + int diff = (int)((codeOffset - lastCodeOffset) / cie.CodeAlignFactor); + if (diff <= 0x3F) + { + cfiCode[cfiCodeOffset++] = (byte)(DW_CFA_advance_loc | diff); + } + else + { + Debug.Assert(diff <= 0xFF); + cfiCode[cfiCodeOffset++] = DW_CFA_advance_loc1; + cfiCode[cfiCodeOffset++] = (byte)diff; + } + lastCodeOffset = codeOffset; + } + + switch (opcode) + { + case CFI_OPCODE.CFI_DEF_CFA_REGISTER: + cfiCode[cfiCodeOffset++] = DW_CFA_def_cfa_register; + cfiCode[cfiCodeOffset++] = (byte)dwarfReg; + break; + + case CFI_OPCODE.CFI_REL_OFFSET: + if (dwarfReg <= 0x3F) + { + cfiCode[cfiCodeOffset++] = (byte)(DW_CFA_offset | (byte)dwarfReg); + } + else + { + cfiCode[cfiCodeOffset++] = DW_CFA_offset_extended; + cfiCodeOffset += DwarfHelper.WriteULEB128(cfiCode.AsSpan(cfiCodeOffset), (uint)dwarfReg); + } + cfiCodeOffset += DwarfHelper.WriteULEB128(cfiCode.AsSpan(cfiCodeOffset), (uint)((cfiOffset - cfaOffset) / cie.DataAlignFactor)); + break; + + case CFI_OPCODE.CFI_ADJUST_CFA_OFFSET: + cfiCode[cfiCodeOffset++] = DW_CFA_def_cfa_offset; + cfaOffset += cfiOffset; + cfiCodeOffset += DwarfHelper.WriteULEB128(cfiCode.AsSpan(cfiCodeOffset), (uint)cfaOffset); + break; + + case CFI_OPCODE.CFI_DEF_CFA: + cfiCode[cfiCodeOffset++] = DW_CFA_def_cfa; + cfiCode[cfiCodeOffset++] = (byte)dwarfReg; + cfaOffset = cfiOffset; + cfiCodeOffset += DwarfHelper.WriteULEB128(cfiCode.AsSpan(cfiCodeOffset), (uint)cfaOffset); + break; + } + } + + var result = cfiCode[0..cfiCodeOffset]; + ArrayPool.Shared.Return(cfiCode); + return result; + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/Dwarf/DwarfFileName.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/Dwarf/DwarfFileName.cs new file mode 100644 index 0000000000000..56805a65689cf --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/Dwarf/DwarfFileName.cs @@ -0,0 +1,7 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace ILCompiler.ObjectWriter +{ + internal sealed record DwarfFileName(string Name, string Directory, ulong Time = 0, ulong Size = 0); +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/Dwarf/DwarfHelper.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/Dwarf/DwarfHelper.cs new file mode 100644 index 0000000000000..c595781222bfb --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/Dwarf/DwarfHelper.cs @@ -0,0 +1,83 @@ +// 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.Numerics; + +namespace ILCompiler.ObjectWriter +{ + internal static class DwarfHelper + { + public static uint SizeOfULEB128(ulong value) + { + // bits_to_encode = (data != 0) ? 64 - CLZ(x) : 1 = 64 - CLZ(data | 1) + // bytes = ceil(bits_to_encode / 7.0); = (6 + bits_to_encode) / 7 + uint x = 6 + 64 - (uint)BitOperations.LeadingZeroCount(value | 1UL); + // Division by 7 is done by (x * 37) >> 8 where 37 = ceil(256 / 7). + // This works for 0 <= x < 256 / (7 * 37 - 256), i.e. 0 <= x <= 85. + return (x * 37) >> 8; + } + + public static uint SizeOfSLEB128(long value) + { + // The same as SizeOfULEB128 calculation but we have to account for the sign bit. + uint x = 1 + 6 + 64 - (uint)BitOperations.LeadingZeroCount((ulong)(value ^ (value >> 63)) | 1UL); + return (x * 37) >> 8; + } + + public static int WriteULEB128(Span buffer, ulong value) + { + if (value >= 0x80) + { + int pos = 0; + do + { + buffer[pos++] = (byte)((value & 0x7F) | ((value >= 0x80) ? 0x80u : 0)); + value >>= 7; + } + while (value > 0); + return pos; + } + else + { + buffer[0] = (byte)value; + return 1; + } + } + + public static void WriteULEB128(IBufferWriter writer, ulong value) + { + Span buffer = writer.GetSpan((int)SizeOfULEB128(value)); + writer.Advance(WriteULEB128(buffer, value)); + } + + public static int WriteSLEB128(Span buffer, long value) + { + bool cont = true; + int pos = 0; + while (cont) + { + var b = (byte)((byte)value & 0x7F); + value >>= 7; + bool isSignBitSet = (b & 0x40) != 0; + if ((value == 0 && !isSignBitSet) || (value == -1 && isSignBitSet)) + { + cont = false; + } + else + { + b |= 0x80; + } + buffer[pos++] = b; + } + return pos; + } + + public static void WriteSLEB128(IBufferWriter writer, long value) + { + Span buffer = writer.GetSpan((int)SizeOfSLEB128(value)); + writer.Advance(WriteSLEB128(buffer, value)); + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/Dwarf/DwarfInfo.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/Dwarf/DwarfInfo.cs new file mode 100644 index 0000000000000..b1408e3eae3bb --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/Dwarf/DwarfInfo.cs @@ -0,0 +1,585 @@ +// 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.Diagnostics; +using System.Linq; +using ILCompiler.DependencyAnalysis; +using Internal.JitInterface; +using Internal.TypeSystem; +using Internal.TypeSystem.TypesDebugInfo; +using static ILCompiler.ObjectWriter.DwarfNative; + +namespace ILCompiler.ObjectWriter +{ + internal abstract class DwarfInfo + { + public abstract void Dump(DwarfInfoWriter writer); + } + + internal sealed class DwarfPrimitiveTypeInfo : DwarfInfo + { + private readonly TypeFlags _typeFlags; + private readonly int _targetPointerSize; + + public DwarfPrimitiveTypeInfo(TypeFlags typeFlags, int targetPointerSize) + { + _typeFlags = typeFlags; + _targetPointerSize = targetPointerSize; + } + + public byte ByteSize => _typeFlags switch + { + TypeFlags.Boolean => 1, + TypeFlags.Char => 2, + TypeFlags.SByte => 1, + TypeFlags.Byte => 1, + TypeFlags.Int16 => 2, + TypeFlags.UInt16 => 2, + TypeFlags.Int32 => 4, + TypeFlags.UInt32 => 4, + TypeFlags.Int64 => 8, + TypeFlags.UInt64 => 8, + TypeFlags.IntPtr => (byte)_targetPointerSize, + TypeFlags.UIntPtr => (byte)_targetPointerSize, + TypeFlags.Single => 4, + TypeFlags.Double => 8, + _ => 0, + }; + + public override void Dump(DwarfInfoWriter writer) + { + if (_typeFlags == TypeFlags.Void) + { + writer.WriteStartDIE(DwarfAbbrev.VoidType); + writer.WriteStringReference("void"); + writer.WriteEndDIE(); + } + else + { + var (name, encoding, byteSize) = _typeFlags switch + { + TypeFlags.Boolean => ("bool", DW_ATE_boolean, 1), + TypeFlags.Char => ("char16_t", DW_ATE_UTF, 2), + TypeFlags.SByte => ("sbyte", DW_ATE_signed, 1), + TypeFlags.Byte => ("byte", DW_ATE_unsigned, 1), + TypeFlags.Int16 => ("short", DW_ATE_signed, 2), + TypeFlags.UInt16 => ("ushort", DW_ATE_unsigned, 2), + TypeFlags.Int32 => ("int", DW_ATE_signed, 4), + TypeFlags.UInt32 => ("uint", DW_ATE_unsigned, 4), + TypeFlags.Int64 => ("long", DW_ATE_signed, 8), + TypeFlags.UInt64 => ("ulong", DW_ATE_unsigned, 8), + TypeFlags.IntPtr => ("nint", DW_ATE_signed, _targetPointerSize), + TypeFlags.UIntPtr => ("nuint", DW_ATE_unsigned, _targetPointerSize), + TypeFlags.Single => ("float", DW_ATE_float, 4), + TypeFlags.Double => ("double", DW_ATE_float, 8), + _ => ("", 0, 0), + }; + + writer.WriteStartDIE(DwarfAbbrev.BaseType); + writer.WriteStringReference(name); + writer.Write([(byte)encoding, (byte)byteSize]); + writer.WriteEndDIE(); + } + } + } + + internal sealed class DwarfEnumTypeInfo : DwarfInfo + { + private readonly EnumTypeDescriptor _typeDescriptor; + private readonly EnumRecordTypeDescriptor[] _typeRecords; + private readonly byte _byteSize; + + public DwarfEnumTypeInfo( + EnumTypeDescriptor typeDescriptor, + EnumRecordTypeDescriptor[] typeRecords, + byte byteSize) + { + _typeDescriptor = typeDescriptor; + _typeRecords = typeRecords; + _byteSize = byteSize; + } + + public override void Dump(DwarfInfoWriter writer) + { + writer.WriteStartDIE(_typeRecords.Length > 0 ? DwarfAbbrev.EnumerationType : DwarfAbbrev.EnumerationTypeNoChildren); + writer.WriteStringReference(_typeDescriptor.Name); + writer.WriteInfoReference(_typeDescriptor.ElementType); + writer.Write([_byteSize]); + + if (_typeRecords.Length > 0) + { + DwarfAbbrev abbrev = _byteSize switch { + 1 => DwarfAbbrev.Enumerator1, + 2 => DwarfAbbrev.Enumerator2, + 4 => DwarfAbbrev.Enumerator4, + 8 => DwarfAbbrev.Enumerator8, + _ => throw new NotSupportedException() + }; + + foreach (EnumRecordTypeDescriptor typeRecord in _typeRecords) + { + writer.WriteStartDIE(abbrev); + writer.WriteStringReference(typeRecord.Name); + switch (_byteSize) + { + case 1: writer.WriteUInt8((byte)typeRecord.Value); break; + case 2: writer.WriteUInt16((ushort)typeRecord.Value); break; + case 4: writer.WriteUInt32((uint)typeRecord.Value); break; + case 8: writer.WriteUInt64(typeRecord.Value); break; + }; + writer.WriteEndDIE(); + } + } + + writer.WriteEndDIE(); + } + } + + internal sealed class DwarfPointerTypeInfo : DwarfInfo + { + private PointerTypeDescriptor _typeDescriptor; + + public DwarfPointerTypeInfo(PointerTypeDescriptor typeDescriptor) + { + _typeDescriptor = typeDescriptor; + } + + public override void Dump(DwarfInfoWriter writer) + { + writer.WriteStartDIE(_typeDescriptor.IsReference != 0 ? DwarfAbbrev.ReferenceType : DwarfAbbrev.PointerType); + writer.WriteInfoReference(_typeDescriptor.ElementType); + writer.Write([_typeDescriptor.Is64Bit != 0 ? (byte)8 : (byte)4]); + writer.WriteEndDIE(); + } + } + + internal sealed class DwarfVoidPtrTypeInfo : DwarfInfo + { + public override void Dump(DwarfInfoWriter writer) + { + writer.WriteStartDIE(DwarfAbbrev.VoidPointerType); + writer.WriteEndDIE(); + } + } + + internal sealed class DwarfSimpleArrayTypeInfo : DwarfInfo + { + private readonly uint _elementType; + private readonly ulong _size; + + public DwarfSimpleArrayTypeInfo(uint elementType, ulong size) + { + _elementType = elementType; + _size = size; + } + + public override void Dump(DwarfInfoWriter writer) + { + writer.WriteStartDIE(DwarfAbbrev.ArrayType); + + // DW_AT_type + writer.WriteInfoReference(_elementType); + + writer.WriteStartDIE(DwarfAbbrev.SubrangeType); + // DW_AT_upper_bound + // NOTE: This produces garbage for _size == 0 + writer.WriteULEB128(_size - 1); + writer.WriteEndDIE(); + + writer.WriteEndDIE(); + } + } + + internal sealed class DwarfMemberFunction + { + public string Name { get; private set; } + public string LinkageName { get; set; } + public MemberFunctionTypeDescriptor Descriptor { get; private set; } + public uint[] ArgumentTypes { get; private set; } + public bool IsStatic { get; private set; } + public long InfoOffset { get; set; } + + public DwarfMemberFunction( + string name, + MemberFunctionTypeDescriptor descriptor, + uint[] argumentTypes, + bool isStatic) + { + Name = name; + LinkageName = name; + Descriptor = descriptor; + ArgumentTypes = argumentTypes; + IsStatic = isStatic; + } + } + + internal sealed class DwarfClassTypeInfo : DwarfInfo + { + private readonly bool _isForwardDecl; + private readonly ClassTypeDescriptor _typeDescriptor; + private readonly ClassFieldsTypeDescriptor _classFieldsTypeDescriptor; + private readonly DataFieldDescriptor[] _fields; + private readonly List _statics; + private List _methods; + + public DwarfClassTypeInfo(ClassTypeDescriptor typeDescriptor) + { + _isForwardDecl = true; + _typeDescriptor = typeDescriptor; + } + + public DwarfClassTypeInfo( + ClassTypeDescriptor typeDescriptor, + ClassFieldsTypeDescriptor classFieldsTypeDescriptor, + DataFieldDescriptor[] fields, + StaticDataFieldDescriptor[] statics) + { + _typeDescriptor = typeDescriptor; + _classFieldsTypeDescriptor = classFieldsTypeDescriptor; + _fields = fields; + if (statics is not null) + { + _statics = new(statics.Length); + foreach (StaticDataFieldDescriptor staticDescriptor in statics) + { + _statics.Add(new DwarfStaticVariableInfo(staticDescriptor)); + } + } + } + + public void AddMemberFunction(DwarfMemberFunction memberFunction) + { + _methods ??= new(); + _methods.Add(memberFunction); + } + + private bool HasChildren => + _typeDescriptor.BaseClassId != 0 || + _fields.Length > 0 || + _methods is not null; + + public IReadOnlyList StaticVariables => _statics ?? []; + + public override void Dump(DwarfInfoWriter writer) + { + writer.WriteStartDIE( + _isForwardDecl ? DwarfAbbrev.ClassTypeDecl : + HasChildren ? DwarfAbbrev.ClassType : DwarfAbbrev.ClassTypeNoChildren); + + // DW_AT_name + writer.WriteStringReference(_typeDescriptor.Name); + + if (!_isForwardDecl) + { + // DW_AT_byte_size + writer.WriteUInt32((uint)_typeDescriptor.InstanceSize); + + if (_typeDescriptor.BaseClassId != 0) + { + writer.WriteStartDIE(DwarfAbbrev.ClassInheritance); + // DW_AT_type + writer.WriteInfoReference(_typeDescriptor.BaseClassId); + // DW_AT_data_member_location + writer.Write([0]); + writer.WriteEndDIE(); + } + + int staticIndex = 0; + foreach (DataFieldDescriptor fieldDescriptor in _fields) + { + if (fieldDescriptor.Offset != 0xFFFFFFFFu) + { + writer.WriteStartDIE(DwarfAbbrev.ClassMember); + // DW_AT_name + writer.WriteStringReference(fieldDescriptor.Name); + // DW_AT_type + writer.WriteInfoReference(fieldDescriptor.FieldTypeIndex); + // DW_AT_data_member_location + writer.WriteUInt32((uint)fieldDescriptor.Offset); + writer.WriteEndDIE(); + } + else + { + _statics[staticIndex].InfoOffset = writer.Position; + writer.WriteStartDIE(DwarfAbbrev.ClassMemberStatic); + // DW_AT_name + writer.WriteStringReference(fieldDescriptor.Name); + // DW_AT_type + writer.WriteInfoReference(fieldDescriptor.FieldTypeIndex); + staticIndex++; + writer.WriteEndDIE(); + } + } + + if (_methods is not null) + { + foreach (DwarfMemberFunction method in _methods) + { + method.InfoOffset = writer.Position; + writer.WriteStartDIE( + !method.IsStatic ? DwarfAbbrev.SubprogramSpec : + method.ArgumentTypes.Length > 0 ? DwarfAbbrev.SubprogramStaticSpec : DwarfAbbrev.SubprogramStaticNoChildrenSpec); + // DW_AT_name + writer.WriteStringReference(method.Name); + // DW_AT_linkage_name + writer.WriteStringReference(method.LinkageName); + // DW_AT_decl_file, DW_AT_decl_line + writer.Write([1, 1]); + // DW_AT_type + writer.WriteInfoReference(method.Descriptor.ReturnType); + + if (!method.IsStatic) + { + // DW_AT_object_pointer + writer.WriteInfoAbsReference(writer.Position + sizeof(uint)); + + writer.WriteStartDIE(DwarfAbbrev.FormalParameterThisSpec); + // DW_AT_type + writer.WriteInfoReference(method.Descriptor.TypeIndexOfThisPointer); + writer.WriteEndDIE(); + } + + foreach (uint argumentType in method.ArgumentTypes) + { + writer.WriteStartDIE(DwarfAbbrev.FormalParameterSpec); + // DW_AT_type + writer.WriteInfoReference(argumentType); + writer.WriteEndDIE(); + } + + writer.WriteEndDIE(); + } + } + } + + writer.WriteEndDIE(); + } + } + + internal sealed class DwarfSubprogramInfo : DwarfInfo + { + private readonly string _sectionSymbolName; + private readonly long _methodAddress; + private readonly int _methodSize; + private readonly DwarfMemberFunction _memberFunction; + private readonly IEnumerable<(DebugVarInfoMetadata, uint)> _debugVars; + private readonly IEnumerable _debugEHClauseInfos; + private readonly bool _isStatic; + private readonly bool _hasChildren; + + public DwarfSubprogramInfo( + string sectionSymbolName, + long methodAddress, + int methodSize, + DwarfMemberFunction memberFunction, + IEnumerable<(DebugVarInfoMetadata, uint)> debugVars, + IEnumerable debugEHClauseInfos) + { + _sectionSymbolName = sectionSymbolName; + _methodAddress = methodAddress; + _methodSize = methodSize; + _memberFunction = memberFunction; + _isStatic = memberFunction.IsStatic; + _hasChildren = !_isStatic || debugVars.Any() || debugEHClauseInfos.Any(); + _debugVars = debugVars; + _debugEHClauseInfos = debugEHClauseInfos; + } + + public override void Dump(DwarfInfoWriter writer) + { + writer.WriteStartDIE( + !_isStatic ? DwarfAbbrev.Subprogram : + _hasChildren ? DwarfAbbrev.SubprogramStatic : DwarfAbbrev.SubprogramStaticNoChildren); + + // DW_AT_specification + writer.WriteInfoAbsReference(_memberFunction.InfoOffset); + + // DW_AT_low_pc + writer.WriteCodeReference(_sectionSymbolName, _methodAddress); + + // DW_AT_high_pc + writer.WriteAddressSize((ulong)_methodSize); + + // DW_AT_frame_base + writer.WriteULEB128(1); + writer.Write([(byte)(DW_OP_reg0 + writer.FrameRegister)]); + + if (!_isStatic) + { + // DW_AT_object_pointer + writer.WriteInfoAbsReference(writer.Position + sizeof(uint)); + } + + /// At the moment, the lexical scope reflects IL, not C#, meaning that + /// there is only one scope for the whole method. We could be more precise + /// in the future by pulling the scope information from the PDB. + foreach ((DebugVarInfoMetadata debugVar, uint typeIndex) in _debugVars) + { + bool isThis = debugVar.IsParameter && debugVar.DebugVarInfo.VarNumber == 0 && !_isStatic; + DumpVar(writer, debugVar, typeIndex, isThis); + } + + // EH clauses + foreach (DebugEHClauseInfo clause in _debugEHClauseInfos) + { + writer.WriteStartDIE(DwarfAbbrev.TryBlock); + // DW_AT_low_pc + writer.WriteCodeReference(_sectionSymbolName, _methodAddress + clause.TryOffset); + // DW_AT_high_pc + writer.WriteAddressSize(clause.TryLength); + writer.WriteEndDIE(); + + writer.WriteStartDIE(DwarfAbbrev.CatchBlock); + // DW_AT_low_pc + writer.WriteCodeReference(_sectionSymbolName, _methodAddress + clause.HandlerOffset); + // DW_AT_high_pc + writer.WriteAddressSize(clause.HandlerLength); + writer.WriteEndDIE(); + } + + writer.WriteEndDIE(); + } + + private void DumpVar(DwarfInfoWriter writer, DebugVarInfoMetadata metadataInfo, uint typeIndex, bool isThis) + { + bool usesDebugLoc = metadataInfo.DebugVarInfo.Ranges.Length != 1; + + if (metadataInfo.IsParameter) + { + if (isThis) + { + writer.WriteStartDIE(usesDebugLoc ? DwarfAbbrev.FormalParameterThisLoc : DwarfAbbrev.FormalParameterThis); + } + else + { + writer.WriteStartDIE(usesDebugLoc ? DwarfAbbrev.FormalParameterLoc : DwarfAbbrev.FormalParameter); + } + } + else + { + writer.WriteStartDIE(usesDebugLoc ? DwarfAbbrev.VariableLoc : DwarfAbbrev.Variable); + } + + // DW_AT_name + writer.WriteStringReference(isThis ? "this" : metadataInfo.Name); + + // DW_AT_decl_file, DW_AT_decl_line + writer.Write([1, 1]); + + // DW_AT_type + writer.WriteInfoReference(typeIndex); + + // DW_AT_location + if (usesDebugLoc) + { + writer.WriteStartLocationList(); + foreach (var range in metadataInfo.DebugVarInfo.Ranges) + { + var expressionBuilder = writer.GetExpressionBuilder(); + DumpVarLocation(expressionBuilder, range.VarLoc); + writer.WriteLocationListExpression( + _sectionSymbolName, + _methodAddress + range.StartOffset, + _methodAddress + range.EndOffset, + expressionBuilder); + } + writer.WriteEndLocationList(); + } + else + { + var expressionBuilder = writer.GetExpressionBuilder(); + DumpVarLocation(expressionBuilder, metadataInfo.DebugVarInfo.Ranges[0].VarLoc); + writer.WriteExpression(expressionBuilder); + } + + writer.WriteEndDIE(); + } + + private static void DumpVarLocation(DwarfExpressionBuilder e, VarLoc loc) + { + switch (loc.LocationType) + { + case VarLocType.VLT_REG: + case VarLocType.VLT_REG_FP: + e.OpReg(loc.B); + break; + case VarLocType.VLT_REG_BYREF: + e.OpBReg(loc.B); + break; + case VarLocType.VLT_STK: + case VarLocType.VLT_STK2: + case VarLocType.VLT_FPSTK: + case VarLocType.VLT_STK_BYREF: + e.OpBReg(loc.B, loc.C); + if (loc.LocationType == VarLocType.VLT_STK_BYREF) + { + e.OpDeref(); + } + break; + case VarLocType.VLT_REG_REG: + e.OpReg(loc.C); + e.OpPiece(); + e.OpReg(loc.B); + e.OpPiece(); + break; + case VarLocType.VLT_REG_STK: + e.OpReg(loc.B); + e.OpPiece(); + e.OpBReg(loc.C, loc.D); + e.OpPiece(); + break; + case VarLocType.VLT_STK_REG: + e.OpBReg(loc.C, loc.D); + e.OpPiece(); + e.OpReg(loc.B); + e.OpPiece(); + break; + default: + // Unsupported + Debug.Assert(loc.LocationType != VarLocType.VLT_FIXED_VA); + break; + } + } + } + + internal sealed class DwarfStaticVariableInfo + { + private readonly StaticDataFieldDescriptor _descriptor; + + public long InfoOffset { get; set; } + public string Name => _descriptor.StaticDataName; + + public DwarfStaticVariableInfo(StaticDataFieldDescriptor descriptor) + { + _descriptor = descriptor; + } + + public void Dump(DwarfInfoWriter writer, string sectionSymbolName, long address) + { + writer.WriteStartDIE(DwarfAbbrev.VariableStatic); + + // DW_AT_specification + writer.WriteInfoAbsReference(InfoOffset); + // DW_AT_location + uint length = 1 + (uint)writer.TargetPointerSize; // DW_OP_addr + if (_descriptor.IsStaticDataInObject != 0) + length += 2; // DW_OP_deref, DW_OP_deref + if (_descriptor.StaticOffset != 0) + length += 1 + DwarfHelper.SizeOfULEB128(_descriptor.StaticOffset); // DW_OP_plus_uconst + writer.WriteULEB128(length); + writer.Write([DW_OP_addr]); + writer.WriteCodeReference(sectionSymbolName, address); + if (_descriptor.IsStaticDataInObject != 0) + writer.Write([DW_OP_deref, DW_OP_deref]); + if (_descriptor.StaticOffset != 0) + { + writer.Write([DW_OP_plus_uconst]); + writer.WriteULEB128(_descriptor.StaticOffset); + } + + writer.WriteEndDIE(); + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/Dwarf/DwarfInfoWriter.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/Dwarf/DwarfInfoWriter.cs new file mode 100644 index 0000000000000..3a2290fb6d531 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/Dwarf/DwarfInfoWriter.cs @@ -0,0 +1,220 @@ +// 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.Collections.Generic; +using System.Diagnostics; +using ILCompiler.DependencyAnalysis; +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 _lateBoundReferences = new(); + private readonly Stack _dieStack = new(); + private readonly Dictionary _usedAbbrevs = new(); + private readonly ArrayBufferWriter _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 buffer) => _infoSectionWriter.Write(buffer); + public void WriteULEB128(ulong value) => _infoSectionWriter.WriteULEB128(value); + public void WriteUInt8(byte value) => _infoSectionWriter.WriteLittleEndian(value); + public void WriteUInt16(ushort value) => _infoSectionWriter.WriteLittleEndian(value); + public void WriteUInt32(uint value) => _infoSectionWriter.WriteLittleEndian(value); + public void WriteUInt64(ulong value) => _infoSectionWriter.WriteLittleEndian(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(string value) + { + long stringsOffset = _stringTableWriter.Position; + _stringTableWriter.WriteUtf8String(value); + + Debug.Assert(stringsOffset < uint.MaxValue); + _infoSectionWriter.EmitSymbolReference(RelocType.IMAGE_REL_BASED_HIGHLOW, ".debug_str", stringsOffset); + } + + public void WriteInfoAbsReference(long offset) + { + Debug.Assert(offset < uint.MaxValue); + _infoSectionWriter.EmitSymbolReference(RelocType.IMAGE_REL_BASED_HIGHLOW, ".debug_info", 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 + { + _infoSectionWriter.EmitSymbolReference(RelocType.IMAGE_REL_BASED_HIGHLOW, ".debug_info", offset); + } + } + + public void WriteCodeReference(string 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, ".debug_line", offset); + } + + public DwarfExpressionBuilder GetExpressionBuilder() + { + _expressionBufferWriter.Clear(); + 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, ".debug_loc", (int)offset); + } + + public void WriteLocationListExpression(string methodName, long startOffset, long endOffset, DwarfExpressionBuilder expressionBuilder) + { + _ = expressionBuilder; + _locSectionWriter.EmitSymbolReference(_codeRelocType, methodName, startOffset); + _locSectionWriter.EmitSymbolReference(_codeRelocType, methodName, endOffset); + _locSectionWriter.WriteLittleEndian((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, ".debug_ranges", offset); + } + + public void WriteRangeListEntry(string 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 + int streamOffset = (int)_infoSectionWriter.Position; + foreach (var lateBoundReference in _lateBoundReferences) + { + uint offset = _builder.ResolveOffset(lateBoundReference.TypeIndex); + + _infoSectionWriter.EmitRelocation( + - streamOffset + lateBoundReference.Position, + lateBoundReference.Data, + RelocType.IMAGE_REL_BASED_HIGHLOW, + ".debug_info", + (int)offset); + } + + // Write abbreviation section + foreach ((DwarfAbbrev abbrev, int abbreviationCode) in _usedAbbrevs) + { + _abbrevSectionWriter.WriteULEB128((ulong)abbreviationCode); + abbrev.Write(_abbrevSectionWriter, TargetPointerSize); + } + _abbrevSectionWriter.Write([0, 0]); + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/Dwarf/DwarfLineProgramTableWriter.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/Dwarf/DwarfLineProgramTableWriter.cs new file mode 100644 index 0000000000000..d185bad1db80a --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/Dwarf/DwarfLineProgramTableWriter.cs @@ -0,0 +1,125 @@ +// 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 ILCompiler.DependencyAnalysis; + +namespace ILCompiler.ObjectWriter +{ + internal sealed class DwarfLineProgramTableWriter : IDisposable + { + private readonly SectionWriter _lineSectionWriter; + private readonly byte _targetPointerSize; + private readonly RelocType _codeRelocType; + private readonly Dictionary _directoryNameToIndex = new(); + private readonly Dictionary _fileNameToIndex = new(); + private readonly byte[] _sizeBuffer; + + public const byte MaximumOperationsPerInstruction = 1; + public const sbyte LineBase = -5; + public const byte LineRange = 14; + public const byte OpCodeBase = 13; + + private static ReadOnlySpan StandardOpCodeLengths => + [ + 0, // DW_LNS_copy + 1, // DW_LNS_advance_pc + 1, // DW_LNS_advance_line + 1, // DW_LNS_set_file + 1, // DW_LNS_set_column + 0, // DW_LNS_negate_stmt + 0, // DW_LNS_set_basic_block + 0, // DW_LNS_const_add_pc + 1, // DW_LNS_fixed_advance_pc + 0, // DW_LNS_set_prologue_end + 0, // DW_LNS_set_epilogue_begin + 1, // DW_LNS_set_isa + ]; + + public DwarfLineProgramTableWriter( + SectionWriter lineSectionWriter, + IReadOnlyList fileNames, + byte targetPointerSize, + byte minimumInstructionLength, + RelocType codeRelocType) + { + _lineSectionWriter = lineSectionWriter; + _targetPointerSize = targetPointerSize; + _codeRelocType = codeRelocType; + + // Length + _sizeBuffer = new byte[sizeof(uint)]; + lineSectionWriter.EmitData(_sizeBuffer); + // Version + lineSectionWriter.WriteLittleEndian(4); + // Header Length + var headerSizeBuffer = new byte[sizeof(uint)]; + lineSectionWriter.EmitData(headerSizeBuffer); + var headerStart = lineSectionWriter.Position; + lineSectionWriter.WriteByte(minimumInstructionLength); + lineSectionWriter.WriteByte(MaximumOperationsPerInstruction); + // default_is_stmt + lineSectionWriter.WriteByte(1); + // line_base + lineSectionWriter.WriteByte(unchecked((byte)LineBase)); + // line_range + lineSectionWriter.WriteByte(LineRange); + // opcode_base + lineSectionWriter.WriteByte(OpCodeBase); + // standard_opcode_lengths + foreach (var opcodeLength in StandardOpCodeLengths) + { + lineSectionWriter.WriteULEB128(opcodeLength); + } + + // Directory names + uint directoryIndex = 1; + foreach (var fileName in fileNames) + { + if (fileName.Directory is string directoryName && + !string.IsNullOrEmpty(directoryName) && + !_directoryNameToIndex.ContainsKey(directoryName)) + { + lineSectionWriter.WriteUtf8String(directoryName); + _directoryNameToIndex.Add(directoryName, directoryIndex); + directoryIndex++; + } + } + // Terminate directory list (empty string) + lineSectionWriter.WriteByte(0); + + // File names + uint fileNameIndex = 1; + foreach (var fileName in fileNames) + { + directoryIndex = fileName.Directory is string directoryName && !string.IsNullOrEmpty(directoryName) ? _directoryNameToIndex[directoryName] : 0; + + lineSectionWriter.WriteUtf8String(fileName.Name); + lineSectionWriter.WriteULEB128(directoryIndex); + lineSectionWriter.WriteULEB128(fileName.Time); + lineSectionWriter.WriteULEB128(fileName.Size); + + _fileNameToIndex[fileName] = fileNameIndex; + fileNameIndex++; + } + // Terminate file name list (empty string) + lineSectionWriter.WriteByte(0); + + // Update header size + BinaryPrimitives.WriteInt32LittleEndian(headerSizeBuffer, (int)(lineSectionWriter.Position - headerStart)); + } + + public void Dispose() + { + // Update size + BinaryPrimitives.WriteInt32LittleEndian(_sizeBuffer, (int)(_lineSectionWriter.Position - sizeof(uint))); + } + + public void WriteLineSequence(DwarfLineSequenceWriter lineSequenceWriter) + { + lineSequenceWriter.Write(_lineSectionWriter, _targetPointerSize, _codeRelocType); + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/Dwarf/DwarfLineSequenceWriter.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/Dwarf/DwarfLineSequenceWriter.cs new file mode 100644 index 0000000000000..2fa8650b0afa8 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/Dwarf/DwarfLineSequenceWriter.cs @@ -0,0 +1,124 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Buffers; +using System.Diagnostics; +using ILCompiler.DependencyAnalysis; +using static ILCompiler.ObjectWriter.DwarfNative; + +namespace ILCompiler.ObjectWriter +{ + internal sealed class DwarfLineSequenceWriter + { + private readonly ArrayBufferWriter _writer; + private readonly string _sectionName; + private readonly byte _minimumInstructionLength; + private readonly uint _maxDeltaAddressPerSpecialCode; + + // DWARF state machine + private ulong _address; + private int _fileNameIndex; + private int _line = 1; + private int _column; + + public DwarfLineSequenceWriter(string sectionName, byte minimumInstructionLength) + { + _writer = new ArrayBufferWriter(); + _sectionName = sectionName; + + // Calculations hard code this + Debug.Assert(DwarfLineProgramTableWriter.MaximumOperationsPerInstruction == 1); + + byte maxOperationAdvance = (255 - DwarfLineProgramTableWriter.OpCodeBase) / DwarfLineProgramTableWriter.LineRange; + _maxDeltaAddressPerSpecialCode = (uint)(maxOperationAdvance * minimumInstructionLength); + _minimumInstructionLength = minimumInstructionLength; + } + + private void WriteByte(byte value) + { + _writer.GetSpan(1)[0] = value; + _writer.Advance(1); + } + + private void WriteULEB128(ulong value) => DwarfHelper.WriteULEB128(_writer, value); + + private void WriteSLEB128(long value) => DwarfHelper.WriteSLEB128(_writer, value); + + public void EmitLineInfo(int fileNameIndex, long methodAddress, NativeSequencePoint sequencePoint) + { + if (_column != sequencePoint.ColNumber) + { + _column = sequencePoint.ColNumber; + WriteByte(DW_LNS_set_column); + WriteULEB128((uint)_column); + } + + if (_fileNameIndex != fileNameIndex) + { + _fileNameIndex = fileNameIndex; + WriteByte(DW_LNS_set_file); + WriteULEB128((uint)_fileNameIndex); + } + + int deltaLine = sequencePoint.LineNumber - _line; + if (deltaLine != 0) + { + bool canEncodeLineInSpecialCode = + deltaLine >= DwarfLineProgramTableWriter.LineBase && + deltaLine < DwarfLineProgramTableWriter.LineBase + DwarfLineProgramTableWriter.LineRange; + + if (!canEncodeLineInSpecialCode) + { + WriteByte(DW_LNS_advance_line); + WriteSLEB128(deltaLine); + deltaLine = 0; + } + } + + ulong deltaAddress = (ulong)sequencePoint.NativeOffset + (ulong)methodAddress - _address; + if (deltaAddress > _maxDeltaAddressPerSpecialCode && deltaAddress <= (2U * _maxDeltaAddressPerSpecialCode)) + { + deltaAddress -= _maxDeltaAddressPerSpecialCode; + WriteByte(DW_LNS_const_add_pc); + } + + if (deltaAddress > 0 || deltaLine != 0) + { + ulong operationAdvance = deltaAddress / _minimumInstructionLength; + ulong opcode = + operationAdvance * DwarfLineProgramTableWriter.LineRange + + DwarfLineProgramTableWriter.OpCodeBase + (ulong)(deltaLine - DwarfLineProgramTableWriter.LineBase); + if (opcode > 255) + { + WriteByte(DW_LNS_advance_pc); + WriteULEB128((uint)operationAdvance); + if (deltaLine != 0) + { + WriteByte((byte)(DwarfLineProgramTableWriter.OpCodeBase + deltaLine - DwarfLineProgramTableWriter.LineBase)); + } + else + { + WriteByte(DW_LNS_copy); + } + } + else + { + WriteByte((byte)opcode); + } + } + + _line = sequencePoint.LineNumber; + _address = (ulong)sequencePoint.NativeOffset + (ulong)methodAddress; + } + + public void Write(SectionWriter lineSection, byte targetPointerSize, RelocType codeRelocType) + { + // Set the address to beginning of section + lineSection.Write([0, (byte)(1u + targetPointerSize), DW_LNE_set_address]); + lineSection.EmitSymbolReference(codeRelocType, _sectionName, 0); + + lineSection.Write(_writer.WrittenSpan); + lineSection.Write([0, 1, DW_LNE_end_sequence]); + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/Dwarf/DwarfNative.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/Dwarf/DwarfNative.cs new file mode 100644 index 0000000000000..d8e0c36ca685c --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/Dwarf/DwarfNative.cs @@ -0,0 +1,548 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace ILCompiler.ObjectWriter +{ + internal static class DwarfNative + { + public const byte DW_EH_PE_absptr = 0x00; + public const byte DW_EH_PE_omit = 0xFF; + public const byte DW_EH_PE_ptr = 0x00; + public const byte DW_EH_PE_uleb128 = 0x01; + public const byte DW_EH_PE_udata2 = 0x02; + public const byte DW_EH_PE_udata4 = 0x03; + public const byte DW_EH_PE_udata8 = 0x04; + public const byte DW_EH_PE_sleb128 = 0x09; + public const byte DW_EH_PE_sdata2 = 0x0A; + public const byte DW_EH_PE_sdata4 = 0x0B; + public const byte DW_EH_PE_sdata8 = 0x0C; + public const byte DW_EH_PE_signed = 0x08; + public const byte DW_EH_PE_pcrel = 0x10; + public const byte DW_EH_PE_textrel = 0x20; + public const byte DW_EH_PE_datarel = 0x30; + public const byte DW_EH_PE_funcrel = 0x40; + public const byte DW_EH_PE_aligned = 0x50; + public const byte DW_EH_PE_indirect = 0x80; + + public const byte DW_CFA_nop = 0x0; + public const byte DW_CFA_set_loc = 0x1; + public const byte DW_CFA_advance_loc1 = 0x2; + public const byte DW_CFA_advance_loc2 = 0x3; + public const byte DW_CFA_advance_loc4 = 0x4; + public const byte DW_CFA_offset_extended = 0x5; + public const byte DW_CFA_restore_extended = 0x6; + public const byte DW_CFA_undefined = 0x7; + public const byte DW_CFA_same_value = 0x8; + public const byte DW_CFA_register = 0x9; + public const byte DW_CFA_remember_state = 0xA; + public const byte DW_CFA_restore_state = 0xB; + public const byte DW_CFA_def_cfa = 0xC; + public const byte DW_CFA_def_cfa_register = 0xD; + public const byte DW_CFA_def_cfa_offset = 0xE; + public const byte DW_CFA_def_cfa_expression = 0xF; + public const byte DW_CFA_expression = 0x10; + public const byte DW_CFA_offset_extended_sf = 0x11; + public const byte DW_CFA_def_cfa_sf = 0x12; + public const byte DW_CFA_def_cfa_offset_sf = 0x13; + public const byte DW_CFA_val_offset = 0x14; + public const byte DW_CFA_val_offset_sf = 0x15; + public const byte DW_CFA_val_expression = 0x16; + public const byte DW_CFA_advance_loc = 0x40; + public const byte DW_CFA_offset = 0x80; + public const byte DW_CFA_restore = 0xC0; + public const byte DW_CFA_GNU_window_save = 0x2D; + public const byte DW_CFA_GNU_args_size = 0x2E; + public const byte DW_CFA_GNU_negative_offset_extended = 0x2F; + public const byte DW_CFA_AARCH64_negate_ra_state = 0x2D; + + public const byte DW_ATE_address = 0x01; + public const byte DW_ATE_boolean = 0x02; + public const byte DW_ATE_complex_float = 0x03; + public const byte DW_ATE_float = 0x04; + public const byte DW_ATE_signed = 0x05; + public const byte DW_ATE_signed_char = 0x06; + public const byte DW_ATE_unsigned = 0x07; + public const byte DW_ATE_unsigned_char = 0x08; + public const byte DW_ATE_imaginary_float = 0x09; /* DWARF3 */ + public const byte DW_ATE_packed_decimal = 0x0A; /* DWARF3f */ + public const byte DW_ATE_numeric_string = 0x0B; /* DWARF3f */ + public const byte DW_ATE_edited = 0x0C; /* DWARF3f */ + public const byte DW_ATE_signed_fixed = 0x0D; /* DWARF3f */ + public const byte DW_ATE_unsigned_fixed = 0x0E; /* DWARF3f */ + public const byte DW_ATE_decimal_float = 0x0F; /* DWARF3f */ + public const byte DW_ATE_UTF = 0x10; /* DWARF4 */ + public const byte DW_ATE_UCS = 0x11; /* DWARF5 */ + public const byte DW_ATE_ASCII = 0x12; /* DWARF5 */ + + public const ushort DW_TAG_array_type = 1; + public const ushort DW_TAG_class_type = 2; + public const ushort DW_TAG_entry_point = 3; + public const ushort DW_TAG_enumeration_type = 4; + public const ushort DW_TAG_formal_parameter = 5; + public const ushort DW_TAG_imported_declaration = 8; + public const ushort DW_TAG_label = 10; + public const ushort DW_TAG_lexical_block = 11; + public const ushort DW_TAG_member = 13; + public const ushort DW_TAG_pointer_type = 15; + public const ushort DW_TAG_reference_type = 16; + public const ushort DW_TAG_compile_unit = 17; + public const ushort DW_TAG_string_type = 18; + public const ushort DW_TAG_structure_type = 19; + public const ushort DW_TAG_subroutine_type = 21; + public const ushort DW_TAG_typedef = 22; + public const ushort DW_TAG_union_type = 23; + public const ushort DW_TAG_unspecified_parameters = 24; + public const ushort DW_TAG_variant = 25; + public const ushort DW_TAG_common_block = 26; + public const ushort DW_TAG_common_inclusion = 27; + public const ushort DW_TAG_inheritance = 28; + public const ushort DW_TAG_inlined_subroutine = 29; + public const ushort DW_TAG_module = 30; + public const ushort DW_TAG_ptr_to_member_type = 31; + public const ushort DW_TAG_set_type = 32; + public const ushort DW_TAG_subrange_type = 33; + public const ushort DW_TAG_with_stmt = 34; + public const ushort DW_TAG_access_declaration = 35; + public const ushort DW_TAG_base_type = 36; + public const ushort DW_TAG_catch_block = 37; + public const ushort DW_TAG_const_type = 38; + public const ushort DW_TAG_constant = 39; + public const ushort DW_TAG_enumerator = 40; + public const ushort DW_TAG_file_type = 41; + public const ushort DW_TAG_friend = 42; + public const ushort DW_TAG_namelist = 43; + public const ushort DW_TAG_namelist_item = 44; + public const ushort DW_TAG_packed_type = 45; + public const ushort DW_TAG_subprogram = 46; + public const ushort DW_TAG_template_type_param = 47; + public const ushort DW_TAG_template_value_param = 48; + public const ushort DW_TAG_thrown_type = 49; + public const ushort DW_TAG_try_block = 50; + public const ushort DW_TAG_variant_part = 51; + public const ushort DW_TAG_variable = 52; + public const ushort DW_TAG_volatile_type = 53; + public const ushort DW_TAG_dwarf_procedure = 54; + public const ushort DW_TAG_restrict_type = 55; + public const ushort DW_TAG_interface_type = 56; + public const ushort DW_TAG_namespace = 57; + public const ushort DW_TAG_imported_module = 58; + public const ushort DW_TAG_unspecified_type = 59; + public const ushort DW_TAG_partial_unit = 60; + public const ushort DW_TAG_imported_unit = 61; + public const ushort DW_TAG_mutable_type = 62; + public const ushort DW_TAG_condition = 63; + public const ushort DW_TAG_shared_type = 64; + public const ushort DW_TAG_type_unit = 65; + public const ushort DW_TAG_rvalue_reference_type = 66; + public const ushort DW_TAG_template_alias = 67; + public const ushort DW_TAG_coarray_type = 68; + public const ushort DW_TAG_generic_subrange = 69; + public const ushort DW_TAG_dynamic_type = 70; + public const ushort DW_TAG_atomic_type = 71; + public const ushort DW_TAG_call_site = 72; + public const ushort DW_TAG_call_site_parameter = 73; + public const ushort DW_TAG_skeleton_unit = 74; + public const ushort DW_TAG_immutable_type = 75; + + public const ushort DW_FORM_addr = 1; + public const ushort DW_FORM_block2 = 3; + public const ushort DW_FORM_block4 = 4; + public const ushort DW_FORM_data2 = 5; + public const ushort DW_FORM_data4 = 6; + public const ushort DW_FORM_data8 = 7; + public const ushort DW_FORM_string = 8; + public const ushort DW_FORM_block = 9; + public const ushort DW_FORM_block1 = 10; + public const ushort DW_FORM_data1 = 11; + public const ushort DW_FORM_flag = 12; + public const ushort DW_FORM_sdata = 13; + public const ushort DW_FORM_strp = 14; + public const ushort DW_FORM_udata = 15; + public const ushort DW_FORM_ref_addr = 16; + public const ushort DW_FORM_ref1 = 17; + public const ushort DW_FORM_ref2 = 18; + public const ushort DW_FORM_ref4 = 19; + public const ushort DW_FORM_ref8 = 20; + public const ushort DW_FORM_ref_udata = 21; + public const ushort DW_FORM_indirect = 22; + public const ushort DW_FORM_sec_offset = 23; + public const ushort DW_FORM_exprloc = 24; + public const ushort DW_FORM_flag_present = 25; + public const ushort DW_FORM_strx = 26; + public const ushort DW_FORM_addrx = 27; + public const ushort DW_FORM_ref_sup4 = 28; + public const ushort DW_FORM_strp_sup = 29; + public const ushort DW_FORM_data16 = 30; + public const ushort DW_FORM_line_strp = 31; + public const ushort DW_FORM_ref_sig8 = 32; + public const ushort DW_FORM_implicit_const = 33; + public const ushort DW_FORM_loclistx = 34; + public const ushort DW_FORM_rnglistx = 35; + public const ushort DW_FORM_ref_sup8 = 36; + public const ushort DW_FORM_strx1 = 37; + public const ushort DW_FORM_strx2 = 38; + public const ushort DW_FORM_strx3 = 39; + public const ushort DW_FORM_strx4 = 40; + public const ushort DW_FORM_addrx1 = 41; + public const ushort DW_FORM_addrx2 = 42; + public const ushort DW_FORM_addrx3 = 43; + public const ushort DW_FORM_addrx4 = 44; + + public const ushort DW_AT_sibling = 1; + public const ushort DW_AT_location = 2; + public const ushort DW_AT_name = 3; + public const ushort DW_AT_ordering = 9; + public const ushort DW_AT_subscr_data = 10; + public const ushort DW_AT_byte_size = 11; + public const ushort DW_AT_bit_offset = 12; + public const ushort DW_AT_bit_size = 13; + public const ushort DW_AT_element_list = 15; + public const ushort DW_AT_stmt_list = 16; + public const ushort DW_AT_low_pc = 17; + public const ushort DW_AT_high_pc = 18; + public const ushort DW_AT_language = 19; + public const ushort DW_AT_member = 20; + public const ushort DW_AT_discr = 21; + public const ushort DW_AT_discr_value = 22; + public const ushort DW_AT_visibility = 23; + public const ushort DW_AT_import = 24; + public const ushort DW_AT_string_length = 25; + public const ushort DW_AT_common_reference = 26; + public const ushort DW_AT_comp_dir = 27; + public const ushort DW_AT_const_value = 28; + public const ushort DW_AT_containing_type = 29; + public const ushort DW_AT_default_value = 30; + public const ushort DW_AT_inline = 32; + public const ushort DW_AT_is_optional = 33; + public const ushort DW_AT_lower_bound = 34; + public const ushort DW_AT_producer = 37; + public const ushort DW_AT_prototyped = 39; + public const ushort DW_AT_return_addr = 42; + public const ushort DW_AT_start_scope = 44; + public const ushort DW_AT_bit_stride = 46; + public const ushort DW_AT_upper_bound = 47; + public const ushort DW_AT_abstract_origin = 49; + public const ushort DW_AT_accessibility = 50; + public const ushort DW_AT_address_class = 51; + public const ushort DW_AT_artificial = 52; + public const ushort DW_AT_base_types = 53; + public const ushort DW_AT_calling_convention = 54; + public const ushort DW_AT_count = 55; + public const ushort DW_AT_data_member_location = 56; + public const ushort DW_AT_decl_column = 57; + public const ushort DW_AT_decl_file = 58; + public const ushort DW_AT_decl_line = 59; + public const ushort DW_AT_declaration = 60; + public const ushort DW_AT_discr_list = 61; + public const ushort DW_AT_encoding = 62; + public const ushort DW_AT_external = 63; + public const ushort DW_AT_frame_base = 64; + public const ushort DW_AT_friend = 65; + public const ushort DW_AT_identifier_case = 66; + public const ushort DW_AT_macro_info = 67; + public const ushort DW_AT_namelist_item = 68; + public const ushort DW_AT_priority = 69; + public const ushort DW_AT_segment = 70; + public const ushort DW_AT_specification = 71; + public const ushort DW_AT_static_link = 72; + public const ushort DW_AT_type = 73; + public const ushort DW_AT_use_location = 74; + public const ushort DW_AT_variable_parameter = 75; + public const ushort DW_AT_virtuality = 76; + public const ushort DW_AT_vtable_elem_location = 77; + public const ushort DW_AT_allocated = 78; + public const ushort DW_AT_associated = 79; + public const ushort DW_AT_data_location = 80; + public const ushort DW_AT_byte_stride = 81; + public const ushort DW_AT_entry_pc = 82; + public const ushort DW_AT_use_UTF8 = 83; + public const ushort DW_AT_extension = 84; + public const ushort DW_AT_ranges = 85; + public const ushort DW_AT_trampoline = 86; + public const ushort DW_AT_call_column = 87; + public const ushort DW_AT_call_file = 88; + public const ushort DW_AT_call_line = 89; + public const ushort DW_AT_description = 90; + public const ushort DW_AT_binary_scale = 91; + public const ushort DW_AT_decimal_scale = 92; + public const ushort DW_AT_small = 93; + public const ushort DW_AT_decimal_sign = 94; + public const ushort DW_AT_digit_count = 95; + public const ushort DW_AT_picture_string = 96; + public const ushort DW_AT_mutable = 97; + public const ushort DW_AT_threads_scaled = 98; + public const ushort DW_AT_explicit = 99; + public const ushort DW_AT_object_pointer = 100; + public const ushort DW_AT_endianity = 101; + public const ushort DW_AT_elemental = 102; + public const ushort DW_AT_pure = 103; + public const ushort DW_AT_recursive = 104; + public const ushort DW_AT_signature = 105; + public const ushort DW_AT_main_subprogram = 106; + public const ushort DW_AT_data_bit_offset = 107; + public const ushort DW_AT_const_expr = 108; + public const ushort DW_AT_enum_class = 109; + public const ushort DW_AT_linkage_name = 110; + public const ushort DW_AT_string_length_bit_size = 111; + public const ushort DW_AT_string_length_byte_size = 112; + public const ushort DW_AT_rank = 113; + public const ushort DW_AT_str_offsets_base = 114; + public const ushort DW_AT_addr_base = 115; + public const ushort DW_AT_rnglists_base = 116; + public const ushort DW_AT_dwo_id = 117; + public const ushort DW_AT_dwo_name = 118; + public const ushort DW_AT_reference = 119; + public const ushort DW_AT_rvalue_reference = 120; + public const ushort DW_AT_macros = 121; + public const ushort DW_AT_call_all_calls = 122; + public const ushort DW_AT_call_all_source_calls = 123; + public const ushort DW_AT_call_all_tail_calls = 124; + public const ushort DW_AT_call_return_pc = 125; + public const ushort DW_AT_call_value = 126; + public const ushort DW_AT_call_origin = 127; + public const ushort DW_AT_call_parameter = 128; + public const ushort DW_AT_call_pc = 129; + public const ushort DW_AT_call_tail_call = 130; + public const ushort DW_AT_call_target = 131; + public const ushort DW_AT_call_target_clobbered = 132; + public const ushort DW_AT_call_data_location = 133; + public const ushort DW_AT_call_data_value = 134; + public const ushort DW_AT_noreturn = 135; + public const ushort DW_AT_alignment = 136; + public const ushort DW_AT_export_symbols = 137; + public const ushort DW_AT_deleted = 138; + public const ushort DW_AT_defaulted = 139; + public const ushort DW_AT_loclists_base = 140; + + public const byte DW_OP_addr = 3; + public const byte DW_OP_deref = 6; + public const byte DW_OP_const1u = 8; + public const byte DW_OP_const1s = 9; + public const byte DW_OP_const2u = 10; + public const byte DW_OP_const2s = 11; + public const byte DW_OP_const4u = 12; + public const byte DW_OP_const4s = 13; + public const byte DW_OP_const8u = 14; + public const byte DW_OP_const8s = 15; + public const byte DW_OP_constu = 16; + public const byte DW_OP_consts = 17; + public const byte DW_OP_dup = 18; + public const byte DW_OP_drop = 19; + public const byte DW_OP_over = 20; + public const byte DW_OP_pick = 21; + public const byte DW_OP_swap = 22; + public const byte DW_OP_rot = 23; + public const byte DW_OP_xderef = 24; + public const byte DW_OP_abs = 25; + public const byte DW_OP_and = 26; + public const byte DW_OP_div = 27; + public const byte DW_OP_minus = 28; + public const byte DW_OP_mod = 29; + public const byte DW_OP_mul = 30; + public const byte DW_OP_neg = 31; + public const byte DW_OP_not = 32; + public const byte DW_OP_or = 33; + public const byte DW_OP_plus = 34; + public const byte DW_OP_plus_uconst = 35; + public const byte DW_OP_shl = 36; + public const byte DW_OP_shr = 37; + public const byte DW_OP_shra = 38; + public const byte DW_OP_xor = 39; + public const byte DW_OP_bra = 40; + public const byte DW_OP_eq = 41; + public const byte DW_OP_ge = 42; + public const byte DW_OP_gt = 43; + public const byte DW_OP_le = 44; + public const byte DW_OP_lt = 45; + public const byte DW_OP_ne = 46; + public const byte DW_OP_skip = 47; + public const byte DW_OP_lit0 = 48; + public const byte DW_OP_lit1 = 49; + public const byte DW_OP_lit2 = 50; + public const byte DW_OP_lit3 = 51; + public const byte DW_OP_lit4 = 52; + public const byte DW_OP_lit5 = 53; + public const byte DW_OP_lit6 = 54; + public const byte DW_OP_lit7 = 55; + public const byte DW_OP_lit8 = 56; + public const byte DW_OP_lit9 = 57; + public const byte DW_OP_lit10 = 58; + public const byte DW_OP_lit11 = 59; + public const byte DW_OP_lit12 = 60; + public const byte DW_OP_lit13 = 61; + public const byte DW_OP_lit14 = 62; + public const byte DW_OP_lit15 = 63; + public const byte DW_OP_lit16 = 64; + public const byte DW_OP_lit17 = 65; + public const byte DW_OP_lit18 = 66; + public const byte DW_OP_lit19 = 67; + public const byte DW_OP_lit20 = 68; + public const byte DW_OP_lit21 = 69; + public const byte DW_OP_lit22 = 70; + public const byte DW_OP_lit23 = 71; + public const byte DW_OP_lit24 = 72; + public const byte DW_OP_lit25 = 73; + public const byte DW_OP_lit26 = 74; + public const byte DW_OP_lit27 = 75; + public const byte DW_OP_lit28 = 76; + public const byte DW_OP_lit29 = 77; + public const byte DW_OP_lit30 = 78; + public const byte DW_OP_lit31 = 79; + public const byte DW_OP_reg0 = 80; + public const byte DW_OP_reg1 = 81; + public const byte DW_OP_reg2 = 82; + public const byte DW_OP_reg3 = 83; + public const byte DW_OP_reg4 = 84; + public const byte DW_OP_reg5 = 85; + public const byte DW_OP_reg6 = 86; + public const byte DW_OP_reg7 = 87; + public const byte DW_OP_reg8 = 88; + public const byte DW_OP_reg9 = 89; + public const byte DW_OP_reg10 = 90; + public const byte DW_OP_reg11 = 91; + public const byte DW_OP_reg12 = 92; + public const byte DW_OP_reg13 = 93; + public const byte DW_OP_reg14 = 94; + public const byte DW_OP_reg15 = 95; + public const byte DW_OP_reg16 = 96; + public const byte DW_OP_reg17 = 97; + public const byte DW_OP_reg18 = 98; + public const byte DW_OP_reg19 = 99; + public const byte DW_OP_reg20 = 100; + public const byte DW_OP_reg21 = 101; + public const byte DW_OP_reg22 = 102; + public const byte DW_OP_reg23 = 103; + public const byte DW_OP_reg24 = 104; + public const byte DW_OP_reg25 = 105; + public const byte DW_OP_reg26 = 106; + public const byte DW_OP_reg27 = 107; + public const byte DW_OP_reg28 = 108; + public const byte DW_OP_reg29 = 109; + public const byte DW_OP_reg30 = 110; + public const byte DW_OP_reg31 = 111; + public const byte DW_OP_breg0 = 112; + public const byte DW_OP_breg1 = 113; + public const byte DW_OP_breg2 = 114; + public const byte DW_OP_breg3 = 115; + public const byte DW_OP_breg4 = 116; + public const byte DW_OP_breg5 = 117; + public const byte DW_OP_breg6 = 118; + public const byte DW_OP_breg7 = 119; + public const byte DW_OP_breg8 = 120; + public const byte DW_OP_breg9 = 121; + public const byte DW_OP_breg10 = 122; + public const byte DW_OP_breg11 = 123; + public const byte DW_OP_breg12 = 124; + public const byte DW_OP_breg13 = 125; + public const byte DW_OP_breg14 = 126; + public const byte DW_OP_breg15 = 127; + public const byte DW_OP_breg16 = 128; + public const byte DW_OP_breg17 = 129; + public const byte DW_OP_breg18 = 130; + public const byte DW_OP_breg19 = 131; + public const byte DW_OP_breg20 = 132; + public const byte DW_OP_breg21 = 133; + public const byte DW_OP_breg22 = 134; + public const byte DW_OP_breg23 = 135; + public const byte DW_OP_breg24 = 136; + public const byte DW_OP_breg25 = 137; + public const byte DW_OP_breg26 = 138; + public const byte DW_OP_breg27 = 139; + public const byte DW_OP_breg28 = 140; + public const byte DW_OP_breg29 = 141; + public const byte DW_OP_breg30 = 142; + public const byte DW_OP_breg31 = 143; + public const byte DW_OP_regx = 144; + public const byte DW_OP_fbreg = 145; + public const byte DW_OP_bregx = 146; + public const byte DW_OP_piece = 147; + public const byte DW_OP_deref_size = 148; + public const byte DW_OP_xderef_size = 149; + public const byte DW_OP_nop = 150; + public const byte DW_OP_push_object_address = 151; + public const byte DW_OP_call2 = 152; + public const byte DW_OP_call4 = 153; + public const byte DW_OP_call_ref = 154; + public const byte DW_OP_form_tls_address = 155; + public const byte DW_OP_call_frame_cfa = 156; + public const byte DW_OP_bit_piece = 157; + public const byte DW_OP_implicit_value = 158; + public const byte DW_OP_stack_value = 159; + public const byte DW_OP_implicit_pointer = 160; + public const byte DW_OP_addrx = 161; + public const byte DW_OP_constx = 162; + public const byte DW_OP_entry_value = 163; + public const byte DW_OP_const_type = 164; + public const byte DW_OP_regval_type = 165; + public const byte DW_OP_deref_type = 166; + public const byte DW_OP_xderef_type = 167; + public const byte DW_OP_convert = 168; + public const byte DW_OP_reinterpret = 169; + + public const byte DW_CHILDREN_no = 0; + public const byte DW_CHILDREN_yes = 1; + + public const byte DW_LNS_copy = 1; + public const byte DW_LNS_advance_pc = 2; + public const byte DW_LNS_advance_line = 3; + public const byte DW_LNS_set_file = 4; + public const byte DW_LNS_set_column = 5; + public const byte DW_LNS_negate_stmt = 6; + public const byte DW_LNS_set_basic_block = 7; + public const byte DW_LNS_const_add_pc = 8; + public const byte DW_LNS_fixed_advance_pc = 9; + public const byte DW_LNS_set_prologue_end = 10; + public const byte DW_LNS_set_epilogue_begin = 11; + public const byte DW_LNS_set_isa = 12; + + public const byte DW_LNE_end_sequence = 1; + public const byte DW_LNE_set_address = 2; + public const byte DW_LNE_define_file = 3; + public const byte DW_LNE_set_discriminator = 4; + + public const byte DW_UT_compile = 1; + public const byte DW_UT_type = 2; + public const byte DW_UT_partial = 3; + public const byte DW_UT_skeleton = 4; + public const byte DW_UT_split_compile = 5; + public const byte DW_UT_split_type = 6; + + public const ushort DW_LANG_C89 = 1; + public const ushort DW_LANG_C = 2; + public const ushort DW_LANG_Ada83 = 3; + public const ushort DW_LANG_C_plus_plus = 4; + public const ushort DW_LANG_Cobol74 = 5; + public const ushort DW_LANG_Cobol85 = 6; + public const ushort DW_LANG_Fortran77 = 7; + public const ushort DW_LANG_Fortran90 = 8; + public const ushort DW_LANG_Pascal83 = 9; + public const ushort DW_LANG_Modula2 = 10; + public const ushort DW_LANG_Java = 11; + public const ushort DW_LANG_C99 = 12; + public const ushort DW_LANG_Ada95 = 13; + public const ushort DW_LANG_Fortran95 = 14; + public const ushort DW_LANG_PLI = 15; + public const ushort DW_LANG_ObjC = 16; + public const ushort DW_LANG_ObjC_plus_plus = 17; + public const ushort DW_LANG_UPC = 18; + public const ushort DW_LANG_D = 19; + public const ushort DW_LANG_Python = 20; + public const ushort DW_LANG_OpenCL = 21; + public const ushort DW_LANG_Go = 22; + public const ushort DW_LANG_Modula3 = 23; + public const ushort DW_LANG_Haskel = 24; + public const ushort DW_LANG_C_plus_plus_03 = 25; + public const ushort DW_LANG_C_plus_plus_11 = 26; + public const ushort DW_LANG_OCaml = 27; + public const ushort DW_LANG_Rust = 28; + public const ushort DW_LANG_C11 = 29; + public const ushort DW_LANG_Swift = 30; + public const ushort DW_LANG_Julia = 31; + public const ushort DW_LANG_Dylan = 32; + public const ushort DW_LANG_C_plus_plus_14 = 33; + public const ushort DW_LANG_Fortran03 = 34; + public const ushort DW_LANG_Fortran08 = 35; + public const ushort DW_LANG_RenderScript = 36; + public const ushort DW_LANG_BLISS = 37; + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/ElfNative.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/ElfNative.cs new file mode 100644 index 0000000000000..b39253a02e854 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/ElfNative.cs @@ -0,0 +1,437 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +namespace ILCompiler.ObjectWriter +{ + /// + /// Native constants for the ELF file format. + /// + internal static class ElfNative + { + // ELF version + public const byte EV_CURRENT = 1; + + // File type + public const ushort ET_REL = 1; + + // File bitness + public const byte ELFCLASS32 = 1; + public const byte ELFCLASS64 = 2; + + // File endianness + public const byte ELFDATA2LSB = 1; + + // Architecture + public const ushort EM_386 = 3; + public const ushort EM_ARM = 40; + public const ushort EM_X86_64 = 62; + public const ushort EM_AARCH64 = 183; + + // Section header type + public const uint SHT_NULL = 0; + public const uint SHT_PROGBITS = 1; + public const uint SHT_SYMTAB = 2; + public const uint SHT_STRTAB = 3; + public const uint SHT_RELA = 4; + public const uint SHT_HASH = 5; + public const uint SHT_DYNAMIC = 6; + public const uint SHT_NOTE = 7; + public const uint SHT_NOBITS = 8; + public const uint SHT_REL = 9; + public const uint SHT_SHLIB = 10; + public const uint SHT_DYNSYM = 11; + public const uint SHT_INIT_ARRAY = 14; + public const uint SHT_FINI_ARRAY = 15; + public const uint SHT_PREINIT_ARRAY = 16; + public const uint SHT_GROUP = 17; + public const uint SHT_SYMTAB_SHNDX = 18; + public const uint SHT_IA_64_UNWIND = 1879048193; + + // Section header flags + public const uint SHF_WRITE = 1; + public const uint SHF_ALLOC = 2; + public const uint SHF_EXECINSTR = 4; + public const uint SHF_MERGE = 16; + public const uint SHF_STRINGS = 32; + public const uint SHF_INFO_LINK = 64; + public const uint SHF_LINK_ORDER = 128; + public const uint SHF_OS_NONCONFORMING = 256; + public const uint SHF_GROUP = 512; + public const uint SHF_TLS = 1024; + public const uint SHF_COMPRESSED = 2048; + + // Section header special index numbers + public const uint SHN_UNDEF = 0; + public const uint SHN_LORESERVE = 65280; + public const uint SHN_XINDEX = 65535; + + // Section group type + public const uint GRP_COMDAT = 1; + + // Symbol type + public const byte STT_NOTYPE = 0; + public const byte STT_OBJECT = 1; + public const byte STT_FUNC = 2; + public const byte STT_SECTION = 3; + public const byte STT_FILE = 4; + public const byte STT_COMMON = 5; + public const byte STT_TLS = 6; + + // Symbol visibility + public const byte STV_DEFAULT = 0; + public const byte STV_INTERNAL = 1; + public const byte STV_HIDDEN = 2; + public const byte STV_PROTECTED = 3; + + // Symbol binding + public const byte STB_LOCAL = 0; + public const byte STB_GLOBAL = 1; + public const byte STB_WEAK = 2; + + // Relocations (x86) + public const uint R_386_NONE = 0; + public const uint R_386_32 = 1; + public const uint R_386_PC32 = 2; + public const uint R_386_GOT32 = 3; + public const uint R_386_PLT32 = 4; + public const uint R_386_COPY = 5; + public const uint R_386_GLOB_DAT = 6; + public const uint R_386_JMP_SLOT = 7; + public const uint R_386_RELATIVE = 8; + public const uint R_386_GOTOFF = 9; + public const uint R_386_GOTPC = 10; + public const uint R_386_32PLT = 11; + public const uint R_386_TLS_TPOFF = 14; + public const uint R_386_TLS_IE = 15; + public const uint R_386_TLS_GOTIE = 16; + public const uint R_386_TLS_LE = 17; + public const uint R_386_TLS_GD = 18; + public const uint R_386_TLS_LDM = 19; + public const uint R_386_16 = 20; + public const uint R_386_PC16 = 21; + public const uint R_386_8 = 22; + public const uint R_386_PC8 = 23; + public const uint R_386_TLS_GD_32 = 24; + public const uint R_386_TLS_GD_PUSH = 25; + public const uint R_386_TLS_GD_CALL = 26; + public const uint R_386_TLS_GD_POP = 27; + public const uint R_386_TLS_LDM_32 = 28; + public const uint R_386_TLS_LDM_PUSH = 29; + public const uint R_386_TLS_LDM_CALL = 30; + public const uint R_386_TLS_LDM_POP = 31; + public const uint R_386_TLS_LDO_32 = 32; + public const uint R_386_TLS_IE_32 = 33; + public const uint R_386_TLS_LE_32 = 34; + public const uint R_386_TLS_DTPMOD32 = 35; + public const uint R_386_TLS_DTPOFF32 = 36; + public const uint R_386_TLS_TPOFF32 = 37; + public const uint R_386_SIZE32 = 38; + public const uint R_386_TLS_GOTDESC = 39; + public const uint R_386_TLS_DESC_CALL = 40; + public const uint R_386_TLS_DESC = 41; + public const uint R_386_IRELATIVE = 42; + + // Relocations (x64) + public const uint R_X86_64_NONE = 0; + public const uint R_X86_64_64 = 1; + public const uint R_X86_64_PC32 = 2; + public const uint R_X86_64_GOT32 = 3; + public const uint R_X86_64_PLT32 = 4; + public const uint R_X86_64_COPY = 5; + public const uint R_X86_64_GLOB_DAT = 6; + public const uint R_X86_64_JUMP_SLOT = 7; + public const uint R_X86_64_RELATIVE = 8; + public const uint R_X86_64_GOTPCREL = 9; + public const uint R_X86_64_32 = 10; + public const uint R_X86_64_32S = 11; + public const uint R_X86_64_16 = 12; + public const uint R_X86_64_PC16 = 13; + public const uint R_X86_64_8 = 14; + public const uint R_X86_64_PC8 = 15; + public const uint R_X86_64_DTPMOD64 = 16; + public const uint R_X86_64_DTPOFF64 = 17; + public const uint R_X86_64_TPOFF64 = 18; + public const uint R_X86_64_TLSGD = 19; + public const uint R_X86_64_TLSLD = 20; + public const uint R_X86_64_DTPOFF32 = 21; + public const uint R_X86_64_GOTTPOFF = 22; + public const uint R_X86_64_TPOFF32 = 23; + public const uint R_X86_64_PC64 = 24; + public const uint R_X86_64_GOTOFF64 = 25; + public const uint R_X86_64_GOTPC32 = 26; + public const uint R_X86_64_GOT64 = 27; + public const uint R_X86_64_GOTPCREL64 = 28; + public const uint R_X86_64_GOTPC64 = 29; + public const uint R_X86_64_GOTPLT64 = 30; + public const uint R_X86_64_PLTOFF64 = 31; + public const uint R_X86_64_SIZE32 = 32; + public const uint R_X86_64_SIZE64 = 33; + public const uint R_X86_64_GOTPC32_TLSDESC = 34; + public const uint R_X86_64_TLSDESC_CALL = 35; + public const uint R_X86_64_TLSDESC = 36; + public const uint R_X86_64_IRELATIVE = 37; + public const uint R_X86_64_RELATIVE64 = 38; + + // Relocations (arm32) + public const uint R_ARM_NONE = 0; + public const uint R_ARM_PC24 = 1; + public const uint R_ARM_ABS32 = 2; + public const uint R_ARM_REL32 = 3; + public const uint R_ARM_PC13 = 4; + public const uint R_ARM_ABS16 = 5; + public const uint R_ARM_ABS12 = 6; + public const uint R_ARM_THM_ABS5 = 7; + public const uint R_ARM_ABS8 = 8; + public const uint R_ARM_SBREL32 = 9; + public const uint R_ARM_THM_PC22 = 10; + public const uint R_ARM_THM_PC8 = 11; + public const uint R_ARM_AMP_VCALL9 = 12; + public const uint R_ARM_SWI24 = 13; + public const uint R_ARM_TLS_DESC = 13; + public const uint R_ARM_THM_SWI8 = 14; + public const uint R_ARM_XPC25 = 15; + public const uint R_ARM_THM_XPC22 = 16; + public const uint R_ARM_TLS_DTPMOD32 = 17; + public const uint R_ARM_TLS_DTPOFF32 = 18; + public const uint R_ARM_TLS_TPOFF32 = 19; + public const uint R_ARM_COPY = 20; + public const uint R_ARM_GLOB_DAT = 21; + public const uint R_ARM_JUMP_SLOT = 22; + public const uint R_ARM_RELATIVE = 23; + public const uint R_ARM_GOTOFF = 24; + public const uint R_ARM_GOTPC = 25; + public const uint R_ARM_GOT32 = 26; + public const uint R_ARM_PLT32 = 27; + public const uint R_ARM_CALL = 28; + public const uint R_ARM_JUMP24 = 29; + public const uint R_ARM_THM_JUMP24 = 30; + public const uint R_ARM_BASE_ABS = 31; + public const uint R_ARM_ALU_PCREL_7_0 = 32; + public const uint R_ARM_ALU_PCREL_15_8 = 33; + public const uint R_ARM_ALU_PCREL_23_15 = 34; + public const uint R_ARM_LDR_SBREL_11_0 = 35; + public const uint R_ARM_ALU_SBREL_19_12 = 36; + public const uint R_ARM_ALU_SBREL_27_20 = 37; + public const uint R_ARM_TARGET1 = 38; + public const uint R_ARM_SBREL31 = 39; + public const uint R_ARM_V4BX = 40; + public const uint R_ARM_TARGET2 = 41; + public const uint R_ARM_PREL31 = 42; + public const uint R_ARM_MOVW_ABS_NC = 43; + public const uint R_ARM_MOVT_ABS = 44; + public const uint R_ARM_MOVW_PREL_NC = 45; + public const uint R_ARM_MOVT_PREL = 46; + public const uint R_ARM_THM_MOVW_ABS_NC = 47; + public const uint R_ARM_THM_MOVT_ABS = 48; + public const uint R_ARM_THM_MOVW_PREL_NC = 49; + public const uint R_ARM_THM_MOVT_PREL = 50; + public const uint R_ARM_THM_JUMP19 = 51; + public const uint R_ARM_THM_JUMP6 = 52; + public const uint R_ARM_THM_ALU_PREL_11_0 = 53; + public const uint R_ARM_THM_PC12 = 54; + public const uint R_ARM_ABS32_NOI = 55; + public const uint R_ARM_REL32_NOI = 56; + public const uint R_ARM_ALU_PC_G0_NC = 57; + public const uint R_ARM_ALU_PC_G0 = 58; + public const uint R_ARM_ALU_PC_G1_NC = 59; + public const uint R_ARM_ALU_PC_G1 = 60; + public const uint R_ARM_ALU_PC_G2 = 61; + public const uint R_ARM_LDR_PC_G1 = 62; + public const uint R_ARM_LDR_PC_G2 = 63; + public const uint R_ARM_LDRS_PC_G0 = 64; + public const uint R_ARM_LDRS_PC_G1 = 65; + public const uint R_ARM_LDRS_PC_G2 = 66; + public const uint R_ARM_LDC_PC_G0 = 67; + public const uint R_ARM_LDC_PC_G1 = 68; + public const uint R_ARM_LDC_PC_G2 = 69; + public const uint R_ARM_ALU_SB_G0_NC = 70; + public const uint R_ARM_ALU_SB_G0 = 71; + public const uint R_ARM_ALU_SB_G1_NC = 72; + public const uint R_ARM_ALU_SB_G1 = 73; + public const uint R_ARM_ALU_SB_G2 = 74; + public const uint R_ARM_LDR_SB_G0 = 75; + public const uint R_ARM_LDR_SB_G1 = 76; + public const uint R_ARM_LDR_SB_G2 = 77; + public const uint R_ARM_LDRS_SB_G0 = 78; + public const uint R_ARM_LDRS_SB_G1 = 79; + public const uint R_ARM_LDRS_SB_G2 = 80; + public const uint R_ARM_LDC_SB_G0 = 81; + public const uint R_ARM_LDC_SB_G1 = 82; + public const uint R_ARM_LDC_SB_G2 = 83; + public const uint R_ARM_MOVW_BREL_NC = 84; + public const uint R_ARM_MOVT_BREL = 85; + public const uint R_ARM_MOVW_BREL = 86; + public const uint R_ARM_THM_MOVW_BREL_NC = 87; + public const uint R_ARM_THM_MOVT_BREL = 88; + public const uint R_ARM_THM_MOVW_BREL = 89; + public const uint R_ARM_TLS_GOTDESC = 90; + public const uint R_ARM_TLS_CALL = 91; + public const uint R_ARM_TLS_DESCSEQ = 92; + public const uint R_ARM_THM_TLS_CALL = 93; + public const uint R_ARM_PLT32_ABS = 94; + public const uint R_ARM_GOT_ABS = 95; + public const uint R_ARM_GOT_PREL = 96; + public const uint R_ARM_GOT_BREL12 = 97; + public const uint R_ARM_GOTOFF12 = 98; + public const uint R_ARM_GOTRELAX = 99; + public const uint R_ARM_GNU_VTENTRY = 100; + public const uint R_ARM_GNU_VTINHERIT = 101; + public const uint R_ARM_THM_PC11 = 102; + public const uint R_ARM_THM_PC9 = 103; + public const uint R_ARM_TLS_GD32 = 104; + public const uint R_ARM_TLS_LDM32 = 105; + public const uint R_ARM_TLS_LDO32 = 106; + public const uint R_ARM_TLS_IE32 = 107; + public const uint R_ARM_TLS_LE32 = 108; + public const uint R_ARM_TLS_LDO12 = 109; + public const uint R_ARM_TLS_LE12 = 110; + public const uint R_ARM_TLS_IE12GP = 111; + public const uint R_ARM_ME_TOO = 128; + public const uint R_ARM_THM_TLS_DESCSEQ = 129; + public const uint R_ARM_THM_TLS_DESCSEQ16 = 129; + public const uint R_ARM_THM_TLS_DESCSEQ32 = 130; + public const uint R_ARM_THM_GOT_BREL12 = 131; + public const uint R_ARM_IRELATIVE = 160; + public const uint R_ARM_RXPC25 = 249; + public const uint R_ARM_RSBREL32 = 250; + public const uint R_ARM_THM_RPC22 = 251; + public const uint R_ARM_RREL32 = 252; + public const uint R_ARM_RABS22 = 253; + public const uint R_ARM_RPC24 = 254; + public const uint R_ARM_RBASE = 255; + + // Relocations (arm64) + public const uint R_AARCH64_NONE = 0; + public const uint R_AARCH64_P32_ABS32 = 1; + public const uint R_AARCH64_P32_COPY = 180; + public const uint R_AARCH64_P32_GLOB_DAT = 181; + public const uint R_AARCH64_P32_JUMP_SLOT = 182; + public const uint R_AARCH64_P32_RELATIVE = 183; + public const uint R_AARCH64_P32_TLS_DTPMOD = 184; + public const uint R_AARCH64_P32_TLS_DTPREL = 185; + public const uint R_AARCH64_P32_TLS_TPREL = 186; + public const uint R_AARCH64_P32_TLSDESC = 187; + public const uint R_AARCH64_P32_IRELATIVE = 188; + public const uint R_AARCH64_ABS64 = 257; + public const uint R_AARCH64_ABS32 = 258; + public const uint R_AARCH64_ABS16 = 259; + public const uint R_AARCH64_PREL64 = 260; + public const uint R_AARCH64_PREL32 = 261; + public const uint R_AARCH64_PREL16 = 262; + public const uint R_AARCH64_MOVW_UABS_G0 = 263; + public const uint R_AARCH64_MOVW_UABS_G0_NC = 264; + public const uint R_AARCH64_MOVW_UABS_G1 = 265; + public const uint R_AARCH64_MOVW_UABS_G1_NC = 266; + public const uint R_AARCH64_MOVW_UABS_G2 = 267; + public const uint R_AARCH64_MOVW_UABS_G2_NC = 268; + public const uint R_AARCH64_MOVW_UABS_G3 = 269; + public const uint R_AARCH64_MOVW_SABS_G0 = 270; + public const uint R_AARCH64_MOVW_SABS_G1 = 271; + public const uint R_AARCH64_MOVW_SABS_G2 = 272; + public const uint R_AARCH64_LD_PREL_LO19 = 273; + public const uint R_AARCH64_ADR_PREL_LO21 = 274; + public const uint R_AARCH64_ADR_PREL_PG_HI21 = 275; + public const uint R_AARCH64_ADR_PREL_PG_HI21_NC = 276; + public const uint R_AARCH64_ADD_ABS_LO12_NC = 277; + public const uint R_AARCH64_LDST8_ABS_LO12_NC = 278; + public const uint R_AARCH64_TSTBR14 = 279; + public const uint R_AARCH64_CONDBR19 = 280; + public const uint R_AARCH64_JUMP26 = 282; + public const uint R_AARCH64_CALL26 = 283; + public const uint R_AARCH64_LDST16_ABS_LO12_NC = 284; + public const uint R_AARCH64_LDST32_ABS_LO12_NC = 285; + public const uint R_AARCH64_LDST64_ABS_LO12_NC = 286; + public const uint R_AARCH64_MOVW_PREL_G0 = 287; + public const uint R_AARCH64_MOVW_PREL_G0_NC = 288; + public const uint R_AARCH64_MOVW_PREL_G1 = 289; + public const uint R_AARCH64_MOVW_PREL_G1_NC = 290; + public const uint R_AARCH64_MOVW_PREL_G2 = 291; + public const uint R_AARCH64_MOVW_PREL_G2_NC = 292; + public const uint R_AARCH64_MOVW_PREL_G3 = 293; + public const uint R_AARCH64_LDST128_ABS_LO12_NC = 299; + public const uint R_AARCH64_MOVW_GOTOFF_G0 = 300; + public const uint R_AARCH64_MOVW_GOTOFF_G0_NC = 301; + public const uint R_AARCH64_MOVW_GOTOFF_G1 = 302; + public const uint R_AARCH64_MOVW_GOTOFF_G1_NC = 303; + public const uint R_AARCH64_MOVW_GOTOFF_G2 = 304; + public const uint R_AARCH64_MOVW_GOTOFF_G2_NC = 305; + public const uint R_AARCH64_MOVW_GOTOFF_G3 = 306; + public const uint R_AARCH64_GOTREL64 = 307; + public const uint R_AARCH64_GOTREL32 = 308; + public const uint R_AARCH64_GOT_LD_PREL19 = 309; + public const uint R_AARCH64_LD64_GOTOFF_LO15 = 310; + public const uint R_AARCH64_ADR_GOT_PAGE = 311; + public const uint R_AARCH64_LD64_GOT_LO12_NC = 312; + public const uint R_AARCH64_LD64_GOTPAGE_LO15 = 313; + public const uint R_AARCH64_TLSGD_ADR_PREL21 = 512; + public const uint R_AARCH64_TLSGD_ADR_PAGE21 = 513; + public const uint R_AARCH64_TLSGD_ADD_LO12_NC = 514; + public const uint R_AARCH64_TLSGD_MOVW_G1 = 515; + public const uint R_AARCH64_TLSGD_MOVW_G0_NC = 516; + public const uint R_AARCH64_TLSLD_ADR_PREL21 = 517; + public const uint R_AARCH64_TLSLD_ADR_PAGE21 = 518; + public const uint R_AARCH64_TLSLD_ADD_LO12_NC = 519; + public const uint R_AARCH64_TLSLD_MOVW_G1 = 520; + public const uint R_AARCH64_TLSLD_MOVW_G0_NC = 521; + public const uint R_AARCH64_TLSLD_LD_PREL19 = 522; + public const uint R_AARCH64_TLSLD_MOVW_DTPREL_G2 = 523; + public const uint R_AARCH64_TLSLD_MOVW_DTPREL_G1 = 524; + public const uint R_AARCH64_TLSLD_MOVW_DTPREL_G1_NC = 525; + public const uint R_AARCH64_TLSLD_MOVW_DTPREL_G0 = 526; + public const uint R_AARCH64_TLSLD_MOVW_DTPREL_G0_NC = 527; + public const uint R_AARCH64_TLSLD_ADD_DTPREL_HI12 = 528; + public const uint R_AARCH64_TLSLD_ADD_DTPREL_LO12 = 529; + public const uint R_AARCH64_TLSLD_ADD_DTPREL_LO12_NC = 530; + public const uint R_AARCH64_TLSLD_LDST8_DTPREL_LO12 = 531; + public const uint R_AARCH64_TLSLD_LDST8_DTPREL_LO12_NC = 532; + public const uint R_AARCH64_TLSLD_LDST16_DTPREL_LO12 = 533; + public const uint R_AARCH64_TLSLD_LDST16_DTPREL_LO12_NC = 534; + public const uint R_AARCH64_TLSLD_LDST32_DTPREL_LO12 = 535; + public const uint R_AARCH64_TLSLD_LDST32_DTPREL_LO12_NC = 536; + public const uint R_AARCH64_TLSLD_LDST64_DTPREL_LO12 = 537; + public const uint R_AARCH64_TLSLD_LDST64_DTPREL_LO12_NC = 538; + public const uint R_AARCH64_TLSIE_MOVW_GOTTPREL_G1 = 539; + public const uint R_AARCH64_TLSIE_MOVW_GOTTPREL_G0_NC = 540; + public const uint R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21 = 541; + public const uint R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC = 542; + public const uint R_AARCH64_TLSIE_LD_GOTTPREL_PREL19 = 543; + public const uint R_AARCH64_TLSLE_MOVW_TPREL_G2 = 544; + public const uint R_AARCH64_TLSLE_MOVW_TPREL_G1 = 545; + public const uint R_AARCH64_TLSLE_MOVW_TPREL_G1_NC = 546; + public const uint R_AARCH64_TLSLE_MOVW_TPREL_G0 = 547; + public const uint R_AARCH64_TLSLE_MOVW_TPREL_G0_NC = 548; + public const uint R_AARCH64_TLSLE_ADD_TPREL_HI12 = 549; + public const uint R_AARCH64_TLSLE_ADD_TPREL_LO12 = 550; + public const uint R_AARCH64_TLSLE_ADD_TPREL_LO12_NC = 551; + public const uint R_AARCH64_TLSLE_LDST8_TPREL_LO12 = 552; + public const uint R_AARCH64_TLSLE_LDST8_TPREL_LO12_NC = 553; + public const uint R_AARCH64_TLSLE_LDST16_TPREL_LO12 = 554; + public const uint R_AARCH64_TLSLE_LDST16_TPREL_LO12_NC = 555; + public const uint R_AARCH64_TLSLE_LDST32_TPREL_LO12 = 556; + public const uint R_AARCH64_TLSLE_LDST32_TPREL_LO12_NC = 557; + public const uint R_AARCH64_TLSLE_LDST64_TPREL_LO12 = 558; + public const uint R_AARCH64_TLSLE_LDST64_TPREL_LO12_NC = 559; + public const uint R_AARCH64_TLSDESC_LD_PREL19 = 560; + public const uint R_AARCH64_TLSDESC_ADR_PREL21 = 561; + public const uint R_AARCH64_TLSDESC_ADR_PAGE21 = 562; + public const uint R_AARCH64_TLSDESC_LD64_LO12 = 563; + public const uint R_AARCH64_TLSDESC_ADD_LO12 = 564; + public const uint R_AARCH64_TLSDESC_OFF_G1 = 565; + public const uint R_AARCH64_TLSDESC_OFF_G0_NC = 566; + public const uint R_AARCH64_TLSDESC_LDR = 567; + public const uint R_AARCH64_TLSDESC_ADD = 568; + public const uint R_AARCH64_TLSDESC_CALL = 569; + public const uint R_AARCH64_TLSLE_LDST128_TPREL_LO12 = 570; + public const uint R_AARCH64_TLSLE_LDST128_TPREL_LO12_NC = 571; + public const uint R_AARCH64_TLSLD_LDST128_DTPREL_LO12 = 572; + public const uint R_AARCH64_TLSLD_LDST128_DTPREL_LO12_NC = 573; + public const uint R_AARCH64_COPY = 1024; + public const uint R_AARCH64_GLOB_DAT = 1025; + public const uint R_AARCH64_JUMP_SLOT = 1026; + public const uint R_AARCH64_RELATIVE = 1027; + public const uint R_AARCH64_TLS_DTPMOD = 1028; + public const uint R_AARCH64_TLS_DTPREL = 1029; + public const uint R_AARCH64_TLS_TPREL = 1030; + public const uint R_AARCH64_TLSDESC = 1031; + public const uint R_AARCH64_IRELATIVE = 1032; + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/ElfObjectWriter.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/ElfObjectWriter.cs new file mode 100644 index 0000000000000..00648d5d7b897 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/ElfObjectWriter.cs @@ -0,0 +1,842 @@ +// 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.Diagnostics; +using System.Buffers.Binary; +using System.Numerics; +using ILCompiler.DependencyAnalysis; +using ILCompiler.DependencyAnalysisFramework; +using Internal.TypeSystem; +using static ILCompiler.DependencyAnalysis.RelocType; +using static ILCompiler.ObjectWriter.ElfNative; + +namespace ILCompiler.ObjectWriter +{ + /// + /// ELF object file format writer for Linux/Unix targets. + /// + /// + /// 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 accomodate the section indexes that don't fit within the regular + /// section number field. + /// + internal sealed class ElfObjectWriter : UnixObjectWriter + { + private readonly ushort _machine; + private readonly List _sections = new(); + private readonly List _symbols = new(); + private uint _localSymbolCount; + private readonly Dictionary _comdatNameToElfSection = new(StringComparer.Ordinal); + + // Symbol table + private readonly Dictionary _symbolNameToIndex = new(); + + public ElfObjectWriter(NodeFactory factory, ObjectWritingOptions options) + : base(factory, options) + { + _machine = factory.Target.Architecture switch + { + TargetArchitecture.X86 => EM_386, + TargetArchitecture.X64 => EM_X86_64, + TargetArchitecture.ARM => EM_ARM, + TargetArchitecture.ARM64 => EM_AARCH64, + _ => throw new NotSupportedException("Unsupported architecture") + }; + + // By convention the symbol table starts with empty symbol + _symbols.Add(new ElfSymbol {}); + } + + private protected override void CreateSection(ObjectNodeSection section, string comdatName, string symbolName, Stream sectionStream) + { + string sectionName = + section.Name == "rdata" ? ".rodata" : + (section.Name.StartsWith('_') || section.Name.StartsWith('.') ? section.Name : "." + section.Name); + int sectionIndex = _sections.Count; + uint type = 0; + uint flags = 0; + ElfSectionDefinition groupSection = null; + + if (section.Type == SectionType.Uninitialized) + { + type = SHT_NOBITS; + flags = SHF_ALLOC | SHF_WRITE; + } + else if (section == ObjectNodeSection.TLSSection) + { + type = SHT_PROGBITS; + flags = SHF_ALLOC | SHF_WRITE | SHF_TLS; + } + else + { + type = section.Name == ".eh_frame" && _machine == EM_X86_64 ? SHT_IA_64_UNWIND : SHT_PROGBITS; + flags = section.Type switch + { + SectionType.Executable => SHF_ALLOC | SHF_EXECINSTR, + SectionType.Writeable => SHF_ALLOC | SHF_WRITE, + SectionType.Debug => sectionName == ".debug_str" ? SHF_MERGE | SHF_STRINGS : 0, + _ => SHF_ALLOC, + }; + } + + if (comdatName is not null) + { + flags |= SHF_GROUP; + if (!_comdatNameToElfSection.TryGetValue(comdatName, out groupSection)) + { + Span tempBuffer = stackalloc byte[sizeof(uint)]; + groupSection = new ElfSectionDefinition + { + SectionHeader = new ElfSectionHeader + { + Type = SHT_GROUP, + Alignment = 4, + EntrySize = (uint)sizeof(uint), + }, + Name = ".group", + Stream = new MemoryStream(5 * sizeof(uint)), + }; + + // Write group flags + BinaryPrimitives.WriteUInt32LittleEndian(tempBuffer, GRP_COMDAT); + groupSection.Stream.Write(tempBuffer); + + _comdatNameToElfSection.Add(comdatName, groupSection); + } + } + + _sections.Add(new ElfSectionDefinition + { + SectionHeader = new ElfSectionHeader + { + Type = type, + Flags = flags, + }, + Name = sectionName, + Stream = sectionStream, + GroupSection = groupSection, + }); + + // Emit section symbol into symbol table (for COMDAT the defining symbol is section symbol) + if (comdatName is null) + { + _symbolNameToIndex[sectionName] = (uint)_symbols.Count; + _symbols.Add(new ElfSymbol + { + Section = _sections[sectionIndex], + Info = STT_SECTION, + }); + } + + base.CreateSection(section, comdatName, symbolName ?? sectionName, sectionStream); + } + + protected internal override void UpdateSectionAlignment(int sectionIndex, int alignment) + { + ElfSectionDefinition elfSection = _sections[sectionIndex]; + elfSection.SectionHeader.Alignment = Math.Max(elfSection.SectionHeader.Alignment, (ulong)alignment); + } + + protected internal override unsafe void EmitRelocation( + int sectionIndex, + long offset, + Span data, + RelocType relocType, + string symbolName, + long addend) + { + // We read the addend from the data and clear it. This is necessary + // to produce correct addends in the `.rela` sections which override + // the destination with the addend from relocation table. + fixed (byte *pData = data) + { + long inlineValue = Relocation.ReadValue(relocType, (void*)pData); + if (inlineValue != 0) + { + addend += inlineValue; + Relocation.WriteValue(relocType, (void*)pData, 0); + } + } + + base.EmitRelocation(sectionIndex, offset, data, relocType, symbolName, addend); + } + + private protected override void EmitSymbolTable( + IDictionary definedSymbols, + SortedSet undefinedSymbols) + { + List sortedSymbols = new(definedSymbols.Count + undefinedSymbols.Count); + foreach ((string name, SymbolDefinition definition) in definedSymbols) + { + var section = _sections[definition.SectionIndex]; + var type = + (section.SectionHeader.Flags & SHF_TLS) == SHF_TLS ? STT_TLS : + definition.Size > 0 ? STT_FUNC : STT_NOTYPE; + sortedSymbols.Add(new ElfSymbol + { + Name = name, + Value = (ulong)definition.Value, + Size = (ulong)definition.Size, + Section = _sections[definition.SectionIndex], + Info = (byte)(type | (STB_GLOBAL << 4)), + Other = definition.Global ? STV_DEFAULT : STV_HIDDEN, + }); + } + + foreach (string externSymbol in undefinedSymbols) + { + if (!_symbolNameToIndex.ContainsKey(externSymbol)) + { + sortedSymbols.Add(new ElfSymbol + { + Name = externSymbol, + Info = (STB_GLOBAL << 4), + }); + } + } + + sortedSymbols.Sort((symA, symB) => string.CompareOrdinal(symA.Name, symB.Name)); + _localSymbolCount = (uint)_symbols.Count; + _symbols.AddRange(sortedSymbols); + uint symbolIndex = _localSymbolCount; + foreach (ElfSymbol definedSymbol in sortedSymbols) + { + _symbolNameToIndex[definedSymbol.Name] = symbolIndex; + symbolIndex++; + } + + // Update group sections links + foreach ((string comdatName, ElfSectionDefinition groupSection) in _comdatNameToElfSection) + { + groupSection.SectionHeader.Info = (uint)_symbolNameToIndex[comdatName]; + } + } + + private protected override void EmitRelocations(int sectionIndex, List relocationList) + { + switch (_machine) + { + case EM_386: + EmitRelocationsX86(sectionIndex, relocationList); + break; + case EM_X86_64: + EmitRelocationsX64(sectionIndex, relocationList); + break; + case EM_ARM: + EmitRelocationsARM(sectionIndex, relocationList); + break; + case EM_AARCH64: + EmitRelocationsARM64(sectionIndex, relocationList); + break; + default: + Debug.Fail("Unsupported architecture"); + break; + } + } + + private void EmitRelocationsX86(int sectionIndex, List relocationList) + { + // TODO: We are emitting .rela sections on x86 which is technically wrong. We should be + // using .rel sections with the addend embedded in the data. Since x86 is not an officially + // supported platform this is left for future enhancement. + if (relocationList.Count > 0) + { + Span relocationEntry = stackalloc byte[12]; + var relocationStream = new MemoryStream(12 * relocationList.Count); + _sections[sectionIndex].RelocationStream = relocationStream; + foreach (SymbolicRelocation symbolicRelocation in relocationList) + { + uint symbolIndex = _symbolNameToIndex[symbolicRelocation.SymbolName]; + uint type = symbolicRelocation.Type switch + { + IMAGE_REL_BASED_HIGHLOW => R_386_32, + IMAGE_REL_BASED_RELPTR32 => R_386_PC32, + IMAGE_REL_BASED_REL32 => R_386_PLT32, + IMAGE_REL_TLSGD => R_386_TLS_GD, + IMAGE_REL_TPOFF => R_386_TLS_TPOFF, + _ => throw new NotSupportedException("Unknown relocation type: " + symbolicRelocation.Type) + }; + + long addend = symbolicRelocation.Addend; + if (symbolicRelocation.Type == IMAGE_REL_BASED_REL32) + { + addend -= 4; + } + + BinaryPrimitives.WriteUInt32LittleEndian(relocationEntry, (uint)symbolicRelocation.Offset); + BinaryPrimitives.WriteUInt32LittleEndian(relocationEntry.Slice(4), ((uint)symbolIndex << 8) | type); + BinaryPrimitives.WriteInt32LittleEndian(relocationEntry.Slice(8), (int)addend); + relocationStream.Write(relocationEntry); + } + } + } + + private void EmitRelocationsX64(int sectionIndex, List relocationList) + { + if (relocationList.Count > 0) + { + Span relocationEntry = stackalloc byte[24]; + var relocationStream = new MemoryStream(24 * relocationList.Count); + _sections[sectionIndex].RelocationStream = relocationStream; + foreach (SymbolicRelocation symbolicRelocation in relocationList) + { + uint symbolIndex = _symbolNameToIndex[symbolicRelocation.SymbolName]; + uint type = symbolicRelocation.Type switch + { + IMAGE_REL_BASED_HIGHLOW => R_X86_64_32, + IMAGE_REL_BASED_DIR64 => R_X86_64_64, + IMAGE_REL_BASED_RELPTR32 => R_X86_64_PC32, + IMAGE_REL_BASED_REL32 => R_X86_64_PLT32, + IMAGE_REL_TLSGD => R_X86_64_TLSGD, + IMAGE_REL_TPOFF => R_X86_64_TPOFF32, + _ => throw new NotSupportedException("Unknown relocation type: " + symbolicRelocation.Type) + }; + + long addend = symbolicRelocation.Addend; + if (symbolicRelocation.Type == IMAGE_REL_BASED_REL32) + { + addend -= 4; + } + + BinaryPrimitives.WriteUInt64LittleEndian(relocationEntry, (ulong)symbolicRelocation.Offset); + BinaryPrimitives.WriteUInt64LittleEndian(relocationEntry.Slice(8), ((ulong)symbolIndex << 32) | type); + BinaryPrimitives.WriteInt64LittleEndian(relocationEntry.Slice(16), addend); + relocationStream.Write(relocationEntry); + } + } + } + + private void EmitRelocationsARM(int sectionIndex, List relocationList) + { + if (relocationList.Count > 0) + { + Span relocationEntry = stackalloc byte[12]; + var relocationStream = new MemoryStream(12 * relocationList.Count); + _sections[sectionIndex].RelocationStream = relocationStream; + foreach (SymbolicRelocation symbolicRelocation in relocationList) + { + uint symbolIndex = _symbolNameToIndex[symbolicRelocation.SymbolName]; + uint type = symbolicRelocation.Type switch + { + IMAGE_REL_BASED_HIGHLOW => R_ARM_ABS32, + IMAGE_REL_BASED_RELPTR32 => R_ARM_REL32, + IMAGE_REL_BASED_REL32 => R_ARM_REL32, + IMAGE_REL_BASED_THUMB_MOV32 => R_ARM_THM_MOVW_ABS_NC, + IMAGE_REL_BASED_THUMB_MOV32_PCREL => R_ARM_THM_MOVW_PREL_NC, + IMAGE_REL_BASED_THUMB_BRANCH24 => R_ARM_THM_PC22, + _ => throw new NotSupportedException("Unknown relocation type: " + symbolicRelocation.Type) + }; + + long addend = symbolicRelocation.Addend; + if (symbolicRelocation.Type == IMAGE_REL_BASED_REL32) + { + addend -= 4; + } + + BinaryPrimitives.WriteUInt32LittleEndian(relocationEntry, (uint)symbolicRelocation.Offset); + BinaryPrimitives.WriteUInt32LittleEndian(relocationEntry.Slice(4), ((uint)symbolIndex << 8) | type); + BinaryPrimitives.WriteInt32LittleEndian(relocationEntry.Slice(8), (int)addend); + relocationStream.Write(relocationEntry); + + if (symbolicRelocation.Type is IMAGE_REL_BASED_THUMB_MOV32 or IMAGE_REL_BASED_THUMB_MOV32_PCREL) + { + BinaryPrimitives.WriteUInt32LittleEndian(relocationEntry, (uint)(symbolicRelocation.Offset + 4)); + BinaryPrimitives.WriteUInt32LittleEndian(relocationEntry.Slice(4), ((uint)symbolIndex << 8) | (type + 1)); + relocationStream.Write(relocationEntry); + } + } + } + } + + private void EmitRelocationsARM64(int sectionIndex, List relocationList) + { + if (relocationList.Count > 0) + { + Span relocationEntry = stackalloc byte[24]; + var relocationStream = new MemoryStream(24 * relocationList.Count); + _sections[sectionIndex].RelocationStream = relocationStream; + foreach (SymbolicRelocation symbolicRelocation in relocationList) + { + uint symbolIndex = _symbolNameToIndex[symbolicRelocation.SymbolName]; + uint type = symbolicRelocation.Type switch + { + IMAGE_REL_BASED_DIR64 => R_AARCH64_ABS64, + IMAGE_REL_BASED_HIGHLOW => R_AARCH64_ABS32, + IMAGE_REL_BASED_RELPTR32 => R_AARCH64_PREL32, + IMAGE_REL_BASED_ARM64_BRANCH26 => R_AARCH64_CALL26, + IMAGE_REL_BASED_ARM64_PAGEBASE_REL21 => R_AARCH64_ADR_PREL_PG_HI21, + IMAGE_REL_BASED_ARM64_PAGEOFFSET_12A => R_AARCH64_ADD_ABS_LO12_NC, + IMAGE_REL_AARCH64_TLSLE_ADD_TPREL_HI12 => R_AARCH64_TLSLE_ADD_TPREL_HI12, + IMAGE_REL_AARCH64_TLSLE_ADD_TPREL_LO12_NC => R_AARCH64_TLSLE_ADD_TPREL_LO12_NC, + IMAGE_REL_AARCH64_TLSDESC_ADR_PAGE21 => R_AARCH64_TLSDESC_ADR_PAGE21, + IMAGE_REL_AARCH64_TLSDESC_LD64_LO12 => R_AARCH64_TLSDESC_LD64_LO12, + IMAGE_REL_AARCH64_TLSDESC_ADD_LO12 => R_AARCH64_TLSDESC_ADD_LO12, + IMAGE_REL_AARCH64_TLSDESC_CALL => R_AARCH64_TLSDESC_CALL, + _ => throw new NotSupportedException("Unknown relocation type: " + symbolicRelocation.Type) + }; + + BinaryPrimitives.WriteUInt64LittleEndian(relocationEntry, (ulong)symbolicRelocation.Offset); + BinaryPrimitives.WriteUInt64LittleEndian(relocationEntry.Slice(8), ((ulong)symbolIndex << 32) | type); + BinaryPrimitives.WriteInt64LittleEndian(relocationEntry.Slice(16), symbolicRelocation.Addend); + relocationStream.Write(relocationEntry); + } + } + } + + private protected override void EmitObjectFile(string objectFilePath) + { + using var outputFileStream = new FileStream(objectFilePath, FileMode.Create); + switch (_machine) + { + case EM_386: + case EM_ARM: + EmitObjectFile(outputFileStream); + break; + default: + EmitObjectFile(outputFileStream); + break; + } + } + + private void EmitObjectFile(FileStream outputFileStream) + where TSize : struct, IBinaryInteger + { + ElfStringTable _stringTable = new(); + uint sectionCount = 1; // NULL section + bool hasSymTabExtendedIndices = false; + Span tempBuffer = stackalloc byte[sizeof(uint)]; + + // Merge the group sections at the end of the section lists + _sections.AddRange(_comdatNameToElfSection.Values); + + // Add marker for non-executable stack + _sections.Add(new ElfSectionDefinition + { + SectionHeader = new ElfSectionHeader { Type = SHT_PROGBITS }, + Name = ".note.GNU-stack", + Stream = Stream.Null, + }); + + // Reserve all symbol names + foreach (var symbol in _symbols) + { + if (symbol.Name is not null) + { + _stringTable.ReserveString(symbol.Name); + } + } + + // Layout the section content in the output file + ulong currentOffset = (ulong)ElfHeader.GetSize(); + foreach (var section in _sections) + { + _stringTable.ReserveString(section.Name); + + if (section.SectionHeader.Alignment > 0) + { + currentOffset = (ulong)((currentOffset + (ulong)section.SectionHeader.Alignment - 1) & ~(ulong)(section.SectionHeader.Alignment - 1)); + } + + // Update section layout + section.SectionIndex = (uint)sectionCount; + section.SectionHeader.Offset = currentOffset; + section.SectionHeader.Size = (ulong)section.Stream.Length; + + if (section.SectionHeader.Type != SHT_NOBITS) + { + currentOffset += (ulong)section.Stream.Length; + } + currentOffset += (ulong)section.RelocationStream.Length; + sectionCount++; + + if (section.RelocationStream != Stream.Null) + { + _stringTable.ReserveString(".rela" + section.Name); + sectionCount++; + } + + // Write the section index into the section's group. We store all the groups + // at the end so we can modify their contents in this loop safely. + if (section.GroupSection is not null) + { + BinaryPrimitives.WriteUInt32LittleEndian(tempBuffer, section.SectionIndex); + section.GroupSection.Stream.Write(tempBuffer); + } + } + + // Reserve names for the predefined sections + _stringTable.ReserveString(".strtab"); + _stringTable.ReserveString(".symtab"); + if (sectionCount >= SHN_LORESERVE) + { + _stringTable.ReserveString(".symtab_shndx"); + hasSymTabExtendedIndices = true; + } + + // Layout the string and symbol table + uint strTabSectionIndex = sectionCount; + currentOffset += _stringTable.Size; + sectionCount++; + uint symTabSectionIndex = sectionCount; + currentOffset += (ulong)(_symbols.Count * ElfSymbol.GetSize()); + sectionCount++; + if (hasSymTabExtendedIndices) + { + currentOffset += (ulong)(_symbols.Count * sizeof(uint)); + sectionCount++; + } + + // Update group section links + foreach (ElfSectionDefinition groupSection in _comdatNameToElfSection.Values) + { + groupSection.SectionHeader.Link = symTabSectionIndex; + } + + // Write the ELF file header + ElfHeader elfHeader = new ElfHeader + { + Type = ET_REL, + Machine = _machine, + Version = EV_CURRENT, + SegmentHeaderEntrySize = 0x38, + SectionHeaderOffset = currentOffset, + SectionHeaderEntrySize = (ushort)ElfSectionHeader.GetSize(), + SectionHeaderEntryCount = sectionCount < SHN_LORESERVE ? (ushort)sectionCount : (ushort)0u, + StringTableIndex = strTabSectionIndex < SHN_LORESERVE ? (ushort)strTabSectionIndex : (ushort)SHN_XINDEX, + }; + elfHeader.Write(outputFileStream); + + // Write the section contents and relocations + foreach (var section in _sections) + { + if (section.SectionHeader.Type != SHT_NOBITS) + { + outputFileStream.Position = (long)section.SectionHeader.Offset; + section.Stream.Position = 0; + section.Stream.CopyTo(outputFileStream); + if (section.RelocationStream != Stream.Null) + { + section.RelocationStream.Position = 0; + section.RelocationStream.CopyTo(outputFileStream); + } + } + } + + // Write the string and symbol table contents + ulong stringTableOffset = (ulong)outputFileStream.Position; + _stringTable.Write(outputFileStream); + + ulong symbolTableOffset = (ulong)outputFileStream.Position; + foreach (var symbol in _symbols) + { + symbol.Write(outputFileStream, _stringTable); + } + + ulong symbolTableExtendedIndicesOffset = (ulong)outputFileStream.Position; + if (hasSymTabExtendedIndices) + { + foreach (var symbol in _symbols) + { + uint index = symbol.Section?.SectionIndex ?? 0; + BinaryPrimitives.WriteUInt32LittleEndian(tempBuffer, index >= SHN_LORESERVE ? index : 0); + outputFileStream.Write(tempBuffer); + } + } + + // Finally, write the section headers + + // Null section + ElfSectionHeader nullSectionHeader = new ElfSectionHeader + { + NameIndex = 0, + Type = SHT_NULL, + Flags = 0u, + Address = 0u, + Offset = 0u, + Size = sectionCount >= SHN_LORESERVE ? sectionCount : 0u, + Link = strTabSectionIndex >= SHN_LORESERVE ? strTabSectionIndex : 0u, + Info = 0u, + Alignment = 0u, + EntrySize = 0u, + }; + nullSectionHeader.Write(outputFileStream); + + // User sections and their relocations + foreach (var section in _sections) + { + section.SectionHeader.NameIndex = _stringTable.GetStringOffset(section.Name); + section.SectionHeader.Write(outputFileStream); + + if (section.SectionHeader.Type != SHT_NOBITS && + section.RelocationStream != Stream.Null) + { + ElfSectionHeader relaSectionHeader = new ElfSectionHeader + { + NameIndex = _stringTable.GetStringOffset(".rela" + section.Name), + Type = SHT_RELA, + Flags = (section.GroupSection is not null ? SHF_GROUP : 0u) | SHF_INFO_LINK, + Address = 0u, + Offset = section.SectionHeader.Offset + section.SectionHeader.Size, + Size = (ulong)section.RelocationStream.Length, + Link = symTabSectionIndex, + Info = section.SectionIndex, + Alignment = 8u, + EntrySize = (ulong)default(TSize).GetByteCount() * 3u, + }; + relaSectionHeader.Write(outputFileStream); + } + } + + // String table section + ElfSectionHeader stringTableSectionHeader = new ElfSectionHeader + { + NameIndex = _stringTable.GetStringOffset(".strtab"), + Type = SHT_STRTAB, + Flags = 0u, + Address = 0u, + Offset = stringTableOffset, + Size = _stringTable.Size, + Link = 0u, + Info = 0u, + Alignment = 0u, + EntrySize = 0u, + }; + stringTableSectionHeader.Write(outputFileStream); + + // Symbol table section + ElfSectionHeader symbolTableSectionHeader = new ElfSectionHeader + { + NameIndex = _stringTable.GetStringOffset(".symtab"), + Type = SHT_SYMTAB, + Flags = 0u, + Address = 0u, + Offset = symbolTableOffset, + Size = (ulong)(_symbols.Count * ElfSymbol.GetSize()), + Link = strTabSectionIndex, + Info = _localSymbolCount, + Alignment = 0u, + EntrySize = (uint)ElfSymbol.GetSize(), + }; + symbolTableSectionHeader.Write(outputFileStream); + + // If the symbol table has references to sections with indexes higher than + // SHN_LORESERVE (0xFF00) we need to write them down in a separate table + // in the .symtab_shndx section. + if (hasSymTabExtendedIndices) + { + ElfSectionHeader sectionHeader = new ElfSectionHeader + { + NameIndex = _stringTable.GetStringOffset(".symtab_shndx"), + Type = SHT_SYMTAB_SHNDX, + Flags = 0u, + Address = 0u, + Offset = symbolTableExtendedIndicesOffset, + Size = (ulong)(_symbols.Count * sizeof(uint)), + Link = symTabSectionIndex, + Info = 0u, + Alignment = 0u, + EntrySize = (uint)sizeof(uint), + }; + sectionHeader.Write(outputFileStream); + } + } + + private sealed class ElfSectionDefinition + { + public required ElfSectionHeader SectionHeader { get; init; } + public uint SectionIndex { get; set; } + public required string Name { get; init; } + public required Stream Stream { get; init; } + public Stream RelocationStream { get; set; } = Stream.Null; + public ElfSectionDefinition GroupSection { get; init; } + } + + private sealed class ElfHeader + { + private static ReadOnlySpan Magic => new byte[] { 0x7F, 0x45, 0x4C, 0x46 }; + + public ushort Type { get; set; } + public ushort Machine { get; set; } + public uint Version { get; set; } + public ulong EntryPoint { get; set; } + public ulong SegmentHeaderOffset { get; set; } + public ulong SectionHeaderOffset { get; set; } + public uint Flags { get; set; } + public ushort SegmentHeaderEntrySize { get; set; } + public ushort SegmentHeaderEntryCount { get; set; } + public ushort SectionHeaderEntrySize { get; set; } + public ushort SectionHeaderEntryCount { get; set; } + public ushort StringTableIndex { get; set; } + + public static int GetSize() + where TSize : struct, IBinaryInteger + { + return + Magic.Length + + 1 + // Class + 1 + // Endianness + 1 + // Header version + 1 + // ABI + 1 + // ABI version + 7 + // Padding + sizeof(ushort) + // Type + sizeof(ushort) + // Machine + sizeof(uint) + // Version + default(TSize).GetByteCount() + // Entry point + default(TSize).GetByteCount() + // Segment header offset + default(TSize).GetByteCount() + // Section header offset + sizeof(uint) + // Flags + sizeof(ushort) + // ELF Header size + sizeof(ushort) + // Segment header entry size + sizeof(ushort) + // Segment header entry count + sizeof(ushort) + // Section header entry size + sizeof(ushort) + // Section header entry count + sizeof(ushort); // String table index + } + + public void Write(FileStream stream) + where TSize : struct, IBinaryInteger + { + Span buffer = stackalloc byte[GetSize()]; + + buffer.Clear(); + Magic.CopyTo(buffer.Slice(0, Magic.Length)); + buffer[4] = typeof(TSize) == typeof(uint) ? ELFCLASS32 : ELFCLASS64; + buffer[5] = ELFDATA2LSB; + buffer[6] = 1; + var tempBuffer = buffer.Slice(16); + tempBuffer = tempBuffer.Slice(((IBinaryInteger)Type).WriteLittleEndian(tempBuffer)); + tempBuffer = tempBuffer.Slice(((IBinaryInteger)Machine).WriteLittleEndian(tempBuffer)); + tempBuffer = tempBuffer.Slice(((IBinaryInteger)Version).WriteLittleEndian(tempBuffer)); + tempBuffer = tempBuffer.Slice(TSize.CreateChecked(EntryPoint).WriteLittleEndian(tempBuffer)); + tempBuffer = tempBuffer.Slice(TSize.CreateChecked(SegmentHeaderOffset).WriteLittleEndian(tempBuffer)); + tempBuffer = tempBuffer.Slice(TSize.CreateChecked(SectionHeaderOffset).WriteLittleEndian(tempBuffer)); + BinaryPrimitives.WriteUInt32LittleEndian(tempBuffer, Flags); + BinaryPrimitives.WriteUInt16LittleEndian(tempBuffer.Slice(4), (ushort)buffer.Length); + BinaryPrimitives.WriteUInt16LittleEndian(tempBuffer.Slice(6), SegmentHeaderEntrySize); + BinaryPrimitives.WriteUInt16LittleEndian(tempBuffer.Slice(8), SegmentHeaderEntryCount); + BinaryPrimitives.WriteUInt16LittleEndian(tempBuffer.Slice(10), SectionHeaderEntrySize); + BinaryPrimitives.WriteUInt16LittleEndian(tempBuffer.Slice(12), SectionHeaderEntryCount); + BinaryPrimitives.WriteUInt16LittleEndian(tempBuffer.Slice(14), StringTableIndex); + + stream.Write(buffer); + } + } + + private sealed class ElfSectionHeader + { + public uint NameIndex { get; set; } + public uint Type { get; set; } + public ulong Flags { get; set; } + public ulong Address { get; set; } + public ulong Offset { get; set; } + public ulong Size { get; set; } + public uint Link { get; set; } + public uint Info { get; set; } + public ulong Alignment { get; set; } + public ulong EntrySize { get; set; } + + public static int GetSize() + where TSize : struct, IBinaryInteger + { + return + sizeof(uint) + // Name index + sizeof(uint) + // Type + default(TSize).GetByteCount() + // Flags + default(TSize).GetByteCount() + // Address + default(TSize).GetByteCount() + // Offset + default(TSize).GetByteCount() + // Size + sizeof(uint) + // Link + sizeof(uint) + // Info + default(TSize).GetByteCount() + // Alignment + default(TSize).GetByteCount(); // Entry size + } + + public void Write(FileStream stream) + where TSize : struct, IBinaryInteger + { + Span buffer = stackalloc byte[GetSize()]; + var tempBuffer = buffer; + + tempBuffer = tempBuffer.Slice(((IBinaryInteger)NameIndex).WriteLittleEndian(tempBuffer)); + tempBuffer = tempBuffer.Slice(((IBinaryInteger)Type).WriteLittleEndian(tempBuffer)); + tempBuffer = tempBuffer.Slice(TSize.CreateChecked(Flags).WriteLittleEndian(tempBuffer)); + tempBuffer = tempBuffer.Slice(TSize.CreateChecked(Address).WriteLittleEndian(tempBuffer)); + tempBuffer = tempBuffer.Slice(TSize.CreateChecked(Offset).WriteLittleEndian(tempBuffer)); + tempBuffer = tempBuffer.Slice(TSize.CreateChecked(Size).WriteLittleEndian(tempBuffer)); + tempBuffer = tempBuffer.Slice(((IBinaryInteger)Link).WriteLittleEndian(tempBuffer)); + tempBuffer = tempBuffer.Slice(((IBinaryInteger)Info).WriteLittleEndian(tempBuffer)); + tempBuffer = tempBuffer.Slice(TSize.CreateChecked(Alignment).WriteLittleEndian(tempBuffer)); + tempBuffer = tempBuffer.Slice(TSize.CreateChecked(EntrySize).WriteLittleEndian(tempBuffer)); + + stream.Write(buffer); + } + } + + private sealed class ElfSymbol + { + public string Name { get; init; } + public ulong Value { get; init; } + public ulong Size { get; init; } + public ElfSectionDefinition Section { get; init; } + public byte Info { get; init; } + public byte Other { get; init; } + + public static int GetSize() + where TSize : struct, IBinaryInteger + { + return typeof(TSize) == typeof(uint) ? 16 : 24; + } + + public void Write(FileStream stream, ElfStringTable stringTable) + where TSize : struct, IBinaryInteger + { + Span buffer = stackalloc byte[GetSize()]; + ushort sectionIndex; + + sectionIndex = Section is { SectionIndex: >= SHN_LORESERVE } ? + (ushort)SHN_XINDEX : + (Section is not null ? (ushort)Section.SectionIndex : (ushort)0u); + + BinaryPrimitives.WriteUInt32LittleEndian(buffer, Name is not null ? stringTable.GetStringOffset(Name) : 0); + if (typeof(TSize) == typeof(uint)) + { + TSize.CreateChecked(Value).WriteLittleEndian(buffer.Slice(4)); + TSize.CreateChecked(Size).WriteLittleEndian(buffer.Slice(8)); + buffer[12] = Info; + buffer[13] = Other; + BinaryPrimitives.WriteUInt16LittleEndian(buffer.Slice(14), sectionIndex); + } + else + { + buffer[4] = Info; + buffer[5] = Other; + BinaryPrimitives.WriteUInt16LittleEndian(buffer.Slice(6), sectionIndex); + BinaryPrimitives.WriteUInt64LittleEndian(buffer.Slice(8), Value); + BinaryPrimitives.WriteUInt64LittleEndian(buffer.Slice(16), Size); + } + + stream.Write(buffer); + } + } + + private sealed class ElfStringTable : StringTableBuilder + { + public ElfStringTable() + { + // Always start the table with empty string + GetStringOffset(""); + } + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ObjectWriter.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/LegacyObjectWriter.cs similarity index 98% rename from src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ObjectWriter.cs rename to src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/LegacyObjectWriter.cs index 2a7fbfda34405..0a77e36b43302 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ObjectWriter.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/LegacyObjectWriter.cs @@ -7,6 +7,7 @@ using System.Diagnostics; using System.Runtime.InteropServices; +using ILCompiler.DependencyAnalysis; using ILCompiler.DependencyAnalysisFramework; using Internal.Text; @@ -15,12 +16,12 @@ using Internal.JitInterface; using ObjectData = ILCompiler.DependencyAnalysis.ObjectNode.ObjectData; -namespace ILCompiler.DependencyAnalysis +namespace ILCompiler.ObjectWriter { /// /// Object writer using src/Native/ObjWriter /// - public class ObjectWriter : IDisposable, ITypesDebugInfoWriter + internal sealed class LegacyObjectWriter : IDisposable, ITypesDebugInfoWriter { private readonly ObjectWritingOptions _options; @@ -213,6 +214,7 @@ private static extern void EmitWinFrameInfo(IntPtr objWriter, byte[] methodName, byte[] blobSymbolName); public void EmitWinFrameInfo(int startOffset, int endOffset, int blobSize, byte[] blobSymbolName) { + _ = blobSize; EmitWinFrameInfo(_nativeObjectWriter, _currentNodeZeroTerminatedName.UnderlyingArray, startOffset, endOffset, blobSymbolName); } @@ -616,7 +618,7 @@ private bool TryGetCompactUnwindEncoding(byte[] blob, out uint encoding) return false; } - public void BuildCFIMap(NodeFactory factory, ObjectNode node) + public void BuildCFIMap(ObjectNode node) { _offsetToCfis.Clear(); _offsetToCfiStart.Clear(); @@ -906,7 +908,7 @@ public void EmitSymbolDefinition(int currentOffset) private IntPtr _nativeObjectWriter = IntPtr.Zero; - public ObjectWriter(string objectFilePath, NodeFactory factory, ObjectWritingOptions options) + public LegacyObjectWriter(string objectFilePath, NodeFactory factory, ObjectWritingOptions options) { var triple = GetLLVMTripleFromTarget(factory.Target); @@ -932,7 +934,7 @@ public void Dispose() Dispose(true); } - public virtual void Dispose(bool bDisposing) + public void Dispose(bool bDisposing) { if (_nativeObjectWriter != IntPtr.Zero) { @@ -949,7 +951,7 @@ public virtual void Dispose(bool bDisposing) } } - ~ObjectWriter() + ~LegacyObjectWriter() { Dispose(false); } @@ -1004,7 +1006,7 @@ public void ResetByteRunInterruptionOffsets(ObjectData nodeContents) public static void EmitObject(string objectFilePath, IReadOnlyCollection nodes, NodeFactory factory, ObjectWritingOptions options, IObjectDumper dumper, Logger logger) { - ObjectWriter objectWriter = new ObjectWriter(objectFilePath, factory, options); + LegacyObjectWriter objectWriter = new LegacyObjectWriter(objectFilePath, factory, options); bool succeeded = false; try @@ -1088,7 +1090,7 @@ public static void EmitObject(string objectFilePath, IReadOnlyCollection + /// Native constants for the Mach-O file format. + /// + internal static class MachNative + { + public const uint MH_MAGIC_64 = 0xFEEDFACF; + + // File type + public const uint MH_OBJECT = 0x1; + + // File header flags + public const uint MH_SUBSECTIONS_VIA_SYMBOLS = 0x2000; + + // CPU type/subtype + public const uint CPU_TYPE_ARM64 = 0x1000000 | 12; + public const uint CPU_TYPE_X86_64 = 0x1000000 | 7; + public const uint CPU_SUBTYPE_ARM64_ALL = 0; + public const uint CPU_SUBTYPE_X86_64_ALL = 3; + + // Load command types + public const uint LC_SYMTAB = 0x2; + public const uint LC_DYSYMTAB = 0xB; + public const uint LC_SEGMENT_64 = 0x19; + public const uint LC_BUILD_VERSION = 0x32; + + // Memory protection + public const uint VM_PROT_NONE = 0; + public const uint VM_PROT_READ = 1; + public const uint VM_PROT_WRITE = 2; + public const uint VM_PROT_EXECUTE = 4; + + // Section types + public const uint S_REGULAR = 0; + public const uint S_ZEROFILL = 1; + public const uint S_CSTRING_LITERALS = 2; + public const uint S_4BYTE_LITERALS = 3; + public const uint S_LITERAL_POINTERS = 5; + public const uint S_NON_LAZY_SYMBOL_POINTERS = 6; + public const uint S_LAZY_SYMBOL_POINTERS = 7; + public const uint S_SYMBOL_STUBS = 8; + public const uint S_MOD_INIT_FUNC_POINTERS = 9; + public const uint S_MOD_TERM_FUNC_POINTERS = 10; + public const uint S_COALESCED = 11; + public const uint S_GB_ZEROFILL = 12; + public const uint S_INTERPOSING = 13; + public const uint S_16BYTE_LITERALS = 14; + public const uint S_DTRACE_DOF = 15; + public const uint S_LAZY_DYLIB_SYMBOL_POINTERS = 16; + public const uint S_THREAD_LOCAL_REGULAR = 17; + public const uint S_THREAD_LOCAL_ZEROFILL = 18; + public const uint S_THREAD_LOCAL_VARIABLES = 19; + public const uint S_THREAD_LOCAL_VARIABLE_POINTERS = 20; + public const uint S_THREAD_LOCAL_INIT_FUNCTION_POINTERS = 21; + public const uint S_INIT_FUNC_OFFSETS = 22; + + // Section flags + public const uint S_ATTR_SOME_INSTRUCTIONS = 0x400; + public const uint S_ATTR_DEBUG = 0x2000000; + public const uint S_ATTR_LIVE_SUPPORT = 0x8000000; + public const uint S_ATTR_NO_DEAD_STRIP = 0x10000000; + public const uint S_ATTR_STRIP_STATIC_SYMS = 0x20000000; + public const uint S_ATTR_NO_TOC = 0x40000000; + public const uint S_ATTR_PURE_INSTRUCTIONS = 0x80000000; + + // Relocation types + public const byte X86_64_RELOC_UNSIGNED = 0; + public const byte X86_64_RELOC_SIGNED = 1; + public const byte X86_64_RELOC_BRANCH = 2; + public const byte X86_64_RELOC_GOT_LOAD = 3; + public const byte X86_64_RELOC_GOT = 4; + public const byte X86_64_RELOC_SUBTRACTOR = 5; + public const byte X86_64_RELOC_SIGNED_1 = 6; + public const byte X86_64_RELOC_SIGNED_2 = 7; + public const byte X86_64_RELOC_SIGNED_4 = 8; + public const byte X86_64_RELOC_TLV = 9; + public const byte ARM64_RELOC_UNSIGNED = 0; + public const byte ARM64_RELOC_SUBTRACTOR = 1; + public const byte ARM64_RELOC_BRANCH26 = 2; + public const byte ARM64_RELOC_PAGE21 = 3; + public const byte ARM64_RELOC_PAGEOFF12 = 4; + public const byte ARM64_RELOC_GOT_LOAD_PAGE21 = 5; + public const byte ARM64_RELOC_GOT_LOAD_PAGEOFF12 = 6; + public const byte ARM64_RELOC_POINTER_TO_GOT = 7; + public const byte ARM64_RELOC_TLVP_LOAD_PAGE21 = 8; + public const byte ARM64_RELOC_TLVP_LOAD_PAGEOFF12 = 9; + public const byte ARM64_RELOC_ADDEND = 10; + + // Symbol type + public const byte N_UNDF = 0; + public const byte N_EXT = 1; + public const byte N_ABS = 2; + public const byte N_INDR = 0xA; + public const byte N_SECT = 0xE; + public const byte N_PBUD = 0xC; + + // Symbol descriptor flags + public const ushort REFERENCE_FLAG_UNDEFINED_NON_LAZY = 0; + public const ushort REFERENCE_FLAG_UNDEFINED_LAZY = 1; + public const ushort REFERENCE_FLAG_DEFINED = 2; + public const ushort REFERENCE_FLAG_PRIVATE_DEFINED = 3; + public const ushort REFERENCE_FLAG_PRIVATE_UNDEFINED_NON_LAZY = 4; + public const ushort REFERENCE_FLAG_PRIVATE_UNDEFINED_LAZY = 5; + public const ushort REFERENCED_DYNAMICALLY = 0x10; + public const ushort N_NO_DEAD_STRIP = 0x20; + public const ushort N_WEAK_REF = 0x40; + public const ushort N_WEAK_DEF = 0x80; + + public const uint PLATFORM_MACOS = 1; + public const uint PLATFORM_IOS = 2; + public const uint PLATFORM_TVOS = 3; + public const uint PLATFORM_WATCHOS = 4; + public const uint PLATFORM_BRIDGEOS = 5; + public const uint PLATFORM_MACCATALYST = 6; + public const uint PLATFORM_IOSSIMULATOR = 7; + public const uint PLATFORM_TVOSSIMULATOR = 8; + public const uint PLATFORM_WATCHOSSIMULATOR = 9; + public const uint PLATFORM_DRIVERKIT = 10; + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/MachObjectWriter.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/MachObjectWriter.cs new file mode 100644 index 0000000000000..86ac433262f99 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/MachObjectWriter.cs @@ -0,0 +1,1081 @@ +// 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.Globalization; +using System.IO; +using System.Linq; +using System.Numerics; +using System.Text; +using ILCompiler.DependencyAnalysis; +using ILCompiler.DependencyAnalysisFramework; +using Internal.TypeSystem; +using static ILCompiler.DependencyAnalysis.RelocType; +using static ILCompiler.ObjectWriter.MachNative; + +namespace ILCompiler.ObjectWriter +{ + /// + /// Mach-O object file format writer for Apple macOS and iOS-like targets. + /// + /// + /// Old version of the Mach-O file format specification is mirrored at + /// https://github.com/aidansteele/osx-abi-macho-file-format-reference. + /// + /// There are some notable differences when compared to ELF or COFF: + /// - The maximum number of sections in object file is limited to 255. + /// - Sections are subdivided by their symbols and treated by the + /// linker as subsections (often referred to as atoms by the linker). + /// + /// The consequences of these design decisions is the COMDAT sections are + /// modeled in entirely different way. Dead code elimination works on the + /// atom level, so relative relocations within the same section have to be + /// preserved. + /// + /// Debug information uses the standard DWARF format. It is, however, not + /// linked into the intermediate executable files. Instead the linker creates + /// a map between the final executable and the object files. Debuggers like + /// lldb then use this map to read the debug information from the object + /// file directly. As a consequence the DWARF information is not generated + /// with relocations for the DWARF sections themselves since it's never + /// needed. + /// + /// While Mach-O uses the DWARF exception handling information for unwind + /// tables it also supports a compact representation for common prolog types. + /// Unofficial reference of the format can be found at + /// https://faultlore.com/blah/compact-unwinding/. It's necessary to emit + /// at least the stub entries pointing to the DWARF information but due + /// to limits in the linked file format it's advisable to use the compact + /// encoding whenever possible. + /// + /// The Apple linker is extremely picky in which relocation types are allowed + /// inside the DWARF sections, both for debugging and exception handling. + /// + internal sealed class MachObjectWriter : UnixObjectWriter + { + private sealed record CompactUnwindCode(string PcStartSymbolName, uint PcLength, uint Code, string LsdaSymbolName = null, string PersonalitySymbolName = null); + + private readonly TargetOS _targetOS; + private readonly uint _cpuType; + private readonly uint _cpuSubType; + private readonly List _sections = new(); + + // Exception handling sections + private MachSection _compactUnwindSection; + private MemoryStream _compactUnwindStream; + private readonly List _compactUnwindCodes = new(); + private readonly uint _compactUnwindDwarfCode; + + // Symbol table + private readonly Dictionary _symbolNameToIndex = new(); + private readonly List _symbolTable = new(); + private readonly MachDynamicLinkEditSymbolTable _dySymbolTable = new(); + + public MachObjectWriter(NodeFactory factory, ObjectWritingOptions options) + : base(factory, options) + { + switch (factory.Target.Architecture) + { + case TargetArchitecture.ARM64: + _cpuType = CPU_TYPE_ARM64; + _cpuSubType = CPU_SUBTYPE_ARM64_ALL; + _compactUnwindDwarfCode = 0x3_00_00_00u; + break; + case TargetArchitecture.X64: + _cpuType = CPU_TYPE_X86_64; + _cpuSubType = CPU_SUBTYPE_X86_64_ALL; + _compactUnwindDwarfCode = 0x4_00_00_00u; + break; + default: + throw new NotSupportedException("Unsupported architecture"); + } + + _targetOS = factory.Target.OperatingSystem; + } + + private protected override void EmitSectionsAndLayout() + { + // Layout sections. At this point we don't really care if the file offsets are correct + // but we need to compute the virtual addresses to populate the symbol table. + uint fileOffset = 0; + LayoutSections(ref fileOffset, out _, out _); + + // Generate section base symbols. The section symbols are used for PC relative relocations + // to subtract the base of the section, and in DWARF to emit section relative relocations. + byte sectionIndex = 0; + foreach (MachSection section in _sections) + { + var machSymbol = new MachSymbol + { + Name = $"lsection{sectionIndex}", + Section = section, + Value = section.VirtualAddress, + Descriptor = 0, + Type = N_SECT, + }; + _symbolTable.Add(machSymbol); + _symbolNameToIndex[machSymbol.Name] = sectionIndex; + sectionIndex++; + } + } + + private void LayoutSections(ref uint fileOffset, out uint segmentFileSize, out ulong segmentSize) + { + ulong virtualAddress = 0; + byte sectionIndex = 1; + + segmentFileSize = 0; + segmentSize = 0; + foreach (MachSection section in _sections) + { + uint alignment = 1u << (int)section.Log2Alignment; + + fileOffset = (fileOffset + alignment - 1) & ~(alignment - 1); + virtualAddress = (virtualAddress + alignment - 1) & ~(alignment - 1); + + if (section.IsInFile) + { + section.FileOffset = fileOffset; + fileOffset += (uint)section.Size; + segmentFileSize = Math.Max(segmentFileSize, fileOffset); + } + else + { + // The offset is unused for virtual sections. + section.FileOffset = 0; + } + + section.VirtualAddress = virtualAddress; + virtualAddress += section.Size; + + section.SectionIndex = sectionIndex; + sectionIndex++; + + segmentSize = Math.Max(segmentSize, virtualAddress); + } + + // ...and the relocation tables + foreach (MachSection section in _sections) + { + section.RelocationOffset = fileOffset; + fileOffset += section.NumberOfRelocationEntries * 8; + } + } + + private protected override void EmitObjectFile(string objectFilePath) + { + _sections.Add(_compactUnwindSection); + + // Segment + sections + uint loadCommandsCount = 1; + uint loadCommandsSize = (uint)(MachSegment64Header.HeaderSize + _sections.Count * MachSection.HeaderSize); + // Symbol table + loadCommandsCount += 2; + loadCommandsSize += (uint)(MachSymbolTableCommandHeader.HeaderSize + MachDynamicLinkEditSymbolTable.HeaderSize); + // Build version + loadCommandsCount++; + loadCommandsSize += (uint)MachBuildVersionCommandHeader.HeaderSize; + + // We added the compact unwinding section, debug sections, and relocations, + // so re-run the layout and this time calculate with the correct file offsets. + uint fileOffset = (uint)MachHeader64.HeaderSize + loadCommandsSize; + uint segmentFileOffset = fileOffset; + LayoutSections(ref fileOffset, out uint segmentFileSize, out ulong segmentSize); + + using var outputFileStream = new FileStream(objectFilePath, FileMode.Create); + + MachHeader64 machHeader = new MachHeader64 + { + CpuType = _cpuType, + CpuSubType = _cpuSubType, + FileType = MH_OBJECT, + NumberOfCommands = loadCommandsCount, + SizeOfCommands = loadCommandsSize, + Flags = MH_SUBSECTIONS_VIA_SYMBOLS, + Reserved = 0, + }; + machHeader.Write(outputFileStream); + + MachSegment64Header machSegment64Header = new MachSegment64Header + { + Name = "", + InitialProtection = VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE, + MaximumProtection = VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE, + Address = 0, + Size = segmentSize, + FileOffset = segmentFileOffset, + FileSize = segmentFileSize, + NumberOfSections = (uint)_sections.Count, + }; + machSegment64Header.Write(outputFileStream); + + foreach (MachSection section in _sections) + { + section.WriteHeader(outputFileStream); + } + + MachStringTable stringTable = new(); + foreach (MachSymbol symbol in _symbolTable) + { + stringTable.ReserveString(symbol.Name); + } + + uint symbolTableOffset = fileOffset; + uint stringTableOffset = symbolTableOffset + ((uint)_symbolTable.Count * 16u); + MachSymbolTableCommandHeader symbolTableHeader = new MachSymbolTableCommandHeader + { + SymbolTableOffset = symbolTableOffset, + NumberOfSymbols = (uint)_symbolTable.Count, + StringTableOffset = stringTableOffset, + StringTableSize = stringTable.Size, + }; + symbolTableHeader.Write(outputFileStream); + _dySymbolTable.Write(outputFileStream); + + // Build version + MachBuildVersionCommandHeader buildVersion = new MachBuildVersionCommandHeader + { + SdkVersion = 0x10_00_00u, // 16.0.0 + }; + switch (_targetOS) + { + case TargetOS.OSX: + buildVersion.Platform = PLATFORM_MACOS; + buildVersion.MinimumPlatformVersion = 0x0A_0C_00; // 10.12.0 + break; + + case TargetOS.MacCatalyst: + buildVersion.Platform = PLATFORM_MACCATALYST; + buildVersion.MinimumPlatformVersion = _cpuType switch + { + CPU_TYPE_X86_64 => 0x0D_05_00u, // 13.5.0 + _ => 0x0E_02_00u, // 14.2.0 + }; + break; + + case TargetOS.iOS: + case TargetOS.iOSSimulator: + case TargetOS.tvOS: + case TargetOS.tvOSSimulator: + buildVersion.Platform = _targetOS switch + { + TargetOS.iOS => PLATFORM_IOS, + TargetOS.iOSSimulator => PLATFORM_IOSSIMULATOR, + TargetOS.tvOS => PLATFORM_TVOS, + TargetOS.tvOSSimulator => PLATFORM_TVOSSIMULATOR, + _ => 0, + }; + buildVersion.MinimumPlatformVersion = 0x0B_00_00; // 11.0.0 + break; + } + buildVersion.Write(outputFileStream); + + // Write section contents + foreach (MachSection section in _sections) + { + if (section.IsInFile) + { + outputFileStream.Position = (long)section.FileOffset; + section.Stream.Position = 0; + section.Stream.CopyTo(outputFileStream); + } + } + + // Write relocations + foreach (MachSection section in _sections) + { + if (section.NumberOfRelocationEntries > 0) + { + foreach (MachRelocation relocation in section.Relocations) + { + relocation.Write(outputFileStream); + } + } + } + + // Write string and symbol table + outputFileStream.Position = symbolTableOffset; + foreach (MachSymbol symbol in _symbolTable) + { + symbol.Write(outputFileStream, stringTable); + } + stringTable.Write(outputFileStream); + } + + private protected override void CreateSection(ObjectNodeSection section, string comdatName, string symbolName, Stream sectionStream) + { + string segmentName = section.Name switch + { + "rdata" => "__TEXT", + ".eh_frame" => "__TEXT", + _ => section.Type switch + { + SectionType.Executable => "__TEXT", + SectionType.Debug => "__DWARF", + _ => "__DATA" + } + }; + + string sectionName = section.Name switch + { + "text" => "__text", + "data" => "__data", + "rdata" => "__const", + "bss" => "__bss", + ".eh_frame" => "__eh_frame", + ".debug_info" => "__debug_info", + ".debug_abbrev" => "__debug_abbrev", + ".debug_ranges" => "__debug_ranges", + ".debug_aranges" => "__debug_aranges", + ".debug_str" => "__debug_str", + ".debug_line" => "__debug_line", + ".debug_loc" => "__debug_loc", + _ => section.Name + }; + + uint flags = section.Name switch + { + "bss" => S_ZEROFILL, + ".eh_frame" => S_COALESCED, + _ => section.Type == SectionType.Uninitialized ? S_ZEROFILL : S_REGULAR + }; + + flags |= section.Name switch + { + ".dotnet_eh_table" => S_ATTR_DEBUG, + ".eh_frame" => S_ATTR_LIVE_SUPPORT | S_ATTR_STRIP_STATIC_SYMS | S_ATTR_NO_TOC, + _ => section.Type switch + { + SectionType.Executable => S_ATTR_SOME_INSTRUCTIONS | S_ATTR_PURE_INSTRUCTIONS, + SectionType.Debug => S_ATTR_DEBUG, + _ => 0 + } + }; + + MachSection machSection = new MachSection(segmentName, sectionName, sectionStream) + { + Log2Alignment = 1, + Flags = flags, + }; + + int sectionIndex = _sections.Count; + _sections.Add(machSection); + + base.CreateSection(section, comdatName, symbolName ?? $"lsection{sectionIndex}", sectionStream); + } + + protected internal override void UpdateSectionAlignment(int sectionIndex, int alignment) + { + MachSection machSection = _sections[sectionIndex]; + Debug.Assert(BitOperations.IsPow2(alignment)); + machSection.Log2Alignment = Math.Max(machSection.Log2Alignment, (uint)BitOperations.Log2((uint)alignment)); + } + + protected internal override unsafe void EmitRelocation( + int sectionIndex, + long offset, + Span data, + RelocType relocType, + string symbolName, + long addend) + { + // Mach-O doesn't use relocations between DWARF sections, so embed the offsets directly + if (relocType is IMAGE_REL_BASED_DIR64 or IMAGE_REL_BASED_HIGHLOW && + _sections[sectionIndex].IsDwarfSection) + { + // DWARF section to DWARF section relocation + if (symbolName.StartsWith('.')) + { + switch (relocType) + { + case IMAGE_REL_BASED_DIR64: + BinaryPrimitives.WriteInt64LittleEndian(data, addend); + break; + case IMAGE_REL_BASED_HIGHLOW: + BinaryPrimitives.WriteUInt32LittleEndian(data, (uint)addend); + break; + default: + throw new NotSupportedException("Unsupported relocation in debug section"); + } + return; + } + // DWARF section to code/data section relocation + else + { + Debug.Assert(IsSectionSymbolName(symbolName)); + Debug.Assert(relocType == IMAGE_REL_BASED_DIR64); + int targetSectionIndex = (int)_symbolNameToIndex[symbolName]; + BinaryPrimitives.WriteUInt64LittleEndian(data, _sections[targetSectionIndex].VirtualAddress + (ulong)addend); + base.EmitRelocation(sectionIndex, offset, data, relocType, symbolName, addend); + } + + return; + } + + switch (relocType) + { + case IMAGE_REL_BASED_ARM64_PAGEBASE_REL21: + case IMAGE_REL_BASED_ARM64_PAGEOFFSET_12A: + // Addend is handled through ARM64_RELOC_ADDEND + break; + + case IMAGE_REL_BASED_RELPTR32: + if (_cpuType == CPU_TYPE_ARM64 || sectionIndex == EhFrameSectionIndex) + { + // On ARM64 we need to represent PC relative relocations as + // subtraction and the PC offset is baked into the addend. + // On x64, ld64 requires X86_64_RELOC_SUBTRACTOR + X86_64_RELOC_UNSIGNED + // for DWARF .eh_frame section. + BinaryPrimitives.WriteInt32LittleEndian( + data, + BinaryPrimitives.ReadInt32LittleEndian(data) + + (int)(addend - offset)); + } + else + { + addend += 4; + if (addend != 0) + { + BinaryPrimitives.WriteInt32LittleEndian( + data, + BinaryPrimitives.ReadInt32LittleEndian(data) + + (int)addend); + } + } + addend = 0; + break; + + default: + if (addend != 0) + { + fixed (byte *pData = data) + { + long inlineValue = Relocation.ReadValue(relocType, (void*)pData); + Relocation.WriteValue(relocType, (void*)pData, inlineValue + addend); + addend = 0; + } + } + break; + } + + base.EmitRelocation(sectionIndex, offset, data, relocType, symbolName, addend); + } + + private protected override void EmitSymbolTable( + IDictionary definedSymbols, + SortedSet undefinedSymbols) + { + // We already emitted symbols for all non-debug sections in EmitSectionsAndLayout, + // these symbols are local and we need to account for them. + uint symbolIndex = (uint)_symbolTable.Count; + _dySymbolTable.LocalSymbolsIndex = 0; + _dySymbolTable.LocalSymbolsCount = symbolIndex; + + // Sort and insert all defined symbols + var sortedDefinedSymbols = new List(definedSymbols.Count); + foreach ((string name, SymbolDefinition definition) in definedSymbols) + { + MachSection section = _sections[definition.SectionIndex]; + sortedDefinedSymbols.Add(new MachSymbol + { + Name = name, + Section = section, + Value = section.VirtualAddress + (ulong)definition.Value, + Descriptor = 0, + Type = N_SECT | N_EXT, + }); + } + sortedDefinedSymbols.Sort((symA, symB) => string.CompareOrdinal(symA.Name, symB.Name)); + foreach (MachSymbol definedSymbol in sortedDefinedSymbols) + { + _symbolTable.Add(definedSymbol); + _symbolNameToIndex[definedSymbol.Name] = symbolIndex; + symbolIndex++; + } + + _dySymbolTable.ExternalSymbolsIndex = _dySymbolTable.LocalSymbolsCount; + _dySymbolTable.ExternalSymbolsCount = (uint)definedSymbols.Count; + + uint savedSymbolIndex = symbolIndex; + foreach (string externSymbol in undefinedSymbols) + { + if (!_symbolNameToIndex.ContainsKey(externSymbol)) + { + var machSymbol = new MachSymbol + { + Name = externSymbol, + Section = null, + Value = 0, + Descriptor = 0, + Type = N_UNDF | N_EXT, + }; + _symbolTable.Add(machSymbol); + _symbolNameToIndex[externSymbol] = symbolIndex; + symbolIndex++; + } + } + + _dySymbolTable.UndefinedSymbolsIndex = _dySymbolTable.LocalSymbolsCount + _dySymbolTable.ExternalSymbolsCount; + _dySymbolTable.UndefinedSymbolsCount = symbolIndex - savedSymbolIndex; + + EmitCompactUnwindTable(definedSymbols); + } + + private protected override void EmitRelocations(int sectionIndex, List relocationList) + { + if (_cpuType == CPU_TYPE_ARM64) + { + EmitRelocationsArm64(sectionIndex, relocationList); + } + else + { + EmitRelocationsX64(sectionIndex, relocationList); + } + } + + private void EmitRelocationsX64(int sectionIndex, List relocationList) + { + ICollection sectionRelocations = _sections[sectionIndex].Relocations; + + relocationList.Reverse(); + foreach (SymbolicRelocation symbolicRelocation in relocationList) + { + uint symbolIndex = _symbolNameToIndex[symbolicRelocation.SymbolName]; + + if (symbolicRelocation.Type == IMAGE_REL_BASED_DIR64) + { + bool isExternal = !IsSectionSymbolName(symbolicRelocation.SymbolName); + sectionRelocations.Add( + new MachRelocation + { + Address = (int)symbolicRelocation.Offset, + SymbolOrSectionIndex = isExternal ? symbolIndex : symbolIndex + 1, + Length = 8, + RelocationType = X86_64_RELOC_UNSIGNED, + IsExternal = isExternal, + IsPCRelative = false, + }); + } + else if (symbolicRelocation.Type == IMAGE_REL_BASED_RELPTR32 && sectionIndex == EhFrameSectionIndex) + { + sectionRelocations.Add( + new MachRelocation + { + Address = (int)symbolicRelocation.Offset, + SymbolOrSectionIndex = (uint)sectionIndex, + Length = 4, + RelocationType = X86_64_RELOC_SUBTRACTOR, + IsExternal = true, + IsPCRelative = false, + }); + sectionRelocations.Add( + new MachRelocation + { + Address = (int)symbolicRelocation.Offset, + SymbolOrSectionIndex = symbolIndex, + Length = 4, + RelocationType = X86_64_RELOC_UNSIGNED, + IsExternal = true, + IsPCRelative = false, + }); + } + else if (symbolicRelocation.Type is IMAGE_REL_BASED_RELPTR32 or IMAGE_REL_BASED_REL32) + { + sectionRelocations.Add( + new MachRelocation + { + Address = (int)symbolicRelocation.Offset, + SymbolOrSectionIndex = symbolIndex, + Length = 4, + RelocationType = X86_64_RELOC_BRANCH, + IsExternal = true, + IsPCRelative = true, + }); + } + else + { + throw new NotSupportedException("Unknown relocation type: " + symbolicRelocation.Type); + } + } + } + + private void EmitRelocationsArm64(int sectionIndex, List relocationList) + { + ICollection sectionRelocations = _sections[sectionIndex].Relocations; + + relocationList.Reverse(); + foreach (SymbolicRelocation symbolicRelocation in relocationList) + { + uint symbolIndex = _symbolNameToIndex[symbolicRelocation.SymbolName]; + + if (symbolicRelocation.Type == IMAGE_REL_BASED_ARM64_BRANCH26) + { + sectionRelocations.Add( + new MachRelocation + { + Address = (int)symbolicRelocation.Offset, + SymbolOrSectionIndex = symbolIndex, + Length = 4, + RelocationType = ARM64_RELOC_BRANCH26, + IsExternal = true, + IsPCRelative = true, + }); + } + else if (symbolicRelocation.Type is IMAGE_REL_BASED_ARM64_PAGEBASE_REL21 or IMAGE_REL_BASED_ARM64_PAGEOFFSET_12A) + { + if (symbolicRelocation.Addend != 0) + { + sectionRelocations.Add( + new MachRelocation + { + Address = (int)symbolicRelocation.Offset, + SymbolOrSectionIndex = (uint)symbolicRelocation.Addend, + Length = 4, + RelocationType = ARM64_RELOC_ADDEND, + IsExternal = false, + IsPCRelative = false, + }); + } + + byte type = symbolicRelocation.Type switch + { + IMAGE_REL_BASED_ARM64_PAGEBASE_REL21 => ARM64_RELOC_PAGE21, + IMAGE_REL_BASED_ARM64_PAGEOFFSET_12A => ARM64_RELOC_PAGEOFF12, + _ => 0 + }; + + sectionRelocations.Add( + new MachRelocation + { + Address = (int)symbolicRelocation.Offset, + SymbolOrSectionIndex = symbolIndex, + Length = 4, + RelocationType = type, + IsExternal = true, + IsPCRelative = symbolicRelocation.Type != IMAGE_REL_BASED_ARM64_PAGEOFFSET_12A, + }); + } + else if (symbolicRelocation.Type == IMAGE_REL_BASED_DIR64) + { + bool isExternal = !IsSectionSymbolName(symbolicRelocation.SymbolName); + sectionRelocations.Add( + new MachRelocation + { + Address = (int)symbolicRelocation.Offset, + SymbolOrSectionIndex = isExternal ? symbolIndex : symbolIndex + 1, + Length = 8, + RelocationType = ARM64_RELOC_UNSIGNED, + IsExternal = isExternal, + IsPCRelative = false, + }); + } + else if (symbolicRelocation.Type == IMAGE_REL_BASED_RELPTR32) + { + // This one is tough... needs to be represented by ARM64_RELOC_SUBTRACTOR + ARM64_RELOC_UNSIGNED. + sectionRelocations.Add( + new MachRelocation + { + Address = (int)symbolicRelocation.Offset, + SymbolOrSectionIndex = (uint)sectionIndex, + Length = 4, + RelocationType = ARM64_RELOC_SUBTRACTOR, + IsExternal = true, + IsPCRelative = false, + }); + sectionRelocations.Add( + new MachRelocation + { + Address = (int)symbolicRelocation.Offset, + SymbolOrSectionIndex = symbolIndex, + Length = 4, + RelocationType = ARM64_RELOC_UNSIGNED, + IsExternal = true, + IsPCRelative = false, + }); + } + else + { + throw new NotSupportedException("Unknown relocation type: " + symbolicRelocation.Type); + } + } + } + + private void EmitCompactUnwindTable(IDictionary definedSymbols) + { + _compactUnwindStream = new MemoryStream(32 * _compactUnwindCodes.Count); + // Preset the size of the compact unwind section which is not generated yet + _compactUnwindStream.SetLength(32 * _compactUnwindCodes.Count); + + _compactUnwindSection = new MachSection("__LD", "__compact_unwind", _compactUnwindStream) + { + Log2Alignment = 3, + Flags = S_REGULAR | S_ATTR_DEBUG, + }; + + IList symbols = _symbolTable; + Span tempBuffer = stackalloc byte[8]; + foreach (var cu in _compactUnwindCodes) + { + EmitCompactUnwindSymbol(cu.PcStartSymbolName); + BinaryPrimitives.WriteUInt32LittleEndian(tempBuffer, cu.PcLength); + BinaryPrimitives.WriteUInt32LittleEndian(tempBuffer.Slice(4), cu.Code); + _compactUnwindStream.Write(tempBuffer); + EmitCompactUnwindSymbol(cu.PersonalitySymbolName); + EmitCompactUnwindSymbol(cu.LsdaSymbolName); + } + + void EmitCompactUnwindSymbol(string symbolName) + { + Span tempBuffer = stackalloc byte[8]; + if (symbolName is not null) + { + SymbolDefinition symbol = definedSymbols[symbolName]; + MachSection section = _sections[symbol.SectionIndex]; + BinaryPrimitives.WriteUInt64LittleEndian(tempBuffer, section.VirtualAddress + (ulong)symbol.Value); + _compactUnwindSection.Relocations.Add( + new MachRelocation + { + Address = (int)_compactUnwindStream.Position, + SymbolOrSectionIndex = (byte)(1 + symbol.SectionIndex), // 1-based + Length = 8, + RelocationType = ARM64_RELOC_UNSIGNED, + IsExternal = false, + IsPCRelative = false, + } + ); + } + _compactUnwindStream.Write(tempBuffer); + } + } + + private protected override string ExternCName(string name) => "_" + name; + + // This represents the following DWARF code: + // DW_CFA_advance_loc: 4 + // DW_CFA_def_cfa_offset: +16 + // DW_CFA_offset: W29 -16 + // DW_CFA_offset: W30 -8 + // DW_CFA_advance_loc: 4 + // DW_CFA_def_cfa_register: W29 + // which is generated for the following frame prolog/epilog: + // stp fp, lr, [sp, #-10]! + // mov fp, sp + // ... + // ldp fp, lr, [sp], #0x10 + // ret + private static ReadOnlySpan DwarfArm64EmptyFrame => new byte[] + { + 0x04, 0x00, 0xFF, 0xFF, 0x10, 0x00, 0x00, 0x00, + 0x04, 0x02, 0x1D, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x02, 0x1E, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x08, 0x01, 0x1D, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + + private protected override bool EmitCompactUnwinding(string startSymbolName, ulong length, string lsdaSymbolName, byte[] blob) + { + uint encoding = _compactUnwindDwarfCode; + + if (_cpuType == CPU_TYPE_ARM64) + { + if (blob.AsSpan().SequenceEqual(DwarfArm64EmptyFrame)) + { + // Frame-based encoding, no saved registers + encoding = 0x04000000; + } + } + + _compactUnwindCodes.Add(new CompactUnwindCode( + PcStartSymbolName: startSymbolName, + PcLength: (uint)length, + Code: encoding | (encoding != _compactUnwindDwarfCode && lsdaSymbolName is not null ? 0x40000000u : 0), // UNWIND_HAS_LSDA + LsdaSymbolName: encoding != _compactUnwindDwarfCode ? lsdaSymbolName : null + )); + + return encoding != _compactUnwindDwarfCode; + } + + private protected override bool UseFrameNames => true; + + private static bool IsSectionSymbolName(string symbolName) => symbolName.StartsWith('l'); + + private struct MachHeader64 + { + public uint CpuType { get; set; } + public uint CpuSubType { get; set; } + public uint FileType { get; set; } + public uint NumberOfCommands { get; set; } + public uint SizeOfCommands { get; set; } + public uint Flags { get; set; } + public uint Reserved { get; set; } + + public static int HeaderSize => 32; + + public void Write(FileStream stream) + { + Span buffer = stackalloc byte[HeaderSize]; + + BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(0, 4), MH_MAGIC_64); + BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(4, 4), (uint)CpuType); + BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(8, 4), CpuSubType); + BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(12, 4), FileType); + BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(16, 4), NumberOfCommands); + BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(20, 4), SizeOfCommands); + BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(24, 4), Flags); + BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(28, 4), Reserved); + + stream.Write(buffer); + } + } + + public struct MachSegment64Header + { + public string Name { get; set; } + public ulong Address { get; set; } + public ulong Size { get; set; } + public ulong FileOffset { get; set; } + public ulong FileSize { get; set; } + public uint MaximumProtection { get; set; } + public uint InitialProtection { get; set; } + public uint NumberOfSections { get; set; } + public uint Flags { get; set; } + + public static int HeaderSize => 72; + + public void Write(FileStream stream) + { + Span buffer = stackalloc byte[HeaderSize]; + + BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(0, 4), LC_SEGMENT_64); + BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(4, 4), (uint)(HeaderSize + NumberOfSections * MachSection.HeaderSize)); + bool encoded = Encoding.UTF8.TryGetBytes(Name, buffer.Slice(8, 16), out _); + Debug.Assert(encoded); + BinaryPrimitives.WriteUInt64LittleEndian(buffer.Slice(24, 8), Address); + BinaryPrimitives.WriteUInt64LittleEndian(buffer.Slice(32, 8), Size); + BinaryPrimitives.WriteUInt64LittleEndian(buffer.Slice(40, 8), FileOffset); + BinaryPrimitives.WriteUInt64LittleEndian(buffer.Slice(48, 8), FileSize); + BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(56, 4), MaximumProtection); + BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(60, 4), InitialProtection); + BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(64, 4), NumberOfSections); + BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(68, 4), Flags); + + stream.Write(buffer); + } + } + + private sealed class MachSection + { + private Stream dataStream; + private List relocationCollection; + + public string SectionName { get; private init; } + public string SegmentName { get; private init; } + public ulong VirtualAddress { get; set; } + public ulong Size => (ulong)dataStream.Length; + public uint FileOffset { get; set; } + public uint Log2Alignment { get; set; } + public uint RelocationOffset { get; set; } + public uint NumberOfRelocationEntries => relocationCollection is null ? 0u : (uint)relocationCollection.Count; + public uint Flags { get; set; } + + public uint Type => Flags & 0xFF; + public bool IsInFile => Size > 0 && Type != S_ZEROFILL && Type != S_GB_ZEROFILL && Type != S_THREAD_LOCAL_ZEROFILL; + + public bool IsDwarfSection { get; } + + public IList Relocations => relocationCollection ??= new List(); + public Stream Stream => dataStream; + public byte SectionIndex { get; set; } + + public static int HeaderSize => 80; // 64-bit section + + public MachSection(string segmentName, string sectionName, Stream stream) + { + ArgumentNullException.ThrowIfNull(segmentName); + ArgumentNullException.ThrowIfNull(sectionName); + + this.SegmentName = segmentName; + this.SectionName = sectionName; + this.IsDwarfSection = segmentName == "__DWARF"; + this.dataStream = stream; + this.relocationCollection = null; + } + + public void WriteHeader(FileStream stream) + { + Span buffer = stackalloc byte[HeaderSize]; + + buffer.Clear(); + bool encoded = Encoding.UTF8.TryGetBytes(SectionName, buffer.Slice(0, 16), out _); + Debug.Assert(encoded); + encoded = Encoding.UTF8.TryGetBytes(SegmentName, buffer.Slice(16, 16), out _); + Debug.Assert(encoded); + BinaryPrimitives.WriteUInt64LittleEndian(buffer.Slice(32, 8), VirtualAddress); + BinaryPrimitives.WriteUInt64LittleEndian(buffer.Slice(40, 8), Size); + BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(48, 4), FileOffset); + BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(52, 4), Log2Alignment); + BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(56, 4), RelocationOffset); + BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(60, 4), NumberOfRelocationEntries); + BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(64, 4), Flags); + + stream.Write(buffer); + } + } + + private sealed class MachRelocation + { + public int Address { get; init; } + public uint SymbolOrSectionIndex { get; init; } + public bool IsPCRelative { get; init; } + public bool IsExternal { get; init; } + public byte Length { get; init; } + public byte RelocationType { get; init; } + + public void Write(FileStream stream) + { + Span relocationBuffer = stackalloc byte[8]; + uint info = SymbolOrSectionIndex; + info |= IsPCRelative ? 0x1_00_00_00u : 0u; + info |= Length switch { 1 => 0u << 25, 2 => 1u << 25, 4 => 2u << 25, _ => 3u << 25 }; + info |= IsExternal ? 0x8_00_00_00u : 0u; + info |= (uint)RelocationType << 28; + BinaryPrimitives.WriteInt32LittleEndian(relocationBuffer, Address); + BinaryPrimitives.WriteUInt32LittleEndian(relocationBuffer.Slice(4), info); + stream.Write(relocationBuffer); + } + } + + private sealed class MachSymbol + { + public string Name { get; init; } = string.Empty; + public byte Type { get; init; } + public MachSection Section { get; init; } + public ushort Descriptor { get; init; } + public ulong Value { get; init; } + + public void Write(FileStream stream, MachStringTable stringTable) + { + Span buffer = stackalloc byte[16]; + uint nameIndex = stringTable.GetStringOffset(Name); + + BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(0, 4), nameIndex); + buffer[4] = Type; + buffer[5] = (byte)(Section?.SectionIndex ?? 0); + BinaryPrimitives.WriteUInt16LittleEndian(buffer.Slice(6, 2), Descriptor); + BinaryPrimitives.WriteUInt64LittleEndian(buffer.Slice(8), Value); + + stream.Write(buffer); + } + } + + private sealed class MachSymbolTableCommandHeader + { + public uint SymbolTableOffset { get; set; } + public uint NumberOfSymbols { get; set; } + public uint StringTableOffset { get; set; } + public uint StringTableSize { get; set; } + + public static int HeaderSize => 24; + + public void Write(FileStream stream) + { + Span buffer = stackalloc byte[HeaderSize]; + + BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(0, 4), LC_SYMTAB); + BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(4, 4), (uint)HeaderSize); + BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(8, 4), SymbolTableOffset); + BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(12, 4), NumberOfSymbols); + BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(16, 4), StringTableOffset); + BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(20, 4), StringTableSize); + + stream.Write(buffer); + } + } + + private sealed class MachDynamicLinkEditSymbolTable + { + public uint LocalSymbolsIndex { get; set; } + public uint LocalSymbolsCount { get; set; } + public uint ExternalSymbolsIndex { get; set; } + public uint ExternalSymbolsCount { get; set; } + public uint UndefinedSymbolsIndex { get; set; } + public uint UndefinedSymbolsCount { get; set; } + public uint TableOfContentsOffset { get; set; } + public uint TableOfContentsCount { get; set; } + public uint ModuleTableOffset { get; set; } + public uint ModuleTableCount { get; set; } + public uint ExternalReferenceTableOffset { get; set; } + public uint ExternalReferenceTableCount { get; set; } + public uint IndirectSymbolTableOffset { get; set; } + public uint IndirectSymbolTableCount { get; set; } + public uint ExternalRelocationTableOffset { get; set; } + public uint ExternalRelocationTableCount { get; set; } + public uint LocalRelocationTableOffset { get; set; } + public uint LocalRelocationTableCount { get; set; } + + public static int HeaderSize => 80; + + public void Write(FileStream stream) + { + Span buffer = stackalloc byte[HeaderSize]; + + BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(0, 4), LC_DYSYMTAB); + BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(4, 4), (uint)HeaderSize); + BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(8, 4), LocalSymbolsIndex); + BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(12, 4), LocalSymbolsCount); + BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(16, 4), ExternalSymbolsIndex); + BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(20, 4), ExternalSymbolsCount); + BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(24, 4), UndefinedSymbolsIndex); + BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(28, 4), UndefinedSymbolsCount); + BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(32, 4), TableOfContentsOffset); + BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(36, 4), TableOfContentsCount); + BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(40, 4), ModuleTableOffset); + BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(44, 4), ModuleTableCount); + BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(48, 4), ExternalReferenceTableOffset); + BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(52, 4), ExternalReferenceTableCount); + BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(56, 4), IndirectSymbolTableOffset); + BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(60, 4), IndirectSymbolTableCount); + BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(64, 4), ExternalRelocationTableOffset); + BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(68, 4), ExternalRelocationTableCount); + BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(72, 4), LocalRelocationTableOffset); + BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(76, 4), LocalRelocationTableCount); + + stream.Write(buffer); + } + } + + private struct MachBuildVersionCommandHeader + { + public uint Platform; + public uint MinimumPlatformVersion { get; set; } + public uint SdkVersion { get; set; } + + public static int HeaderSize => 24; + + public void Write(FileStream stream) + { + Span buffer = stackalloc byte[HeaderSize]; + + BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(0, 4), LC_BUILD_VERSION); + BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(4, 4), (uint)HeaderSize); + BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(8, 4), Platform); + BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(12, 4), MinimumPlatformVersion); + BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(16, 4), SdkVersion); + BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(20, 4), 0); // No tools + + stream.Write(buffer); + } + } + + private sealed class MachStringTable : StringTableBuilder + { + public MachStringTable() + { + // Always start the table with empty string + GetStringOffset(""); + } + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/ObjectWriter.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/ObjectWriter.cs new file mode 100644 index 0000000000000..9699c9551a2f1 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/ObjectWriter.cs @@ -0,0 +1,558 @@ +// 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.Linq; +using ILCompiler.DependencyAnalysis; +using ILCompiler.DependencyAnalysisFramework; +using Internal.TypeSystem; +using Internal.TypeSystem.TypesDebugInfo; +using ObjectData = ILCompiler.DependencyAnalysis.ObjectNode.ObjectData; + +namespace ILCompiler.ObjectWriter +{ + public abstract class ObjectWriter + { + private protected sealed record SymbolDefinition(int SectionIndex, long Value, int Size = 0, bool Global = false); + private protected sealed record SymbolicRelocation(long Offset, RelocType Type, string SymbolName, long Addend = 0); + private protected sealed record BlockToRelocate(int SectionIndex, long Offset, byte[] Data, Relocation[] Relocations); + + private protected readonly NodeFactory _nodeFactory; + private protected readonly ObjectWritingOptions _options; + private readonly bool _isSingleFileCompilation; + private readonly bool _usesSubsectionsViaSymbols; + + private readonly Dictionary _mangledNameMap = new(); + + private readonly byte _insPaddingByte; + + // Standard sections + private readonly Dictionary _sectionNameToSectionIndex = new(StringComparer.Ordinal); + private readonly List _sectionIndexToData = new(); + private readonly List> _sectionIndexToRelocations = new(); + + // Symbol table + private readonly Dictionary _definedSymbols = new(StringComparer.Ordinal); + + // Debugging + private UserDefinedTypeDescriptor _userDefinedTypeDescriptor; + + private protected ObjectWriter(NodeFactory factory, ObjectWritingOptions options) + { + _nodeFactory = factory; + _options = options; + _isSingleFileCompilation = _nodeFactory.CompilationModuleGroup.IsSingleFileCompilation; + _usesSubsectionsViaSymbols = factory.Target.IsApplePlatform; + + // Padding byte for code sections (NOP for x86/x64) + _insPaddingByte = factory.Target.Architecture switch + { + TargetArchitecture.X86 => 0x90, + TargetArchitecture.X64 => 0x90, + _ => 0 + }; + } + + private protected abstract void CreateSection(ObjectNodeSection section, string comdatName, string symbolName, Stream sectionStream); + + protected internal abstract void UpdateSectionAlignment(int sectionIndex, int alignment); + + /// + /// Get or creates an object file section. + /// + /// Base section name and type definition. + /// Name of the COMDAT symbol or null. + /// Name of the section definiting symbol for COMDAT or null + /// Writer for a given section. + /// + /// When creating a COMDAT section both and + /// has to be specified. specifies the group section. For the primary + /// symbol both and will be the same. + /// For associated sections, such as exception or debugging information, the + /// will be different. + /// + private protected SectionWriter GetOrCreateSection(ObjectNodeSection section, string comdatName = null, string symbolName = null) + { + int sectionIndex; + SectionData sectionData; + + if (comdatName is not null || !_sectionNameToSectionIndex.TryGetValue(section.Name, out sectionIndex)) + { + sectionData = new SectionData(section.Type == SectionType.Executable ? _insPaddingByte : (byte)0); + sectionIndex = _sectionIndexToData.Count; + CreateSection(section, comdatName, symbolName, sectionData.GetReadStream()); + _sectionIndexToData.Add(sectionData); + _sectionIndexToRelocations.Add(new()); + if (comdatName is null) + { + _sectionNameToSectionIndex.Add(section.Name, sectionIndex); + } + } + else + { + sectionData = _sectionIndexToData[sectionIndex]; + } + + return new SectionWriter( + this, + sectionIndex, + sectionData); + } + + private protected bool ShouldShareSymbol(ObjectNode node) + { + if (_usesSubsectionsViaSymbols) + return false; + + return ShouldShareSymbol(node, node.GetSection(_nodeFactory)); + } + + private protected bool ShouldShareSymbol(ObjectNode node, ObjectNodeSection section) + { + if (_usesSubsectionsViaSymbols) + return false; + + // Foldable sections are always COMDATs + if (section == ObjectNodeSection.FoldableManagedCodeUnixContentSection || + section == ObjectNodeSection.FoldableManagedCodeWindowsContentSection || + section == ObjectNodeSection.FoldableReadOnlyDataSection) + return true; + + if (_isSingleFileCompilation) + return false; + + if (node is not ISymbolNode) + return false; + + // These intentionally clash with one another, but are merged with linker directives so should not be COMDAT folded + if (node is ModulesSectionNode) + return false; + + return true; + } + + private protected static ObjectNodeSection GetSharedSection(ObjectNodeSection section, string key) + { + string standardSectionPrefix = ""; + if (section.IsStandardSection) + standardSectionPrefix = "."; + + return new ObjectNodeSection(standardSectionPrefix + section.Name, section.Type, key); + } + + private void EmitOrResolveRelocation( + int sectionIndex, + long offset, + Span data, + RelocType relocType, + string symbolName, + long addend) + { + if (!_usesSubsectionsViaSymbols && + relocType is RelocType.IMAGE_REL_BASED_REL32 or RelocType.IMAGE_REL_BASED_RELPTR32 or RelocType.IMAGE_REL_BASED_ARM64_BRANCH26 && + _definedSymbols.TryGetValue(symbolName, out SymbolDefinition definedSymbol) && + definedSymbol.SectionIndex == sectionIndex) + { + // Resolve the relocation to already defined symbol and write it into data + switch (relocType) + { + case RelocType.IMAGE_REL_BASED_REL32: + addend += BinaryPrimitives.ReadInt32LittleEndian(data); + addend -= 4; + BinaryPrimitives.WriteInt32LittleEndian(data, (int)(definedSymbol.Value - offset) + (int)addend); + return; + + case RelocType.IMAGE_REL_BASED_RELPTR32: + addend += BinaryPrimitives.ReadInt32LittleEndian(data); + BinaryPrimitives.WriteInt32LittleEndian(data, (int)(definedSymbol.Value - offset) + (int)addend); + return; + + case RelocType.IMAGE_REL_BASED_ARM64_BRANCH26: + var ins = BinaryPrimitives.ReadUInt32LittleEndian(data) & 0xFC000000; + BinaryPrimitives.WriteUInt32LittleEndian(data, (((uint)(int)(definedSymbol.Value - offset) >> 2) & 0x3FFFFFF) | ins); + return; + } + } + + EmitRelocation(sectionIndex, offset, data, relocType, symbolName, addend); + } + + /// + /// Emits a single relocation into a given section. + /// + /// + /// The relocation is not resolved until is called + /// later when symbol table is already generated. + /// + protected internal virtual void EmitRelocation( + int sectionIndex, + long offset, + Span data, + RelocType relocType, + string symbolName, + long addend) + { + _sectionIndexToRelocations[sectionIndex].Add(new SymbolicRelocation(offset, relocType, symbolName, addend)); + } + + private protected bool SectionHasRelocations(int sectionIndex) + { + return _sectionIndexToRelocations[sectionIndex].Count > 0; + } + + private protected virtual void EmitReferencedMethod(string symbolName) { } + + /// + /// Emit symbolic relocations into object file as format specific + /// relocations. + /// + /// + /// This methods is guaranteed to run after . + /// + private protected abstract void EmitRelocations(int sectionIndex, List relocationList); + + /// + /// Emit new symbol definition at specified location in a given section. + /// + /// + /// The symbols are emitted into the object file representation later by + /// . Various formats have restrictions on + /// the order of the symbols so any necessary sorting is done when the + /// symbol table is created. + /// + protected internal void EmitSymbolDefinition( + int sectionIndex, + string symbolName, + long offset = 0, + int size = 0, + bool global = false) + { + _definedSymbols.Add( + symbolName, + new SymbolDefinition(sectionIndex, offset, size, global)); + } + + /// + /// Emit symbolic definitions into object file symbols. + /// + private protected abstract void EmitSymbolTable( + IDictionary definedSymbols, + SortedSet undefinedSymbols); + + private protected virtual string ExternCName(string name) => name; + + private protected string GetMangledName(ISymbolNode symbolNode) + { + string symbolName; + + if (!_mangledNameMap.TryGetValue(symbolNode, out symbolName)) + { + symbolName = ExternCName(symbolNode.GetMangledName(_nodeFactory.NameMangler)); + _mangledNameMap.Add(symbolNode, symbolName); + } + + return symbolName; + } + + private protected abstract void EmitUnwindInfo( + SectionWriter sectionWriter, + INodeWithCodeInfo nodeWithCodeInfo, + string currentSymbolName); + + private protected uint GetVarTypeIndex(bool isStateMachineMoveNextMethod, DebugVarInfoMetadata debugVar) + { + uint typeIndex; + try + { + if (isStateMachineMoveNextMethod && debugVar.DebugVarInfo.VarNumber == 0) + { + typeIndex = _userDefinedTypeDescriptor.GetStateMachineThisVariableTypeIndex(debugVar.Type); + // FIXME + // varName = "locals"; + } + else + { + typeIndex = _userDefinedTypeDescriptor.GetVariableTypeIndex(debugVar.Type); + } + } + catch (TypeSystemException) + { + typeIndex = 0; // T_NOTYPE + // FIXME + // Debug.Fail(); + } + return typeIndex; + } + + private protected virtual void EmitSectionsAndLayout() + { + } + + private protected abstract void EmitObjectFile(string objectFilePath); + + private protected abstract void CreateEhSections(); + + private SortedSet GetUndefinedSymbols() + { + SortedSet undefinedSymbolSet = new SortedSet(StringComparer.Ordinal); + foreach (var relocationList in _sectionIndexToRelocations) + foreach (var symbolicRelocation in relocationList) + { + if (!_definedSymbols.ContainsKey(symbolicRelocation.SymbolName)) + { + undefinedSymbolSet.Add(symbolicRelocation.SymbolName); + } + } + return undefinedSymbolSet; + } + + private protected abstract ITypesDebugInfoWriter CreateDebugInfoBuilder(); + + private protected abstract void EmitDebugFunctionInfo( + uint methodTypeIndex, + string methodName, + SymbolDefinition methodSymbol, + INodeWithDebugInfo debugNode, + bool hasSequencePoints); + + private protected abstract void EmitDebugSections(IDictionary definedSymbols); + + private void EmitObject(string objectFilePath, IReadOnlyCollection nodes, IObjectDumper dumper, Logger logger) + { + // Pre-create some of the sections + GetOrCreateSection(ObjectNodeSection.TextSection); + if (_nodeFactory.Target.OperatingSystem == TargetOS.Windows) + { + GetOrCreateSection(ObjectNodeSection.ManagedCodeWindowsContentSection); + } + else + { + GetOrCreateSection(ObjectNodeSection.ManagedCodeUnixContentSection); + } + + // Create sections for exception handling + CreateEhSections(); + + // Debugging + if (_options.HasFlag(ObjectWritingOptions.GenerateDebugInfo)) + { + _userDefinedTypeDescriptor = new UserDefinedTypeDescriptor(CreateDebugInfoBuilder(), _nodeFactory); + } + + ProgressReporter progressReporter = default; + if (logger.IsVerbose) + { + int count = 0; + foreach (var node in nodes) + if (node is ObjectNode) + count++; + + logger.LogMessage($"Writing {count} object nodes..."); + + progressReporter = new ProgressReporter(logger, count); + } + + List blocksToRelocate = new(); + foreach (DependencyNode depNode in nodes) + { + ObjectNode node = depNode as ObjectNode; + if (node is null) + continue; + + if (logger.IsVerbose) + progressReporter.LogProgress(); + + if (node.ShouldSkipEmittingObjectNode(_nodeFactory)) + continue; + + ObjectData nodeContents = node.GetData(_nodeFactory); + + dumper?.DumpObjectNode(_nodeFactory, node, nodeContents); + + string currentSymbolName = null; + if (node is ISymbolNode symbolNode) + { + currentSymbolName = GetMangledName(symbolNode); + } + + ObjectNodeSection section = node.GetSection(_nodeFactory); + SectionWriter sectionWriter = ShouldShareSymbol(node, section) ? + GetOrCreateSection(section, currentSymbolName, currentSymbolName) : + GetOrCreateSection(section); + + sectionWriter.EmitAlignment(nodeContents.Alignment); + + foreach (ISymbolDefinitionNode n in nodeContents.DefinedSymbols) + { + bool isMethod = n.Offset == 0 && node is IMethodNode or AssemblyStubNode; + sectionWriter.EmitSymbolDefinition( + n == node ? currentSymbolName : GetMangledName(n), + n.Offset, + isMethod ? nodeContents.Data.Length : 0); + if (_nodeFactory.GetSymbolAlternateName(n) is string alternateName) + { + sectionWriter.EmitSymbolDefinition( + ExternCName(alternateName), + n.Offset, + isMethod ? nodeContents.Data.Length : 0, + global: true); + } + } + + if (nodeContents.Relocs is not null) + { + blocksToRelocate.Add(new BlockToRelocate( + sectionWriter.SectionIndex, + sectionWriter.Position, + nodeContents.Data, + nodeContents.Relocs)); + } + + // Emit unwinding frames and LSDA + if (node is INodeWithCodeInfo nodeWithCodeInfo) + { + EmitUnwindInfo(sectionWriter, nodeWithCodeInfo, currentSymbolName); + } + + // Write the data. Note that this has to be done last as not to advance + // the section writer position. + sectionWriter.EmitData(nodeContents.Data); + } + + foreach (BlockToRelocate blockToRelocate in blocksToRelocate) + { + foreach (Relocation reloc in blockToRelocate.Relocations) + { + string relocSymbolName = GetMangledName(reloc.Target); + + EmitOrResolveRelocation( + blockToRelocate.SectionIndex, + blockToRelocate.Offset + reloc.Offset, + blockToRelocate.Data.AsSpan(reloc.Offset), + reloc.RelocType, + relocSymbolName, + reloc.Target.Offset); + + if (_options.HasFlag(ObjectWritingOptions.ControlFlowGuard) && + reloc.Target is IMethodNode or AssemblyStubNode) + { + // For now consider all method symbols address taken. + // We could restrict this in the future to those that are referenced from + // reflection tables, EH tables, were actually address taken in code, or are referenced from vtables. + EmitReferencedMethod(relocSymbolName); + } + } + } + blocksToRelocate.Clear(); + + EmitSectionsAndLayout(); + + if (_options.HasFlag(ObjectWritingOptions.GenerateDebugInfo)) + { + if (logger.IsVerbose) + logger.LogMessage($"Emitting debug information"); + + foreach (DependencyNode depNode in nodes) + { + ObjectNode node = depNode as ObjectNode; + if (node is null || node.ShouldSkipEmittingObjectNode(_nodeFactory)) + { + continue; + } + + // Ensure any allocated MethodTables have debug info + if (node is ConstructedEETypeNode methodTable) + { + _userDefinedTypeDescriptor.GetTypeIndex(methodTable.Type, needsCompleteType: true); + } + + if (node is INodeWithDebugInfo debugNode and ISymbolDefinitionNode symbolDefinitionNode and IMethodNode methodNode) + { + bool hasSequencePoints = debugNode.GetNativeSequencePoints().Any(); + uint methodTypeIndex = hasSequencePoints ? _userDefinedTypeDescriptor.GetMethodFunctionIdTypeIndex(methodNode.Method) : 0; + string methodName = GetMangledName(symbolDefinitionNode); + + if (_definedSymbols.TryGetValue(methodName, out var methodSymbol)) + { + EmitDebugFunctionInfo(methodTypeIndex, methodName, methodSymbol, debugNode, hasSequencePoints); + } + } + } + + // Ensure all fields associated with generated static bases have debug info + foreach (MetadataType typeWithStaticBase in _nodeFactory.MetadataManager.GetTypesWithStaticBases()) + { + _userDefinedTypeDescriptor.GetTypeIndex(typeWithStaticBase, needsCompleteType: true); + } + + EmitDebugSections(_definedSymbols); + } + + EmitSymbolTable(_definedSymbols, GetUndefinedSymbols()); + + int relocSectionIndex = 0; + foreach (List relocationList in _sectionIndexToRelocations) + { + EmitRelocations(relocSectionIndex, relocationList); + relocSectionIndex++; + } + + EmitObjectFile(objectFilePath); + } + + public static void EmitObject(string objectFilePath, IReadOnlyCollection nodes, NodeFactory factory, ObjectWritingOptions options, IObjectDumper dumper, Logger logger) + { + var stopwatch = new Stopwatch(); + stopwatch.Start(); + + if (Environment.GetEnvironmentVariable("DOTNET_USE_LLVM_OBJWRITER") == "1") + { + LegacyObjectWriter.EmitObject(objectFilePath, nodes, factory, options, dumper, logger); + } + else + { + ObjectWriter objectWriter = + factory.Target.IsApplePlatform ? new MachObjectWriter(factory, options) : + factory.Target.OperatingSystem == TargetOS.Windows ? new CoffObjectWriter(factory, options) : + new ElfObjectWriter(factory, options); + objectWriter.EmitObject(objectFilePath, nodes, dumper, logger); + } + + stopwatch.Stop(); + if (logger.IsVerbose) + logger.LogMessage($"Done writing object file in {stopwatch.Elapsed}"); + } + + private struct ProgressReporter + { + private readonly Logger _logger; + private readonly int _increment; + private int _current; + + // Will report progress every (100 / 10) = 10% + private const int Steps = 10; + + public ProgressReporter(Logger logger, int total) + { + _logger = logger; + _increment = total / Steps; + _current = 0; + } + + public void LogProgress() + { + _current++; + + int adjusted = _current + Steps - 1; + if ((adjusted % _increment) == 0) + { + _logger.LogMessage($"{(adjusted / _increment) * (100 / Steps)}%..."); + } + } + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/ObjectWritingOptions.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/ObjectWritingOptions.cs new file mode 100644 index 0000000000000..a3b76e220cde2 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/ObjectWritingOptions.cs @@ -0,0 +1,15 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; + +namespace ILCompiler.ObjectWriter +{ + [Flags] + public enum ObjectWritingOptions + { + GenerateDebugInfo = 0x01, + ControlFlowGuard = 0x02, + UseDwarf5 = 0x4, + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/SectionData.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/SectionData.cs new file mode 100644 index 0000000000000..9ae566487981a --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/SectionData.cs @@ -0,0 +1,180 @@ +// 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.Numerics; +using System.Text; + +namespace ILCompiler.ObjectWriter +{ + /// + /// Optimized append-only structure for writing sections. + /// + /// + /// The section data are kept in memory as a list of buffers. It supports + /// appending existing read-only buffers without copying (such as buffer + /// from ObjectNode.ObjectData). + /// + internal sealed class SectionData + { + private readonly ArrayBufferWriter _appendBuffer = new(); + private readonly List> _buffers = new(); + private long _length; + private readonly byte[] _padding = new byte[16]; + + public SectionData(byte paddingByte = 0) + { + _padding.AsSpan().Fill(paddingByte); + } + + private void FlushAppendBuffer() + { + if (_appendBuffer.WrittenCount > 0) + { + _buffers.Add(_appendBuffer.WrittenSpan.ToArray()); + _length += _appendBuffer.WrittenCount; + _appendBuffer.Clear(); + } + } + + public void AppendData(ReadOnlyMemory data) + { + FlushAppendBuffer(); + _buffers.Add(data); + _length += data.Length; + } + + public void AppendPadding(int paddingLength) + { + if (paddingLength > 0) + { + if (_appendBuffer.WrittenCount > 0 || paddingLength > _padding.Length) + { + _appendBuffer.GetSpan(paddingLength).Slice(0, paddingLength).Fill(_padding[0]); + _appendBuffer.Advance(paddingLength); + } + else + { + AppendData(_padding.AsMemory(0, paddingLength)); + } + } + } + + public IBufferWriter BufferWriter => _appendBuffer; + + public long Length => _length + _appendBuffer.WrittenCount; + + /// + /// Gets a read-only stream accessing the section data. + /// + public Stream GetReadStream() => new ReadStream(this); + + private sealed class ReadStream : Stream + { + private readonly SectionData _sectionData; + private int _bufferIndex; + private int _bufferPosition; + private long _position; + + public override bool CanRead => true; + public override bool CanSeek => true; + public override bool CanWrite => false; + public override long Length => _sectionData.Length; + + public override long Position + { + get => _position; + set + { + // Flush any non-appended data + _sectionData.FlushAppendBuffer(); + + // Seek to the correct buffer + _position = 0; + _bufferIndex = 0; + _bufferPosition = 0; + while (_position < value && _bufferIndex < _sectionData._buffers.Count) + { + if (_sectionData._buffers[_bufferIndex].Length < value - _position) + { + _position += _sectionData._buffers[_bufferIndex].Length; + _bufferIndex++; + } + else + { + _bufferPosition = (int)(value - _position); + _position = value; + break; + } + } + } + } + + public ReadStream(SectionData sectionData) + { + _sectionData = sectionData; + } + + public override void Flush() => throw new NotSupportedException(); + + public override int Read(byte[] buffer, int offset, int count) + { + return Read(buffer.AsSpan(offset, count)); + } + + public override int Read(Span buffer) + { + int bytesRead = 0; + + // Flush any non-appended data + _sectionData.FlushAppendBuffer(); + + // _bufferIndex and _bufferPosition is only valid after seeking when + // _position < _length + while (_position < _sectionData._length && _bufferIndex < _sectionData._buffers.Count) + { + ReadOnlySpan currentBuffer = _sectionData._buffers[_bufferIndex].Span.Slice(_bufferPosition); + + if (currentBuffer.Length >= buffer.Length) + { + currentBuffer.Slice(0, buffer.Length).CopyTo(buffer); + bytesRead += buffer.Length; + _position += buffer.Length; + _bufferPosition += buffer.Length; + return bytesRead; + } + + currentBuffer.CopyTo(buffer); + buffer = buffer.Slice(currentBuffer.Length); + bytesRead += currentBuffer.Length; + _position += currentBuffer.Length; + _bufferIndex++; + _bufferPosition = 0; + } + + return bytesRead; + } + + public override long Seek(long offset, SeekOrigin origin) + { + Position = origin switch + { + SeekOrigin.End => Length + offset, + SeekOrigin.Current => Position + offset, + SeekOrigin.Begin => offset, + _ => throw new ArgumentOutOfRangeException(nameof(origin)) + }; + return Position; + } + + public override void SetLength(long value) => throw new NotSupportedException(); + public override void Write(byte[] buffer, int offset, int count) => throw new NotSupportedException(); + public override void Write(ReadOnlySpan buffer) => throw new NotSupportedException(); + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/SectionWriter.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/SectionWriter.cs new file mode 100644 index 0000000000000..c7a52e6bf46a9 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/SectionWriter.cs @@ -0,0 +1,132 @@ +// 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.Numerics; +using System.Text; +using ILCompiler.DependencyAnalysis; + +namespace ILCompiler.ObjectWriter +{ + internal struct SectionWriter + { + private readonly ObjectWriter _objectWriter; + private readonly SectionData _sectionData; + + public int SectionIndex { get; init; } + + internal SectionWriter( + ObjectWriter objectWriter, + int sectionIndex, + SectionData sectionData) + { + _objectWriter = objectWriter; + SectionIndex = sectionIndex; + _sectionData = sectionData; + } + + public readonly void EmitData(ReadOnlyMemory data) + { + _sectionData.AppendData(data); + } + + public readonly void EmitAlignment(int alignment) + { + _objectWriter.UpdateSectionAlignment(SectionIndex, alignment); + + long position = Position; + int padding = (int)(((position + alignment - 1) & ~(alignment - 1)) - position); + _sectionData.AppendPadding(padding); + } + + public readonly void EmitRelocation( + long relativeOffset, + Span data, + RelocType relocType, + string symbolName, + long addend) + { + _objectWriter.EmitRelocation( + SectionIndex, + Position + relativeOffset, + data, + relocType, + symbolName, + addend); + } + + public readonly void EmitSymbolDefinition( + string symbolName, + long relativeOffset = 0, + int size = 0, + bool global = false) + { + _objectWriter.EmitSymbolDefinition( + SectionIndex, + symbolName, + Position + relativeOffset, + size, + global); + } + + public readonly void EmitSymbolReference( + RelocType relocType, + string symbolName, + long addend = 0) + { + IBufferWriter bufferWriter = _sectionData.BufferWriter; + int size = relocType == RelocType.IMAGE_REL_BASED_DIR64 ? sizeof(ulong) : sizeof(uint); + Span buffer = bufferWriter.GetSpan(size); + buffer.Clear(); + _objectWriter.EmitRelocation( + SectionIndex, + Position, + buffer, + relocType, + symbolName, + addend); + bufferWriter.Advance(size); + } + + public readonly void Write(ReadOnlySpan value) + { + IBufferWriter bufferWriter = _sectionData.BufferWriter; + value.CopyTo(bufferWriter.GetSpan(value.Length)); + bufferWriter.Advance(value.Length); + } + + public readonly void WriteULEB128(ulong value) => DwarfHelper.WriteULEB128(_sectionData.BufferWriter, value); + + public readonly void WriteSLEB128(long value) => DwarfHelper.WriteSLEB128(_sectionData.BufferWriter, value); + + public readonly void WriteByte(byte value) + { + IBufferWriter bufferWriter = _sectionData.BufferWriter; + bufferWriter.GetSpan(1)[0] = value; + bufferWriter.Advance(1); + } + + public readonly void WriteLittleEndian(T value) + where T : IBinaryInteger + { + IBufferWriter bufferWriter = _sectionData.BufferWriter; + Span buffer = bufferWriter.GetSpan(value.GetByteCount()); + bufferWriter.Advance(value.WriteLittleEndian(buffer)); + } + + public readonly void WriteUtf8String(string value) + { + IBufferWriter bufferWriter = _sectionData.BufferWriter; + int size = Encoding.UTF8.GetByteCount(value) + 1; + Span buffer = bufferWriter.GetSpan(size); + Encoding.UTF8.GetBytes(value, buffer); + buffer[size - 1] = 0; + bufferWriter.Advance(size); + } + + public readonly void WritePadding(int size) => _sectionData.AppendPadding(size); + + public readonly long Position => _sectionData.Length; + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/StringTableBuilder.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/StringTableBuilder.cs new file mode 100644 index 0000000000000..81f7315d225e4 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/StringTableBuilder.cs @@ -0,0 +1,185 @@ +// 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.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Text; +using System.Linq; + +namespace ILCompiler.ObjectWriter +{ + internal class StringTableBuilder + { + private readonly MemoryStream _stream = new(); + private readonly SortedSet _reservedStrings = new(StringComparer.Ordinal); + private Dictionary _stringToOffset = new(StringComparer.Ordinal); + + public void Write(FileStream stream) + { + _stream.Position = 0; + _stream.CopyTo(stream); + } + + public uint Size + { + get + { + FlushReservedStrings(); + return (uint)_stream.Length; + } + } + + public void ReserveString(string text) + { + if (text is object && !_stringToOffset.ContainsKey(text)) + { + _reservedStrings.Add(text); + } + } + + private void FlushReservedStrings() + { + string[] reservedStrings = _reservedStrings.ToArray(); + + // Pre-sort the string based on their matching suffix + MultiKeySort(reservedStrings, 0); + + // Add the strings to string table + string lastText = null; + for (int i = 0; i < reservedStrings.Length; i++) + { + var text = reservedStrings[i]; + uint index; + if (lastText is not null && lastText.EndsWith(text, StringComparison.Ordinal)) + { + // Suffix matches the last symbol + index = (uint)(_stream.Length - Encoding.UTF8.GetByteCount(text) - 1); + _stringToOffset.Add(text, index); + } + else + { + lastText = text; + CreateIndex(text); + } + } + + _reservedStrings.Clear(); + + static char TailCharacter(string str, int pos) + { + int index = str.Length - pos - 1; + if ((uint)index < str.Length) + return str[index]; + return '\0'; + } + + static void MultiKeySort(Span input, int pos) + { + if (!MultiKeySortSmallInput(input, pos)) + { + MultiKeySortLargeInput(input, pos); + } + } + + static void MultiKeySortLargeInput(Span input, int pos) + { + tailcall: + char pivot = TailCharacter(input[0], pos); + int l = 0, h = input.Length; + for (int i = 1; i < h;) + { + char c = TailCharacter(input[i], pos); + if (c > pivot) + { + (input[l], input[i]) = (input[i], input[l]); + l++; i++; + } + else if (c < pivot) + { + h--; + (input[h], input[i]) = (input[i], input[h]); + } + else + { + i++; + } + } + + MultiKeySort(input.Slice(0, l), pos); + MultiKeySort(input.Slice(h), pos); + if (pivot != '\0') + { + // Use a loop as a poor man's tailcall + // MultiKeySort(input.Slice(l, h - l), pos + 1); + pos++; + input = input.Slice(l, h - l); + if (!MultiKeySortSmallInput(input, pos)) + { + goto tailcall; + } + } + } + + static bool MultiKeySortSmallInput(Span input, int pos) + { + if (input.Length <= 1) + return true; + + // Optimize comparing two strings + if (input.Length == 2) + { + while (true) + { + char c0 = TailCharacter(input[0], pos); + char c1 = TailCharacter(input[1], pos); + if (c0 < c1) + { + (input[0], input[1]) = (input[1], input[0]); + break; + } + else if (c0 > c1 || c0 == (char)0) + { + break; + } + pos++; + } + return true; + } + + return false; + } + } + + private uint CreateIndex(string text) + { + uint offset = (uint)_stream.Position; + int reservedBytes = Encoding.UTF8.GetByteCount(text) + 1; + byte[] buffer = ArrayPool.Shared.Rent(reservedBytes); + var span = new Span(buffer, 0, reservedBytes); + Encoding.UTF8.GetBytes(text, span); + span[reservedBytes - 1] = 0; + _stream.Write(span); + ArrayPool.Shared.Return(buffer); + _stringToOffset[text] = offset; + return offset; + } + + public uint GetStringOffset(string text) + { + if (_reservedStrings.Count > 0) + { + FlushReservedStrings(); + } + + if (_stringToOffset.TryGetValue(text, out uint index)) + { + return index; + } + + return CreateIndex(text); + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/UnixObjectWriter.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/UnixObjectWriter.cs new file mode 100644 index 0000000000000..37d274b794649 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/UnixObjectWriter.cs @@ -0,0 +1,267 @@ +// 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.TypeSystem; +using Internal.TypeSystem.TypesDebugInfo; + +namespace ILCompiler.ObjectWriter +{ + /// + /// Base implementation for ELF and Mach-O object file format writers. Implements + /// the common code for DWARF debugging and exception handling information. + /// + internal abstract class UnixObjectWriter : ObjectWriter + { + private sealed record UnixSectionDefinition(string SymbolName, Stream SectionStream); + + // Debugging + private DwarfBuilder _dwarfBuilder; + private readonly List _sections = new(); + + // Exception handling sections + private SectionWriter _lsdaSectionWriter; + private int _ehFrameSectionIndex; + private DwarfCie _dwarfCie; + private DwarfEhFrame _dwarfEhFrame; + + protected int EhFrameSectionIndex => _ehFrameSectionIndex; + + private static readonly ObjectNodeSection LsdaSection = new ObjectNodeSection(".dotnet_eh_table", SectionType.ReadOnly, null); + private static readonly ObjectNodeSection EhFrameSection = new ObjectNodeSection(".eh_frame", SectionType.ReadOnly, null); + 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); + + protected UnixObjectWriter(NodeFactory factory, ObjectWritingOptions options) + : base(factory, options) + { + } + + private protected override void CreateSection(ObjectNodeSection section, string comdatName, string symbolName, Stream sectionStream) + { + if (section.Type != SectionType.Debug && + section != LsdaSection && + section != EhFrameSection && + (comdatName is null || Equals(comdatName, symbolName))) + { + // Record code and data sections that can be referenced from debugging information + _sections.Add(new UnixSectionDefinition(symbolName, sectionStream)); + } + else + { + _sections.Add(null); + } + } + + private protected virtual bool EmitCompactUnwinding(string startSymbolName, ulong length, string lsdaSymbolName, byte[] blob) => false; + + private protected virtual bool UseFrameNames => false; + + private protected override void EmitUnwindInfo( + SectionWriter sectionWriter, + INodeWithCodeInfo nodeWithCodeInfo, + string currentSymbolName) + { + if (nodeWithCodeInfo.FrameInfos is FrameInfo[] frameInfos && + nodeWithCodeInfo is ISymbolDefinitionNode) + { + bool useFrameNames = UseFrameNames; + SectionWriter lsdaSectionWriter; + + if (ShouldShareSymbol((ObjectNode)nodeWithCodeInfo)) + { + lsdaSectionWriter = GetOrCreateSection(LsdaSection, currentSymbolName, $"_lsda0{currentSymbolName}"); + } + else + { + lsdaSectionWriter = _lsdaSectionWriter; + } + + long mainLsdaOffset = lsdaSectionWriter.Position; + for (int i = 0; i < frameInfos.Length; i++) + { + FrameInfo frameInfo = frameInfos[i]; + + int start = frameInfo.StartOffset; + int end = frameInfo.EndOffset; + byte[] blob = frameInfo.BlobData; + + string lsdaSymbolName = $"_lsda{i}{currentSymbolName}"; + string framSymbolName = $"_fram{i}{currentSymbolName}"; + + lsdaSectionWriter.EmitSymbolDefinition(lsdaSymbolName); + if (start != 0 && useFrameNames) + { + sectionWriter.EmitSymbolDefinition(framSymbolName, start); + } + + FrameInfoFlags flags = frameInfo.Flags; + + if (i != 0) + { + lsdaSectionWriter.WriteByte((byte)flags); + lsdaSectionWriter.WriteLittleEndian((int)(mainLsdaOffset - lsdaSectionWriter.Position)); + // Emit relative offset from the main function + lsdaSectionWriter.WriteLittleEndian((uint)(start - 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; + + lsdaSectionWriter.WriteByte((byte)flags); + + if (associatedDataNode is not null) + { + string symbolName = GetMangledName(associatedDataNode); + lsdaSectionWriter.EmitSymbolReference(RelocType.IMAGE_REL_BASED_RELPTR32, symbolName, 0); + } + + if (ehInfo is not null) + { + string symbolName = GetMangledName(ehInfo); + lsdaSectionWriter.EmitSymbolReference(RelocType.IMAGE_REL_BASED_RELPTR32, symbolName, 0); + } + + if (nodeWithCodeInfo.GCInfo is not null) + { + lsdaSectionWriter.Write(nodeWithCodeInfo.GCInfo); + } + } + + string 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: null); + _dwarfEhFrame.AddFde(fde); + } + } + } + } + + private protected override void EmitDebugFunctionInfo( + uint methodTypeIndex, + string 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 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 (null, 0); + }); + } + + 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 override ITypesDebugInfoWriter CreateDebugInfoBuilder() + { + return _dwarfBuilder = new DwarfBuilder( + _nodeFactory.NameMangler, + _nodeFactory.Target, + _options.HasFlag(ObjectWritingOptions.UseDwarf5)); + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj b/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj index 67eb341202caf..1137962a79322 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj @@ -562,7 +562,6 @@ - @@ -592,6 +591,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/coreclr/tools/aot/ILCompiler.RyuJit/Compiler/DependencyAnalysis/MethodCodeNode.cs b/src/coreclr/tools/aot/ILCompiler.RyuJit/Compiler/DependencyAnalysis/MethodCodeNode.cs index 2c5c14d9b5fdb..a59888d2d291d 100644 --- a/src/coreclr/tools/aot/ILCompiler.RyuJit/Compiler/DependencyAnalysis/MethodCodeNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.RyuJit/Compiler/DependencyAnalysis/MethodCodeNode.cs @@ -195,12 +195,23 @@ public IEnumerable GetDebugVars() i++; } - var localNames = new string[_localTypes.Length]; - - foreach (var local in _debugInfo.GetLocalVariables()) + string[] localNames; + if (_localTypes.Length > 0) + { + localNames = new string[_localTypes.Length]; + var localVariables = _debugInfo.GetLocalVariables(); + if (localVariables != null) + { + foreach (var local in localVariables) + { + if (!local.CompilerGenerated && local.Slot < localNames.Length) + localNames[local.Slot] = local.Name; + } + } + } + else { - if (!local.CompilerGenerated && local.Slot < localNames.Length) - localNames[local.Slot] = local.Name; + localNames = Array.Empty(); } foreach (var varInfo in _debugVarInfos) diff --git a/src/coreclr/tools/aot/ILCompiler.RyuJit/Compiler/RyuJitCompilation.cs b/src/coreclr/tools/aot/ILCompiler.RyuJit/Compiler/RyuJitCompilation.cs index 885f2ce2026c5..b20a3a470d037 100644 --- a/src/coreclr/tools/aot/ILCompiler.RyuJit/Compiler/RyuJitCompilation.cs +++ b/src/coreclr/tools/aot/ILCompiler.RyuJit/Compiler/RyuJitCompilation.cs @@ -3,12 +3,14 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; using ILCompiler.DependencyAnalysis; using ILCompiler.DependencyAnalysisFramework; +using ILCompiler.ObjectWriter; using ILLink.Shared; using Internal.IL; @@ -106,7 +108,7 @@ protected override void CompileInternal(string outputFile, ObjectDumper dumper) if ((_compilationOptions & RyuJitCompilationOptions.ControlFlowGuardAnnotations) != 0) options |= ObjectWritingOptions.ControlFlowGuard; - ObjectWriter.EmitObject(outputFile, nodes, NodeFactory, options, dumper, _logger); + ObjectWriter.ObjectWriter.EmitObject(outputFile, nodes, NodeFactory, options, dumper, _logger); } protected override void ComputeDependencyNodeDependencies(List> obj)