150 lines
4.9 KiB
C#
150 lines
4.9 KiB
C#
// dnlib: See LICENSE.txt for more info
|
|
|
|
using System;
|
|
using System.IO;
|
|
using System.Runtime.InteropServices;
|
|
using System.Text;
|
|
using dnlib.DotNet.MD;
|
|
using dnlib.DotNet.Pdb.Symbols;
|
|
using dnlib.IO;
|
|
|
|
namespace dnlib.DotNet.Pdb {
|
|
static class SymbolReaderFactory {
|
|
public static SymbolReader CreateFromAssemblyFile(PdbReaderOptions options, Metadata metadata, string assemblyFileName) {
|
|
var pdbContext = new PdbReaderContext(metadata.PEImage, options);
|
|
if (!pdbContext.HasDebugInfo)
|
|
return null;
|
|
if (!pdbContext.TryGetCodeViewData(out var guid, out uint age, out var pdbWindowsFilename))
|
|
return null;
|
|
|
|
string pdbFilename;
|
|
int index = pdbWindowsFilename.LastIndexOfAny(windowsPathSepChars);
|
|
if (index >= 0)
|
|
pdbFilename = pdbWindowsFilename.Substring(index + 1);
|
|
else
|
|
pdbFilename = pdbWindowsFilename;
|
|
|
|
string fileToCheck;
|
|
try {
|
|
fileToCheck = assemblyFileName == string.Empty ? pdbFilename : Path.Combine(Path.GetDirectoryName(assemblyFileName), pdbFilename);
|
|
if (!File.Exists(fileToCheck)) {
|
|
var ext = Path.GetExtension(pdbFilename);
|
|
if (string.IsNullOrEmpty(ext))
|
|
ext = "pdb";
|
|
fileToCheck = Path.ChangeExtension(assemblyFileName, ext);
|
|
}
|
|
}
|
|
catch (ArgumentException) {
|
|
return null;// Invalid filename
|
|
}
|
|
return Create(options, metadata, fileToCheck);
|
|
}
|
|
static readonly char[] windowsPathSepChars = new char[] { '\\', '/' };
|
|
|
|
public static SymbolReader Create(PdbReaderOptions options, Metadata metadata, string pdbFileName) {
|
|
var pdbContext = new PdbReaderContext(metadata.PEImage, options);
|
|
if (!pdbContext.HasDebugInfo)
|
|
return null;
|
|
return CreateCore(pdbContext, metadata, DataReaderFactoryUtils.TryCreateDataReaderFactory(pdbFileName));
|
|
}
|
|
|
|
public static SymbolReader Create(PdbReaderOptions options, Metadata metadata, byte[] pdbData) {
|
|
var pdbContext = new PdbReaderContext(metadata.PEImage, options);
|
|
if (!pdbContext.HasDebugInfo)
|
|
return null;
|
|
return CreateCore(pdbContext, metadata, ByteArrayDataReaderFactory.Create(pdbData, filename: null));
|
|
}
|
|
|
|
public static SymbolReader Create(PdbReaderOptions options, Metadata metadata, DataReaderFactory pdbStream) {
|
|
var pdbContext = new PdbReaderContext(metadata.PEImage, options);
|
|
return CreateCore(pdbContext, metadata, pdbStream);
|
|
}
|
|
|
|
static SymbolReader CreateCore(PdbReaderContext pdbContext, Metadata metadata, DataReaderFactory pdbStream) {
|
|
SymbolReader symReader = null;
|
|
bool error = true;
|
|
try {
|
|
if (!pdbContext.HasDebugInfo)
|
|
return null;
|
|
|
|
#if NETSTANDARD || NETCOREAPP
|
|
var isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
|
|
#else
|
|
var isWindows = true;
|
|
#endif
|
|
|
|
if ((pdbContext.Options & PdbReaderOptions.MicrosoftComReader) != 0 && isWindows && pdbStream is not null && IsWindowsPdb(pdbStream.CreateReader()))
|
|
symReader = Dss.SymbolReaderWriterFactory.Create(pdbContext, metadata, pdbStream);
|
|
else
|
|
symReader = CreateManaged(pdbContext, metadata, pdbStream);
|
|
|
|
if (symReader is not null) {
|
|
error = false;
|
|
return symReader;
|
|
}
|
|
}
|
|
catch (IOException) {
|
|
}
|
|
finally {
|
|
if (error) {
|
|
pdbStream?.Dispose();
|
|
symReader?.Dispose();
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
static bool IsWindowsPdb(DataReader reader) {
|
|
const string SIG = "Microsoft C/C++ MSF 7.00\r\n\u001ADS\0";
|
|
if (!reader.CanRead(SIG.Length))
|
|
return false;
|
|
return reader.ReadString(SIG.Length, Encoding.ASCII) == SIG;
|
|
}
|
|
|
|
public static SymbolReader TryCreateEmbeddedPdbReader(PdbReaderOptions options, Metadata metadata) {
|
|
var pdbContext = new PdbReaderContext(metadata.PEImage, options);
|
|
if (!pdbContext.HasDebugInfo)
|
|
return null;
|
|
return TryCreateEmbeddedPortablePdbReader(pdbContext, metadata);
|
|
}
|
|
|
|
static SymbolReader CreateManaged(PdbReaderContext pdbContext, Metadata metadata, DataReaderFactory pdbStream) {
|
|
try {
|
|
// Embedded PDBs have priority
|
|
var embeddedReader = TryCreateEmbeddedPortablePdbReader(pdbContext, metadata);
|
|
if (embeddedReader is not null) {
|
|
pdbStream?.Dispose();
|
|
return embeddedReader;
|
|
}
|
|
|
|
return CreateManagedCore(pdbContext, pdbStream);
|
|
}
|
|
catch {
|
|
pdbStream?.Dispose();
|
|
throw;
|
|
}
|
|
}
|
|
|
|
static SymbolReader CreateManagedCore(PdbReaderContext pdbContext, DataReaderFactory pdbStream) {
|
|
if (pdbStream is null)
|
|
return null;
|
|
try {
|
|
var reader = pdbStream.CreateReader();
|
|
if (reader.Length >= 4) {
|
|
uint sig = reader.ReadUInt32();
|
|
if (sig == 0x424A5342)
|
|
return Portable.SymbolReaderFactory.TryCreate(pdbContext, pdbStream, isEmbeddedPortablePdb: false);
|
|
return Managed.SymbolReaderFactory.Create(pdbContext, pdbStream);
|
|
}
|
|
}
|
|
catch (IOException) {
|
|
}
|
|
pdbStream?.Dispose();
|
|
return null;
|
|
}
|
|
|
|
static SymbolReader TryCreateEmbeddedPortablePdbReader(PdbReaderContext pdbContext, Metadata metadata) =>
|
|
Portable.SymbolReaderFactory.TryCreateEmbeddedPortablePdbReader(pdbContext, metadata);
|
|
}
|
|
}
|