// 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.Text; using System.Diagnostics; using System.Reflection.PortableExecutable; using Internal.Text; using Internal.TypeSystem; using Internal.TypeSystem.Ecma; using System.IO; using System.Collections.Immutable; using System.Collections.Generic; using ILCompiler.Diagnostics; using ILCompiler.DependencyAnalysisFramework; using System.Security.Cryptography; namespace ILCompiler.DependencyAnalysis.ReadyToRun { public abstract class DebugDirectoryEntryNode : ObjectNode, ISymbolDefinitionNode { protected readonly EcmaModule _module; public DebugDirectoryEntryNode(EcmaModule module) { _module = module; } public override ObjectNodeSection GetSection(NodeFactory factory) { return ObjectNodeSection.ReadOnlyDataSection; } public override bool IsShareable => false; protected internal override int Phase => (int)ObjectNodePhase.Ordered; public override bool StaticDependenciesAreComputed => true; public int Offset => 0; public abstract void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb); protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler); public override int CompareToImpl(ISortableNode other, CompilerComparer comparer) { return _module.CompareTo(((DebugDirectoryEntryNode)other)._module); } } public static class DeterministicDebugDirectoryEntry { internal static void EmitHeader(ref ObjectDataBuilder builder) { builder.EmitUInt(0 /* Characteristics */); builder.EmitUInt(0); builder.EmitUShort(0); builder.EmitUShort(0); builder.EmitInt((int)DebugDirectoryEntryType.Reproducible); builder.EmitInt(0); builder.EmitUInt(0); builder.EmitUInt(0); } } public class PerfMapDebugDirectoryEntryNode : DebugDirectoryEntryNode { const int PerfMapEntrySize = sizeof(uint) + // Magic SignatureSize + // Signature sizeof(uint) + // Age 260; // FileName public const uint PerfMapMagic = 0x4D523252;// R2RM public const int PerfMapEntryType = 21; // DebugDirectoryEntryType for this entry. private const int SignatureSize = 16; public override int ClassCode => 813123850; public unsafe int Size => PerfMapEntrySize; public PerfMapDebugDirectoryEntryNode(string entryName, int perfMapFormatVersion) : base(null) { _entryName = entryName; _perfMapFormatVersion = perfMapFormatVersion; } private readonly string _entryName; private readonly int _perfMapFormatVersion; public override void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) { sb.Append(nameMangler.CompilationUnitPrefix); sb.Append($"__PerfMapDebugDirectoryEntryNode_{_entryName.Replace('.','_')}"); } public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) { ObjectDataBuilder builder = new ObjectDataBuilder(factory, relocsOnly); builder.RequireInitialPointerAlignment(); builder.AddSymbol(this); List<AssemblyInfo> assemblies = []; foreach (string inputPath in factory.TypeSystemContext.InputFilePaths.Values) { EcmaModule module = factory.TypeSystemContext.GetModuleFromPath(inputPath); assemblies.Add(new AssemblyInfo(module.Assembly.GetName().Name, module.MetadataReader.GetGuid(module.MetadataReader.GetModuleDefinition().Mvid))); } byte[] signature = PerfMapWriter.PerfMapV1SignatureHelper(assemblies, factory.Target); builder.EmitUInt(PerfMapMagic); builder.EmitBytes(signature); builder.EmitInt(_perfMapFormatVersion); builder.EmitBytes(Encoding.UTF8.GetBytes(_entryName)); builder.EmitByte(0); Debug.Assert(builder.CountBytes <= PerfMapEntrySize); return builder.ToObjectData(); } internal void EmitHeader(ref ObjectDataBuilder builder) { builder.EmitUInt(0); /* Characteristics */ builder.EmitUInt(0); /* Stamp */ builder.EmitUShort(1); /* Major */ builder.EmitUShort(0); /* Minor */ builder.EmitInt((int)PerfMapEntryType); builder.EmitInt(Size); builder.EmitReloc(this, RelocType.IMAGE_REL_BASED_ADDR32NB); builder.EmitReloc(this, RelocType.IMAGE_REL_FILE_ABSOLUTE); } public override int CompareToImpl(ISortableNode other, CompilerComparer comparer) { return _entryName.CompareTo(((PerfMapDebugDirectoryEntryNode)other)._entryName); } } public class NativeDebugDirectoryEntryNode : DebugDirectoryEntryNode { const int RSDSSize = sizeof(int) + // Magic 16 + // Signature (guid) sizeof(int) + // Age 260; // FileName public override int ClassCode => 119958401; public unsafe int Size => RSDSSize; public const uint RsdsMagic = 0x53445352;// R2RM public NativeDebugDirectoryEntryNode(string pdbName) : base(null) { _pdbName = pdbName; } private readonly string _pdbName; private readonly RSDSChecksumNode _checksumNode = new(); public override void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) { sb.Append(nameMangler.CompilationUnitPrefix); sb.Append($"__NativeDebugDirectory_{_pdbName.Replace('.','_')}"); } public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) { ObjectDataBuilder builder = new ObjectDataBuilder(factory, relocsOnly); builder.RequireInitialPointerAlignment(); builder.AddSymbol(this); builder.EmitUInt(RsdsMagic); builder.EmitChecksumReloc(_checksumNode); // Age builder.EmitInt(1); string pdbFileName = _pdbName; byte[] pdbFileNameBytes = Encoding.UTF8.GetBytes(pdbFileName); builder.EmitBytes(pdbFileNameBytes); builder.EmitByte(0); // Null terminator Debug.Assert(builder.CountBytes <= RSDSSize); return builder.ToObjectData(); } public byte[] GenerateRSDSEntryData(byte[] hash) { MemoryStream rsdsEntry = new MemoryStream(RSDSSize); using (BinaryWriter writer = new BinaryWriter(rsdsEntry)) { writer.Write(RsdsMagic); Debug.Assert(hash.Length >= 16); writer.Write(hash, 0, 16); // Age writer.Write(1); string pdbFileName = _pdbName; byte[] pdbFileNameBytes = Encoding.UTF8.GetBytes(pdbFileName); writer.Write(pdbFileNameBytes); writer.Write(0); // Null terminator Debug.Assert(rsdsEntry.Length <= RSDSSize); return rsdsEntry.ToArray(); } } public override int CompareToImpl(ISortableNode other, CompilerComparer comparer) { return _pdbName.CompareTo(((NativeDebugDirectoryEntryNode)other)._pdbName); } internal void EmitHeader(ref ObjectDataBuilder builder, uint stamp, ushort majorVersion) { builder.EmitUInt(0); /* Characteristics */ builder.EmitUInt(stamp); builder.EmitUShort(majorVersion); // Make sure the "is portable pdb" indicator (MinorVersion == 0x504d) is clear. // The NI PDB generated currently is a full PDB. builder.EmitUShort(0 /* MinorVersion */); builder.EmitInt((int)DebugDirectoryEntryType.CodeView); builder.EmitInt(Size); builder.EmitReloc(this, RelocType.IMAGE_REL_BASED_ADDR32NB); builder.EmitReloc(this, RelocType.IMAGE_REL_FILE_ABSOLUTE); } private class RSDSChecksumNode : DependencyNodeCore<NodeFactory>, IChecksumNode { public int ChecksumSize => 16; public void EmitChecksum(ReadOnlySpan<byte> outputBlob, Span<byte> checksumLocation) { Debug.Assert(checksumLocation.Length == ChecksumSize); // Take the first 16 bytes of the SHA256 hash as the RSDS checksum. SHA256.HashData(outputBlob)[0..ChecksumSize].CopyTo(checksumLocation); } public override bool InterestingForDynamicDependencyAnalysis => false; public override bool HasDynamicDependencies => false; public override bool HasConditionalStaticDependencies => false; public override bool StaticDependenciesAreComputed => true; public int Offset => 0; public bool RepresentsIndirectionCell => false; public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) { sb.Append(nameMangler.CompilationUnitPrefix); sb.Append($"__RSDSChecksum"); } public override IEnumerable<CombinedDependencyListEntry> GetConditionalStaticDependencies(NodeFactory context) => []; public override IEnumerable<DependencyListEntry> GetStaticDependencies(NodeFactory context) => []; public override IEnumerable<CombinedDependencyListEntry> SearchDynamicDependencies(List<DependencyNodeCore<NodeFactory>> markedNodes, int firstNode, NodeFactory context) => []; protected override string GetName(NodeFactory context) { return "RSDSChecksum"; } } } public class CopiedDebugDirectoryEntryNode : DebugDirectoryEntryNode { private readonly int _debugEntryIndex; public override int ClassCode => 1558397; public CopiedDebugDirectoryEntryNode(EcmaModule sourceModule, int debugEntryIndex) : base(sourceModule) { Debug.Assert(debugEntryIndex >= 0); _debugEntryIndex = debugEntryIndex; } public override void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) { sb.Append(nameMangler.CompilationUnitPrefix); sb.Append($"__CopiedDebugEntryNode_{_debugEntryIndex}_{_module.Assembly.GetName().Name}"); } public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) { if (relocsOnly) { return new ObjectData(Array.Empty<byte>(), Array.Empty<Relocation>(), 1, new ISymbolDefinitionNode[] { this }); } ImmutableArray<DebugDirectoryEntry> entries = _module.PEReader.SafeReadDebugDirectory(); Debug.Assert(entries != null && _debugEntryIndex < entries.Length); DebugDirectoryEntry sourceDebugEntry = entries[_debugEntryIndex]; PEMemoryBlock block = _module.PEReader.GetSectionData(sourceDebugEntry.DataRelativeVirtualAddress); byte[] result = new byte[sourceDebugEntry.DataSize]; block.GetContent(0, sourceDebugEntry.DataSize).CopyTo(result); return new ObjectData(result, Array.Empty<Relocation>(), _module.Context.Target.PointerSize, new ISymbolDefinitionNode[] { this }); } public override int CompareToImpl(ISortableNode other, CompilerComparer comparer) { int moduleComp = base.CompareToImpl(other, comparer); if (moduleComp != 0) return moduleComp; return _debugEntryIndex - ((CopiedDebugDirectoryEntryNode)other)._debugEntryIndex; } } } |