mirror of
https://github.com/icsharpcode/ILSpy.git
synced 2026-10-12 02:15:58 +00:00
Fix #1676: Add pdb support to powershell and command-line frontends
derived from work by @an-dr-eas-k in #1679
This commit is contained in:
@@ -3,6 +3,7 @@
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>netcoreapp2.1</TargetFramework>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<IsPackable>true</IsPackable>
|
||||
<PackAsTool>true</PackAsTool>
|
||||
<AssemblyName>ilspycmd</AssemblyName>
|
||||
@@ -12,7 +13,7 @@
|
||||
<Copyright>Copyright 2011-2019 AlphaSierraPapa</Copyright>
|
||||
<PackageProjectUrl>https://github.com/icsharpcode/ILSpy/</PackageProjectUrl>
|
||||
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
||||
<PackageIcon>ILSpyCmdNuGetPackageIcon.png</PackageIcon>
|
||||
<PackageIcon>ILSpyCmdNuGetPackageIcon.png</PackageIcon>
|
||||
<RepositoryUrl>https://github.com/icsharpcode/ILSpy/</RepositoryUrl>
|
||||
<Company />
|
||||
<AssemblyVersion>6.0.0.0</AssemblyVersion>
|
||||
@@ -26,6 +27,12 @@
|
||||
<WarningsAsErrors>NU1605</WarningsAsErrors>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Include="..\ICSharpCode.Decompiler.PdbProvider.Cecil\MonoCecilDebugInfoProvider.cs" Link="MonoCecilDebugInfoProvider.cs" />
|
||||
<Compile Include="..\ILSpy\DebugInfo\PortableDebugInfoProvider.cs" Link="PortableDebugInfoProvider.cs" />
|
||||
<Compile Include="..\ILSpy\DebugInfo\DebugInfoUtils.cs" Link="DebugInfoUtils.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="ILSpyCmdNuGetPackageIcon.png" Pack="true" PackagePath="\" />
|
||||
</ItemGroup>
|
||||
@@ -45,6 +52,7 @@
|
||||
<PackageReference Include="System.Runtime.Handles" Version="4.3.0" />
|
||||
<PackageReference Include="System.Runtime.InteropServices" Version="4.3.0" />
|
||||
<PackageReference Include="System.Text.Encoding.Extensions" Version="4.3.0" />
|
||||
<PackageReference Include="Mono.Cecil" Version="0.10.3" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -12,6 +12,7 @@ using System.Threading;
|
||||
using System.Reflection.Metadata;
|
||||
using System.Reflection.PortableExecutable;
|
||||
using ICSharpCode.Decompiler.DebugInfo;
|
||||
using ICSharpCode.Decompiler.PdbProvider;
|
||||
// ReSharper disable All
|
||||
|
||||
namespace ICSharpCode.Decompiler.Console
|
||||
@@ -45,9 +46,13 @@ Remarks:
|
||||
[Option("-il|--ilcode", "Show IL code.", CommandOptionType.NoValue)]
|
||||
public bool ShowILCodeFlag { get; }
|
||||
|
||||
[Option("-d|--debuginfo", "Generate PDB.", CommandOptionType.NoValue)]
|
||||
[Option("-genpdb", "Generate PDB.", CommandOptionType.NoValue)]
|
||||
public bool CreateDebugInfoFlag { get; }
|
||||
|
||||
[FileExistsOrNull]
|
||||
[Option("-usepdb", "Use PDB.", CommandOptionType.SingleOrNoValue)]
|
||||
public (bool IsSet, string Value) InputPDBFile { get; }
|
||||
|
||||
[Option("-l|--list <entity-type(s)>", "Lists all entities of the specified type(s). Valid types: c(lass), i(interface), s(truct), d(elegate), e(num)", CommandOptionType.MultipleValue)]
|
||||
public string[] EntityTypes { get; } = new string[0];
|
||||
|
||||
@@ -142,7 +147,9 @@ Remarks:
|
||||
foreach (var path in ReferencePaths) {
|
||||
resolver.AddSearchDirectory(path);
|
||||
}
|
||||
return new CSharpDecompiler(assemblyFileName, resolver, GetSettings());
|
||||
return new CSharpDecompiler(assemblyFileName, resolver, GetSettings()) {
|
||||
DebugInfoProvider = TryLoadPDB(module)
|
||||
};
|
||||
}
|
||||
|
||||
int ListContent(string assemblyFileName, TextWriter output, ISet<TypeKind> kinds)
|
||||
@@ -175,6 +182,7 @@ Remarks:
|
||||
resolver.AddSearchDirectory(path);
|
||||
}
|
||||
decompiler.AssemblyResolver = resolver;
|
||||
decompiler.DebugInfoProvider = TryLoadPDB(module);
|
||||
decompiler.DecompileProject(module, outputDirectory);
|
||||
return 0;
|
||||
}
|
||||
@@ -211,5 +219,16 @@ Remarks:
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
IDebugInfoProvider TryLoadPDB(PEFile module)
|
||||
{
|
||||
if (InputPDBFile.IsSet) {
|
||||
if (InputPDBFile.Value == null)
|
||||
return DebugInfoUtils.LoadSymbols(module);
|
||||
return DebugInfoUtils.FromFile(module, InputPDBFile.Value);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,19 +1,38 @@
|
||||
using System;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.IO;
|
||||
|
||||
namespace ICSharpCode.Decompiler.Console
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Class)]
|
||||
public class ProjectOptionRequiresOutputDirectoryValidationAttribute : ValidationAttribute
|
||||
public sealed class ProjectOptionRequiresOutputDirectoryValidationAttribute : ValidationAttribute
|
||||
{
|
||||
public ProjectOptionRequiresOutputDirectoryValidationAttribute()
|
||||
{
|
||||
}
|
||||
|
||||
protected override ValidationResult IsValid(object value, ValidationContext context)
|
||||
{
|
||||
if (value is ILSpyCmdProgram obj) {
|
||||
if (obj.CreateCompilableProjectFlag && String.IsNullOrEmpty(obj.OutputDirectory)) {
|
||||
if (obj.CreateCompilableProjectFlag && string.IsNullOrEmpty(obj.OutputDirectory)) {
|
||||
return new ValidationResult("--project cannot be used unless --outputdir is also specified");
|
||||
}
|
||||
}
|
||||
return ValidationResult.Success;
|
||||
}
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Property)]
|
||||
public sealed class FileExistsOrNullAttribute : ValidationAttribute
|
||||
{
|
||||
protected override ValidationResult IsValid(object value, ValidationContext context)
|
||||
{
|
||||
var s = value as string;
|
||||
if (string.IsNullOrEmpty(s))
|
||||
return ValidationResult.Success;
|
||||
if (File.Exists(s))
|
||||
return ValidationResult.Success;
|
||||
return new ValidationResult($"File '{s}' does not exist!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ using Mono.Cecil;
|
||||
using Mono.Cecil.Pdb;
|
||||
using SRM = System.Reflection.Metadata;
|
||||
|
||||
namespace ICSharpCode.Decompiler.PdbProvider.Cecil
|
||||
namespace ICSharpCode.Decompiler.PdbProvider
|
||||
{
|
||||
public class MonoCecilDebugInfoProvider : IDebugInfoProvider
|
||||
{
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Management.Automation;
|
||||
using System.Reflection.PortableExecutable;
|
||||
using System.Text;
|
||||
using ICSharpCode.Decompiler.CSharp;
|
||||
using ICSharpCode.Decompiler.Metadata;
|
||||
using ICSharpCode.Decompiler.PdbProvider;
|
||||
|
||||
namespace ICSharpCode.Decompiler.PowerShell
|
||||
{
|
||||
@@ -24,18 +28,25 @@ namespace ICSharpCode.Decompiler.PowerShell
|
||||
[Parameter(HelpMessage = "Remove dead code")]
|
||||
public bool RemoveDeadCode { get; set; }
|
||||
|
||||
[Parameter(HelpMessage = "Use PDB")]
|
||||
public string PDBFilePath { get; set; }
|
||||
|
||||
protected override void ProcessRecord()
|
||||
{
|
||||
string path = GetUnresolvedProviderPathFromPSPath(LiteralPath);
|
||||
|
||||
try {
|
||||
var module = new PEFile(LiteralPath, new FileStream(LiteralPath, FileMode.Open, FileAccess.Read), PEStreamOptions.Default);
|
||||
var debugInfo = DebugInfoUtils.FromFile(module, PDBFilePath);
|
||||
var decompiler = new CSharpDecompiler(path, new DecompilerSettings(LanguageVersion) {
|
||||
ThrowOnAssemblyResolveErrors = false,
|
||||
RemoveDeadCode = RemoveDeadCode,
|
||||
RemoveDeadStores = RemoveDeadStores
|
||||
RemoveDeadStores = RemoveDeadStores,
|
||||
UseDebugSymbols = debugInfo != null,
|
||||
ShowDebugInfo = debugInfo != null,
|
||||
});
|
||||
decompiler.DebugInfoProvider = debugInfo;
|
||||
WriteObject(decompiler);
|
||||
|
||||
} catch (Exception e) {
|
||||
WriteVerbose(e.ToString());
|
||||
WriteError(new ErrorRecord(e, ErrorIds.AssemblyLoadFailed, ErrorCategory.OperationStopped, null));
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||
<RootNamespace>ICSharpCode.Decompiler.PowerShell</RootNamespace>
|
||||
</PropertyGroup>
|
||||
@@ -9,6 +10,13 @@
|
||||
<ItemGroup>
|
||||
<PackageReference Include="PowerShellStandard.Library" Version="5.1.0" />
|
||||
<PackageReference Include="ICSharpCode.Decompiler" Version="6.0.0.5420-preview1" />
|
||||
<PackageReference Include="Mono.Cecil" Version="0.10.3" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Include="..\ICSharpCode.Decompiler.PdbProvider.Cecil\MonoCecilDebugInfoProvider.cs" Link="MonoCecilDebugInfoProvider.cs" />
|
||||
<Compile Include="..\ILSpy\DebugInfo\PortableDebugInfoProvider.cs" Link="PortableDebugInfoProvider.cs" />
|
||||
<Compile Include="..\ILSpy\DebugInfo\DebugInfoUtils.cs" Link="DebugInfoUtils.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -0,0 +1,113 @@
|
||||
// Copyright (c) 2018 Siegfried Pammer
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
||||
// software and associated documentation files (the "Software"), to deal in the Software
|
||||
// without restriction, including without limitation the rights to use, copy, modify, merge,
|
||||
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
|
||||
// to whom the Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all copies or
|
||||
// substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
|
||||
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection.Metadata;
|
||||
using System.Reflection.PortableExecutable;
|
||||
using System.Runtime.InteropServices;
|
||||
using ICSharpCode.Decompiler.DebugInfo;
|
||||
using ICSharpCode.Decompiler.Metadata;
|
||||
|
||||
namespace ICSharpCode.Decompiler.PdbProvider
|
||||
{
|
||||
static class DebugInfoUtils
|
||||
{
|
||||
public static IDebugInfoProvider LoadSymbols(PEFile module)
|
||||
{
|
||||
try {
|
||||
var reader = module.Reader;
|
||||
// try to open portable pdb file/embedded pdb info:
|
||||
if (TryOpenPortablePdb(module, out var provider, out var pdbFileName)) {
|
||||
return new PortableDebugInfoProvider(pdbFileName, provider);
|
||||
} else {
|
||||
// search for pdb in same directory as dll
|
||||
pdbFileName = Path.Combine(Path.GetDirectoryName(module.FileName), Path.GetFileNameWithoutExtension(module.FileName) + ".pdb");
|
||||
if (File.Exists(pdbFileName)) {
|
||||
return new MonoCecilDebugInfoProvider(module, pdbFileName);
|
||||
}
|
||||
}
|
||||
} catch (Exception ex) when (ex is BadImageFormatException || ex is COMException) {
|
||||
// Ignore PDB load errors
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static IDebugInfoProvider FromFile(PEFile module, string pdbFileName)
|
||||
{
|
||||
if (string.IsNullOrEmpty(pdbFileName))
|
||||
return null;
|
||||
|
||||
Stream stream = OpenStream(pdbFileName);
|
||||
if (stream == null)
|
||||
return null;
|
||||
|
||||
if (stream.Read(buffer, 0, buffer.Length) == LegacyPDBPrefix.Length
|
||||
&& System.Text.Encoding.ASCII.GetString(buffer) == LegacyPDBPrefix) {
|
||||
stream.Position = 0;
|
||||
return new MonoCecilDebugInfoProvider(module, pdbFileName);
|
||||
} else {
|
||||
stream.Position = 0;
|
||||
var provider = MetadataReaderProvider.FromPortablePdbStream(stream);
|
||||
return new PortableDebugInfoProvider(pdbFileName, provider);
|
||||
}
|
||||
}
|
||||
|
||||
const string LegacyPDBPrefix = "Microsoft C/C++ MSF 7.00";
|
||||
static readonly byte[] buffer = new byte[LegacyPDBPrefix.Length];
|
||||
|
||||
static 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(module.FileName, OpenStream, out provider, out pdbFileName);
|
||||
}
|
||||
if (entry.Type == DebugDirectoryEntryType.CodeView) {
|
||||
string pdbDirectory = Path.GetDirectoryName(module.FileName);
|
||||
pdbFileName = Path.Combine(pdbDirectory, Path.GetFileNameWithoutExtension(module.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;
|
||||
}
|
||||
|
||||
static 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -20,9 +20,8 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection.Metadata;
|
||||
using ICSharpCode.Decompiler.DebugInfo;
|
||||
using ICSharpCode.Decompiler.Metadata;
|
||||
|
||||
namespace ICSharpCode.ILSpy.DebugInfo
|
||||
namespace ICSharpCode.Decompiler.PdbProvider
|
||||
{
|
||||
class PortableDebugInfoProvider : IDebugInfoProvider
|
||||
{
|
||||
|
||||
@@ -135,6 +135,7 @@
|
||||
<Compile Include="CreateListDialog.xaml.cs">
|
||||
<DependentUpon>CreateListDialog.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="DebugInfo\DebugInfoUtils.cs" />
|
||||
<Compile Include="DebugInfo\PortableDebugInfoProvider.cs" />
|
||||
<Compile Include="DebugSteps.xaml.cs">
|
||||
<DependentUpon>DebugSteps.xaml</DependentUpon>
|
||||
|
||||
+2
-67
@@ -28,10 +28,9 @@ using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using ICSharpCode.Decompiler.DebugInfo;
|
||||
using ICSharpCode.Decompiler.Metadata;
|
||||
using ICSharpCode.Decompiler.PdbProvider.Cecil;
|
||||
using ICSharpCode.Decompiler.PdbProvider;
|
||||
using ICSharpCode.Decompiler.TypeSystem;
|
||||
using ICSharpCode.Decompiler.TypeSystem.Implementation;
|
||||
using ICSharpCode.ILSpy.DebugInfo;
|
||||
using ICSharpCode.ILSpy.Options;
|
||||
|
||||
namespace ICSharpCode.ILSpy
|
||||
@@ -167,7 +166,7 @@ namespace ICSharpCode.ILSpy
|
||||
|
||||
if (DecompilerSettingsPanel.CurrentDecompilerSettings.UseDebugSymbols) {
|
||||
try {
|
||||
LoadSymbols(module);
|
||||
debugInfoProvider = DebugInfoUtils.LoadSymbols(module);
|
||||
} catch (IOException) {
|
||||
} catch (UnauthorizedAccessException) {
|
||||
} catch (InvalidOperationException) {
|
||||
@@ -180,70 +179,6 @@ namespace ICSharpCode.ILSpy
|
||||
return module;
|
||||
}
|
||||
|
||||
void LoadSymbols(PEFile module)
|
||||
{
|
||||
try {
|
||||
var reader = module.Reader;
|
||||
// try to open portable pdb file/embedded pdb info:
|
||||
if (TryOpenPortablePdb(module, out var provider, out var pdbFileName)) {
|
||||
debugInfoProvider = new PortableDebugInfoProvider(pdbFileName, provider);
|
||||
} else {
|
||||
// search for pdb in same directory as dll
|
||||
string pdbDirectory = Path.GetDirectoryName(fileName);
|
||||
pdbFileName = Path.Combine(pdbDirectory, Path.GetFileNameWithoutExtension(fileName) + ".pdb");
|
||||
if (File.Exists(pdbFileName)) {
|
||||
debugInfoProvider = new MonoCecilDebugInfoProvider(module, pdbFileName);
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: use symbol cache, get symbols from microsoft
|
||||
}
|
||||
} catch (Exception ex) when (ex is BadImageFormatException || ex is COMException) {
|
||||
// Ignore PDB load errors
|
||||
}
|
||||
}
|
||||
|
||||
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]
|
||||
static int assemblyLoadDisableCount;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user