|
// 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();
}
}
|