diff --git a/ICSharpCode.Decompiler/DebugInfo/PortablePdbWriter.cs b/ICSharpCode.Decompiler/DebugInfo/PortablePdbWriter.cs index f20552316..23adcf7e6 100644 --- a/ICSharpCode.Decompiler/DebugInfo/PortablePdbWriter.cs +++ b/ICSharpCode.Decompiler/DebugInfo/PortablePdbWriter.cs @@ -6,6 +6,7 @@ using System.IO; using System.Linq; using System.Reflection.Metadata; using System.Reflection.Metadata.Ecma335; +using System.Reflection.PortableExecutable; using System.Security.Cryptography; using System.Text; using ICSharpCode.Decompiler.CSharp; @@ -29,6 +30,10 @@ namespace ICSharpCode.Decompiler.DebugInfo public static readonly Guid HashAlgorithmSHA256 = new Guid("8829d00f-11b8-4213-878b-770e8597ac16"); static readonly FileVersionInfo decompilerVersion = FileVersionInfo.GetVersionInfo(typeof(CSharpDecompiler).Assembly.Location); + public static bool HasCodeViewDebugDirectoryEntry(PEFile file) + { + return file.Reader.ReadDebugDirectory().Any(entry => entry.Type == DebugDirectoryEntryType.CodeView); + } public static void WritePdb(PEFile file, CSharpDecompiler decompiler, DecompilerSettings settings, Stream targetStream) { @@ -101,7 +106,8 @@ namespace ICSharpCode.Decompiler.DebugInfo metadata.AddImportScope(default, default); } } - var debugDir = file.Reader.ReadDebugDirectory().FirstOrDefault(dir => dir.IsPortableCodeView); + + var debugDir = file.Reader.ReadDebugDirectory().FirstOrDefault(dir => dir.Type == DebugDirectoryEntryType.CodeView); var portable = file.Reader.ReadCodeViewDebugDirectoryData(debugDir); var contentId = new BlobContentId(portable.Guid, debugDir.Stamp); PortablePdbBuilder serializer = new PortablePdbBuilder(metadata, GetRowCounts(reader), entrypointHandle, blobs => contentId); diff --git a/ILSpy/Commands/GeneratePdbContextMenuEntry.cs b/ILSpy/Commands/GeneratePdbContextMenuEntry.cs index 19952b4a5..f4ddd6fd9 100644 --- a/ILSpy/Commands/GeneratePdbContextMenuEntry.cs +++ b/ILSpy/Commands/GeneratePdbContextMenuEntry.cs @@ -21,6 +21,7 @@ using System.Diagnostics; using System.IO; using System.Linq; using System.Threading.Tasks; +using System.Windows; using ICSharpCode.Decompiler; using ICSharpCode.Decompiler.CSharp; using ICSharpCode.Decompiler.DebugInfo; @@ -51,6 +52,11 @@ namespace ICSharpCode.ILSpy internal static void GeneratePdbForAssembly(LoadedAssembly assembly) { + var file = assembly.GetPEFileOrNull(); + if (!PortablePdbWriter.HasCodeViewDebugDirectoryEntry(file)) { + MessageBox.Show($"Cannot create PDB file for {Path.GetFileName(assembly.FileName)}, because it does not contain a PE Debug Directory Entry of type 'CodeView'."); + return; + } SaveFileDialog dlg = new SaveFileDialog(); dlg.FileName = DecompilerTextView.CleanUpName(assembly.ShortName) + ".pdb"; dlg.Filter = "Portable PDB|*.pdb|All files|*.*"; @@ -62,7 +68,6 @@ namespace ICSharpCode.ILSpy Stopwatch stopwatch = Stopwatch.StartNew(); using (FileStream stream = new FileStream(fileName, FileMode.OpenOrCreate, FileAccess.Write)) { try { - var file = assembly.GetPEFileOrNull(); var decompiler = new CSharpDecompiler(file, assembly.GetAssemblyResolver(), options.DecompilerSettings); PortablePdbWriter.WritePdb(file, decompiler, options.DecompilerSettings, stream); } catch (OperationCanceledException) { diff --git a/ILSpy/LoadedAssembly.cs b/ILSpy/LoadedAssembly.cs index d18422a71..8c3f1d1cd 100644 --- a/ILSpy/LoadedAssembly.cs +++ b/ILSpy/LoadedAssembly.cs @@ -22,6 +22,7 @@ using System.IO; using System.Reflection.Metadata; using System.Reflection.PortableExecutable; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; using ICSharpCode.Decompiler; @@ -174,12 +175,12 @@ namespace ICSharpCode.ILSpy return module; } - private void LoadSymbols(PEFile module) + void LoadSymbols(PEFile module) { try { var reader = module.Reader; // try to open portable pdb file/embedded pdb info: - if (reader.TryOpenAssociatedPortablePdb(fileName, OpenStream, out var provider, out var pdbFileName)) { + if (TryOpenPortablePdb(module, out var provider, out var pdbFileName)) { debugInfoProvider = new PortableDebugInfoProvider(pdbFileName, provider); } else { // search for pdb in same directory as dll @@ -192,20 +193,50 @@ namespace ICSharpCode.ILSpy // TODO: use symbol cache, get symbols from microsoft } - } catch (BadImageFormatException) { + } catch (Exception ex) when (ex is BadImageFormatException || ex is COMException) { // Ignore PDB load errors } + } - Stream OpenStream(string fileName) - { - if (!File.Exists(fileName)) - return null; - var memory = new MemoryStream(); - using (var stream = File.OpenRead(fileName)) - stream.CopyTo(memory); - memory.Position = 0; - return memory; + const string LegacyPDBPrefix = "Microsoft C/C++ MSF 7.00"; + byte[] buffer = new byte[LegacyPDBPrefix.Length]; + + bool TryOpenPortablePdb(PEFile module, out MetadataReaderProvider provider, out string pdbFileName) + { + provider = null; + pdbFileName = null; + var reader = module.Reader; + foreach (var entry in reader.ReadDebugDirectory()) { + if (entry.IsPortableCodeView) { + return reader.TryOpenAssociatedPortablePdb(fileName, OpenStream, out provider, out pdbFileName); + } + if (entry.Type == DebugDirectoryEntryType.CodeView) { + string pdbDirectory = Path.GetDirectoryName(fileName); + pdbFileName = Path.Combine(pdbDirectory, Path.GetFileNameWithoutExtension(fileName) + ".pdb"); + if (File.Exists(pdbFileName)) { + Stream stream = OpenStream(pdbFileName); + if (stream.Read(buffer, 0, buffer.Length) == LegacyPDBPrefix.Length + && System.Text.Encoding.ASCII.GetString(buffer) == LegacyPDBPrefix) { + return false; + } + stream.Position = 0; + provider = MetadataReaderProvider.FromPortablePdbStream(stream); + return true; + } + } } + return false; + } + + Stream OpenStream(string fileName) + { + if (!File.Exists(fileName)) + return null; + var memory = new MemoryStream(); + using (var stream = File.OpenRead(fileName)) + stream.CopyTo(memory); + memory.Position = 0; + return memory; } [ThreadStatic]