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