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