private bool TryOpenCodeViewPortablePdb(DebugDirectoryEntry codeViewEntry, string peImageDirectory, Func <string, Stream?> pdbFileStreamProvider, out MetadataReaderProvider?provider, out string?pdbPath, ref Exception?errorToReport) { pdbPath = null; provider = null; CodeViewDebugDirectoryData data; try { data = ReadCodeViewDebugDirectoryData(codeViewEntry); } catch (Exception e) when(e is BadImageFormatException || e is IOException) { errorToReport = errorToReport ?? e; return(false); } var id = new BlobContentId(data.Guid, codeViewEntry.Stamp); // The interpretation os the path in the CodeView needs to be platform agnostic, // so that PDBs built on Windows work on Unix-like systems and vice versa. // System.IO.Path.GetFileName() on Unix-like systems doesn't treat '\' as a file name separator, // so we need a custom implementation. Also avoid throwing an exception if the path contains invalid characters, // they might not be invalid on the other platform. It's up to the FS APIs to deal with that when opening the stream. string collocatedPdbPath = PathUtilities.CombinePathWithRelativePath(peImageDirectory, PathUtilities.GetFileName(data.Path)); if (TryOpenPortablePdbFile(collocatedPdbPath, id, pdbFileStreamProvider, out provider, ref errorToReport)) { pdbPath = collocatedPdbPath; return(true); } return(false); }
/// <summary> /// Reads the data pointed to by the specified Debug Directory entry and interprets them as CodeView. /// </summary> /// <exception cref="ArgumentException"><paramref name="entry"/> is not a CodeView entry.</exception> /// <exception cref="BadImageFormatException">Bad format of the data.</exception> /// <exception cref="IOException">IO error while reading from the underlying stream.</exception> public unsafe CodeViewDebugDirectoryData ReadCodeViewDebugDirectoryData(DebugDirectoryEntry entry) { if (entry.Type != DebugDirectoryEntryType.CodeView) { throw new ArgumentException(SR.NotCodeViewEntry, nameof(entry)); } using (var block = GetDebugDirectoryEntryDataBlock(entry)) { var reader = new BlobReader(block.Pointer, block.Size); if (reader.ReadByte() != (byte)'R' || reader.ReadByte() != (byte)'S' || reader.ReadByte() != (byte)'D' || reader.ReadByte() != (byte)'S') { throw new BadImageFormatException(SR.UnexpectedCodeViewDataSignature); } Guid guid = reader.ReadGuid(); int age = reader.ReadInt32(); string path = reader.ReadUtf8NullTerminated(); // path may be padded with NULs while (reader.RemainingBytes > 0) { if (reader.ReadByte() != 0) { throw new BadImageFormatException(SR.InvalidPathPadding); } } return(new CodeViewDebugDirectoryData(guid, age, path)); } }
partial void TryOpenEmbeddedPortablePdb(DebugDirectoryEntry embeddedPdbEntry, ref bool openedEmbeddedPdb, ref MetadataReaderProvider provider, ref Exception errorToReport) { provider = null; MetadataReaderProvider candidate = null; try { candidate = ReadEmbeddedPortablePdbDebugDirectoryData(embeddedPdbEntry); // throws if headers are invalid: candidate.GetMetadataReader(); provider = candidate; openedEmbeddedPdb = true; return; } catch (Exception e) when(e is BadImageFormatException || e is IOException) { errorToReport = errorToReport ?? e; openedEmbeddedPdb = false; } finally { if (candidate == null) { candidate?.Dispose(); } } }
/// <summary> /// Reads the data pointed to by the specified Debug Directory entry and interprets them as CodeView. /// </summary> /// <exception cref="ArgumentException"><paramref name="entry"/> is not a CodeView entry.</exception> /// <exception cref="BadImageFormatException">Bad format of the data.</exception> /// <exception cref="IOException">IO error while reading from the underlying stream.</exception> /// <exception cref="InvalidOperationException">PE image not available.</exception> public CodeViewDebugDirectoryData ReadCodeViewDebugDirectoryData(DebugDirectoryEntry entry) { if (entry.Type != DebugDirectoryEntryType.CodeView) { Throw.InvalidArgument(SR.Format(SR.UnexpectedDebugDirectoryType, nameof(DebugDirectoryEntryType.CodeView)), nameof(entry)); } using (var block = GetDebugDirectoryEntryDataBlock(entry)) { return(DecodeCodeViewDebugDirectoryData(block)); } }
/// <summary> /// Reads the data pointed to by the specified Debug Directory entry and interprets them as PDB Checksum entry. /// </summary> /// <exception cref="ArgumentException"><paramref name="entry"/> is not a PDB Checksum entry.</exception> /// <exception cref="BadImageFormatException">Bad format of the data.</exception> /// <exception cref="IOException">IO error while reading from the underlying stream.</exception> /// <exception cref="InvalidOperationException">PE image not available.</exception> public PdbChecksumDebugDirectoryData ReadPdbChecksumDebugDirectoryData(DebugDirectoryEntry entry) { if (entry.Type != DebugDirectoryEntryType.PdbChecksum) { Throw.InvalidArgument(SR.Format(SR.UnexpectedDebugDirectoryType, nameof(DebugDirectoryEntryType.PdbChecksum)), nameof(entry)); } using (var block = GetDebugDirectoryEntryDataBlock(entry)) { return(DecodePdbChecksumDebugDirectoryData(block)); } }
/// <summary> /// Reads the data pointed to by the specified Debug Directory entry and interprets them as Embedded Portable PDB blob. /// </summary> /// <returns> /// Provider of a metadata reader reading Portable PDB image. /// </returns> /// <exception cref="ArgumentException"><paramref name="entry"/> is not a <see cref="DebugDirectoryEntryType.EmbeddedPortablePdb"/> entry.</exception> /// <exception cref="BadImageFormatException">Bad format of the data.</exception> /// <exception cref="InvalidOperationException">PE image not available.</exception> public MetadataReaderProvider ReadEmbeddedPortablePdbDebugDirectoryData(DebugDirectoryEntry entry) { if (entry.Type != DebugDirectoryEntryType.EmbeddedPortablePdb) { Throw.InvalidArgument(SR.Format(SR.UnexpectedDebugDirectoryType, nameof(DebugDirectoryEntryType.EmbeddedPortablePdb)), nameof(entry)); } ValidateEmbeddedPortablePdbVersion(entry); using (var block = GetDebugDirectoryEntryDataBlock(entry)) { var pdbImage = DecodeEmbeddedPortablePdbDebugDirectoryData(block); return(MetadataReaderProvider.FromPortablePdbImage(pdbImage)); } }
// internal for testing internal static void ValidateEmbeddedPortablePdbVersion(DebugDirectoryEntry entry) { // Major version encodes the version of Portable PDB format itself. // Minor version encodes the version of Embedded Portable PDB blob. // Accept any version of Portable PDB >= 1.0, // but only accept version 1.* of the Embedded Portable PDB blob. // Any breaking change in the format should rev major version of the embedded blob. ushort formatVersion = entry.MajorVersion; if (formatVersion < PortablePdbVersions.MinFormatVersion) { throw new BadImageFormatException(SR.Format(SR.UnsupportedFormatVersion, PortablePdbVersions.Format(formatVersion))); } ushort embeddedBlobVersion = entry.MinorVersion; if (embeddedBlobVersion != PortablePdbVersions.DefaultEmbeddedVersion) { throw new BadImageFormatException(SR.Format(SR.UnsupportedFormatVersion, PortablePdbVersions.Format(embeddedBlobVersion))); } }
public static CodeViewDebugDirectoryData ReadCodeViewDebugDirectoryData(Stream peStream, DebugDirectoryEntry entry) { var reader = new BinaryReader(peStream); peStream.Position = entry.DataPointer; if (reader.ReadByte() != (byte)'R' || reader.ReadByte() != (byte)'S' || reader.ReadByte() != (byte)'D' || reader.ReadByte() != (byte)'S') { throw new BadImageFormatException(); } byte[] guidBlob = new byte[16]; reader.Read(guidBlob, 0, guidBlob.Length); int age = reader.ReadInt32(); byte[] pathBlob = new byte[entry.DataSize - 24]; reader.Read(pathBlob, 0, pathBlob.Length); int terminator = Array.IndexOf(pathBlob, (byte)0); if (terminator < 0) { throw new BadImageFormatException("Path should be NUL terminated"); } for (int i = terminator + 1; i < pathBlob.Length; i++) { if (pathBlob[i] != 0) { throw new BadImageFormatException(); } } // TODO: handle unpaired surrogates string path = Encoding.UTF8.GetString(pathBlob, 0, terminator); return new CodeViewDebugDirectoryData(path, new Guid(guidBlob), age); }
/// <summary> /// Reads the data pointed to by the specified Debug Directory entry and interprets them as Embedded Portable PDB blob. /// </summary> /// <returns> /// Provider of a metadata reader reading Portable PDB image. /// </returns> /// <exception cref="ArgumentException"><paramref name="entry"/> is not a <see cref="DebugDirectoryEntryType.EmbeddedPortablePdb"/> entry.</exception> /// <exception cref="BadImageFormatException">Bad format of the data.</exception> public unsafe MetadataReaderProvider ReadEmbeddedPortablePdbDebugDirectoryData(DebugDirectoryEntry entry) { if (entry.Type != DebugDirectoryEntryType.EmbeddedPortablePdb) { throw new ArgumentException(SR.NotCodeViewEntry, nameof(entry)); } ValidateEmbeddedPortablePdbVersion(entry); using (AbstractMemoryBlock block = _peImage.GetMemoryBlock(entry.DataPointer, entry.DataSize)) { var pdbImage = DecodeEmbeddedPortablePdbDebugDirectoryData(block); return MetadataReaderProvider.FromPortablePdbImage(pdbImage); } }
/// <summary> /// Reads the data pointed to by the specified Debug Directory entry and interprets them as Embedded Portable PDB blob. /// </summary> /// <returns> /// Provider of a metadata reader reading Portable PDB image. /// </returns> /// <exception cref="ArgumentException"><paramref name="entry"/> is not a <see cref="DebugDirectoryEntryType.EmbeddedPortablePdb"/> entry.</exception> /// <exception cref="BadImageFormatException">Bad format of the data.</exception> public unsafe MetadataReaderProvider ReadEmbeddedPortablePdbDebugDirectoryData(DebugDirectoryEntry entry) { if (entry.Type != DebugDirectoryEntryType.EmbeddedPortablePdb) { throw new ArgumentException(SR.NotCodeViewEntry, nameof(entry)); } ValidateEmbeddedPortablePdbVersion(entry); using (var block = GetDebugDirectoryEntryDataBlock(entry)) { var pdbImage = DecodeEmbeddedPortablePdbDebugDirectoryData(block); return(MetadataReaderProvider.FromPortablePdbImage(pdbImage)); } }
partial void TryOpenEmbeddedPortablePdb(DebugDirectoryEntry embeddedPdbEntry, ref bool openedEmbeddedPdb, ref MetadataReaderProvider?provider, ref Exception?errorToReport);
private AbstractMemoryBlock GetDebugDirectoryEntryDataBlock(DebugDirectoryEntry entry) { int dataOffset = IsLoadedImage ? entry.DataRelativeVirtualAddress : entry.DataPointer; return(_peImage.GetMemoryBlock(dataOffset, entry.DataSize)); }
/// <summary> /// Reads the data pointed to by the specified Debug Directory entry and interprets them as Embedded Portable PDB blob. /// </summary> /// <returns> /// Provider of a metadata reader reading the embedded Portable PDB image. /// Dispose to release resources allocated for the embedded PDB. /// </returns> /// <exception cref="ArgumentException"><paramref name="entry"/> is not a <see cref="DebugDirectoryEntryType.EmbeddedPortablePdb"/> entry.</exception> /// <exception cref="BadImageFormatException">Bad format of the data.</exception> /// <exception cref="InvalidOperationException">PE image not available.</exception> public unsafe MetadataReaderProvider ReadEmbeddedPortablePdbDebugDirectoryData(DebugDirectoryEntry entry) { if (entry.Type != DebugDirectoryEntryType.EmbeddedPortablePdb) { Throw.InvalidArgument(SR.Format(SR.UnexpectedDebugDirectoryType, nameof(DebugDirectoryEntryType.EmbeddedPortablePdb)), nameof(entry)); } ValidateEmbeddedPortablePdbVersion(entry); using var block = GetDebugDirectoryEntryDataBlock(entry); return(new MetadataReaderProvider(DecodeEmbeddedPortablePdbDebugDirectoryData(block))); }
// internal for testing internal static void ValidateEmbeddedPortablePdbVersion(DebugDirectoryEntry entry) { // Major version encodes the version of Portable PDB format itself. // Minor version encodes the version of Embedded Portable PDB blob. // Accept any version of Portable PDB >= 1.0, // but only accept version 1.* of the Embedded Portable PDB blob. // Any breaking change in the format should rev major version of the embedded blob. ushort formatVersion = entry.MajorVersion; if (formatVersion < PortablePdbVersions.MinFormatVersion) { throw new BadImageFormatException(SR.Format(SR.UnsupportedFormatVersion, PortablePdbVersions.Format(formatVersion))); } ushort embeddedBlobVersion = entry.MinorVersion; if (embeddedBlobVersion != PortablePdbVersions.DefaultEmbeddedVersion) { throw new BadImageFormatException(SR.Format(SR.UnsupportedFormatVersion, PortablePdbVersions.Format(embeddedBlobVersion))); } }
/// <summary> /// Reads the data pointed to by the specified Debug Directory entry and interprets them as CodeView. /// </summary> /// <exception cref="ArgumentException"><paramref name="entry"/> is not a CodeView entry.</exception> /// <exception cref="BadImageFormatException">Bad format of the data.</exception> /// <exception cref="IOException">IO error while reading from the underlying stream.</exception> public unsafe CodeViewDebugDirectoryData ReadCodeViewDebugDirectoryData(DebugDirectoryEntry entry) { if (entry.Type != DebugDirectoryEntryType.CodeView) { throw new ArgumentException(SR.NotCodeViewEntry, nameof(entry)); } using (AbstractMemoryBlock block = _peImage.GetMemoryBlock(entry.DataPointer, entry.DataSize)) { var reader = new BlobReader(block.Pointer, block.Size); if (reader.ReadByte() != (byte)'R' || reader.ReadByte() != (byte)'S' || reader.ReadByte() != (byte)'D' || reader.ReadByte() != (byte)'S') { throw new BadImageFormatException(SR.UnexpectedCodeViewDataSignature); } Guid guid = reader.ReadGuid(); int age = reader.ReadInt32(); string path = reader.ReadUtf8NullTerminated(); // path may be padded with NULs while (reader.RemainingBytes > 0) { if (reader.ReadByte() != 0) { throw new BadImageFormatException(SR.InvalidPathPadding); } } return new CodeViewDebugDirectoryData(guid, age, path); } }
public static CodeViewDebugDirectoryData ReadCodeViewDebugDirectoryData(Stream peStream, DebugDirectoryEntry entry) { var reader = new BinaryReader(peStream); peStream.Position = entry.DataPointer; if (reader.ReadByte() != (byte)'R' || reader.ReadByte() != (byte)'S' || reader.ReadByte() != (byte)'D' || reader.ReadByte() != (byte)'S') { throw new BadImageFormatException(); } byte[] guidBlob = new byte[16]; reader.Read(guidBlob, 0, guidBlob.Length); int age = reader.ReadInt32(); byte[] pathBlob = new byte[entry.DataSize - 24]; reader.Read(pathBlob, 0, pathBlob.Length); int terminator = Array.IndexOf(pathBlob, (byte)0); if (terminator < 0) { throw new BadImageFormatException("Path should be NUL terminated"); } for (int i = terminator + 1; i < pathBlob.Length; i++) { if (pathBlob[i] != 0) { throw new BadImageFormatException(); } } // TODO: handle unpaired surrogates string path = Encoding.UTF8.GetString(pathBlob, 0, terminator); return(new CodeViewDebugDirectoryData(path, new Guid(guidBlob), age)); }
/// <summary> /// Reads the data pointed to by the specified Debug Directory entry and interprets them as PDB Checksum entry. /// </summary> /// <exception cref="ArgumentException"><paramref name="entry"/> is not a PDB Checksum entry.</exception> /// <exception cref="BadImageFormatException">Bad format of the data.</exception> /// <exception cref="IOException">IO error while reading from the underlying stream.</exception> /// <exception cref="InvalidOperationException">PE image not available.</exception> internal static PdbChecksumDebugDirectoryData ReadPdbChecksumDebugDirectoryData(this PEReader peReader, DebugDirectoryEntry entry) { if (entry.Type != PdbChecksumEntryType) { throw new ArgumentException("Unexpected debug directory entry type", nameof(entry)); } var peImage = peReader.GetEntireImage(); int dataOffset = peReader.IsLoadedImage ? entry.DataRelativeVirtualAddress : entry.DataPointer; var reader = peImage.GetReader(dataOffset, entry.DataSize); int nameLength = reader.IndexOf(0); if (nameLength <= 0) { throw new BadImageFormatException("Invalid PDB Checksum data format"); } string algorithmName = reader.ReadUTF8(nameLength); // NUL reader.Offset += 1; var checksum = reader.ReadBytes(reader.RemainingBytes).ToImmutableArray(); if (checksum.Length == 0) { throw new BadImageFormatException("Invalid PDB Checksum data format"); } return(new PdbChecksumDebugDirectoryData(algorithmName, checksum)); }
private AbstractMemoryBlock GetDebugDirectoryEntryDataBlock(DebugDirectoryEntry entry) { int dataOffset = IsLoadedImage ? entry.DataRelativeVirtualAddress : entry.DataPointer; return _peImage.GetMemoryBlock(dataOffset, entry.DataSize); }
private static OpenedReader TryOpenReaderFromEmbeddedPdb(PEReader peReader, DebugDirectoryEntry embeddedPdbEntry) { OpenedReader result = null; MetadataReaderProvider provider = null; try { // TODO: We might want to cache this provider globally (across stack traces), // since decompressing embedded PDB takes some time. provider = peReader.ReadEmbeddedPortablePdbDebugDirectoryData(embeddedPdbEntry); result = new OpenedReader(provider, provider.GetMetadataReader()); } catch (Exception e) when (e is BadImageFormatException || e is IOException) { return null; } finally { if (result == null) { provider?.Dispose(); } } return result; }
private static OpenedReader TryOpenReaderFromCodeView(PEReader peReader, DebugDirectoryEntry codeViewEntry, string assemblyPath) { OpenedReader result = null; MetadataReaderProvider provider = null; try { var data = peReader.ReadCodeViewDebugDirectoryData(codeViewEntry); string pdbPath = data.Path; if (assemblyPath != null) { try { pdbPath = Path.Combine(Path.GetDirectoryName(assemblyPath), Path.GetFileName(pdbPath)); } catch { // invalid characters in CodeView path return null; } } var pdbStream = TryOpenFile(pdbPath); if (pdbStream == null) { return null; } provider = MetadataReaderProvider.FromPortablePdbStream(pdbStream); var reader = provider.GetMetadataReader(); // Validate that the PDB matches the assembly version if (data.Age == 1 && new BlobContentId(reader.DebugMetadataHeader.Id) == new BlobContentId(data.Guid, codeViewEntry.Stamp)) { result = new OpenedReader(provider, reader); } } catch (Exception e) when (e is BadImageFormatException || e is IOException) { return null; } finally { if (result == null) { provider?.Dispose(); } } return result; }
private static void ReadPortableDebugTableEntries(PEReader peReader, out DebugDirectoryEntry codeViewEntry, out DebugDirectoryEntry embeddedPdbEntry) { // See spec: https://github.com/dotnet/corefx/blob/master/src/System.Reflection.Metadata/specs/PE-COFF.md codeViewEntry = default(DebugDirectoryEntry); embeddedPdbEntry = default(DebugDirectoryEntry); foreach (DebugDirectoryEntry entry in peReader.ReadDebugDirectory()) { if (entry.Type == DebugDirectoryEntryType.CodeView) { const ushort PortableCodeViewVersionMagic = 0x504d; if (entry.MinorVersion != PortableCodeViewVersionMagic) { continue; } codeViewEntry = entry; } else if (entry.Type == DebugDirectoryEntryType.EmbeddedPortablePdb) { embeddedPdbEntry = entry; } } }