File: Linker\CustomSymbolWriter.cs
Web Access
Project: src\src\tools\illink\src\linker\Mono.Linker.csproj (illink)
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
 
using System.IO;
using System.Text;
 
using Mono.Cecil;
using Mono.Cecil.Cil;
 
namespace Mono.Linker
{
    internal sealed class CustomSymbolWriterProvider : ISymbolWriterProvider
    {
        readonly DefaultSymbolWriterProvider _defaultProvider = new DefaultSymbolWriterProvider();
        readonly bool _preserveSymbolPaths;
 
        public CustomSymbolWriterProvider(bool preserveSymbolPaths) => this._preserveSymbolPaths = preserveSymbolPaths;
 
        public ISymbolWriter GetSymbolWriter(ModuleDefinition module, string fileName)
            => new CustomSymbolWriter(_defaultProvider.GetSymbolWriter(module, fileName), module, _preserveSymbolPaths);
 
        public ISymbolWriter GetSymbolWriter(ModuleDefinition module, Stream symbolStream)
            => new CustomSymbolWriter(_defaultProvider.GetSymbolWriter(module, symbolStream), module, _preserveSymbolPaths);
    }
 
    internal sealed class CustomSymbolWriter : ISymbolWriter
    {
        // ASCII "RSDS": https://github.com/dotnet/runtime/blob/main/docs/design/specs/PE-COFF.md#codeview-debug-directory-entry-type-2
        const int CodeViewSignature = 0x53445352;
 
        readonly ISymbolWriter _symbolWriter;
        readonly ModuleDefinition _module;
        readonly bool _preserveSymbolPaths;
 
        internal CustomSymbolWriter(ISymbolWriter defaultWriter, ModuleDefinition module, bool preserveSymbolPaths)
        {
            _symbolWriter = defaultWriter;
            _module = module;
            _preserveSymbolPaths = preserveSymbolPaths;
        }
 
        public ImageDebugHeader GetDebugHeader()
        {
            var header = _symbolWriter.GetDebugHeader();
            if (!_preserveSymbolPaths)
                return header;
 
            if (!header.HasEntries)
                return header;
 
            for (int i = 0; i < header.Entries.Length; i++)
            {
                header.Entries[i] = ProcessEntry(header.Entries[i]);
            }
 
            return header;
        }
 
        ImageDebugHeaderEntry ProcessEntry(ImageDebugHeaderEntry entry)
        {
            if (entry.Directory.Type != ImageDebugType.CodeView)
                return entry;
 
            var reader = new BinaryReader(new MemoryStream(entry.Data));
            var newDataStream = new MemoryStream();
            var writer = new BinaryWriter(newDataStream);
 
            var sig = reader.ReadUInt32();
            if (sig != CodeViewSignature)
                return entry;
 
            writer.Write(sig);
            writer.Write(reader.ReadBytes(16)); // MVID
            writer.Write(reader.ReadUInt32()); // Age
 
            writer.Write(Encoding.UTF8.GetBytes(GetOriginalPdbPath()));
            writer.Write((byte)0);
 
            var newData = newDataStream.ToArray();
 
            var directory = entry.Directory;
            directory.SizeOfData = newData.Length;
 
            return new ImageDebugHeaderEntry(directory, newData);
        }
 
        string GetOriginalPdbPath()
        {
            if (!_module.HasDebugHeader)
                return string.Empty;
 
            var debugHeader = _module.GetDebugHeader();
            foreach (var entry in debugHeader.Entries)
            {
                if (entry.Directory.Type != ImageDebugType.CodeView)
                    continue;
 
                var reader = new BinaryReader(new MemoryStream(entry.Data));
                var sig = reader.ReadUInt32();
                if (sig != CodeViewSignature)
                    return string.Empty;
 
                var stream = reader.BaseStream;
                stream.Seek(16 + 4, SeekOrigin.Current); // MVID and Age
                                                         // Pdb path is NUL-terminated path at offset 24.
                                                         // https://github.com/dotnet/runtime/blob/main/docs/design/specs/PE-COFF.md#codeview-debug-directory-entry-type-2
                return Encoding.UTF8.GetString(
                    reader.ReadBytes((int)(stream.Length - stream.Position - 1))); // remaining length - ending \0
            }
 
            return string.Empty;
        }
 
        public ISymbolReaderProvider GetReaderProvider() => _symbolWriter.GetReaderProvider();
 
        public void Write(MethodDebugInformation info) => _symbolWriter.Write(info);
 
        public void Write(ICustomDebugInformationProvider provider) => _symbolWriter.Write(provider);
 
        public void Write() => _symbolWriter.Write();
 
        public void Dispose() => _symbolWriter.Dispose();
    }
}