/// <summary> /// Disposes all memory allocated by the reader. /// </summary> /// <remarks> /// <see cref="Dispose"/> can be called multiple times (but not in parallel). /// It is not safe to call <see cref="Dispose"/> in parallel with any other operation on the <see cref="PEReader"/> /// or reading from <see cref="PEMemoryBlock"/>s retrieved from the reader. /// </remarks> public void Dispose() { _lazyPEHeaders = null; _peImage?.Dispose(); _peImage = null; _lazyImageBlock?.Dispose(); _lazyImageBlock = null; _lazyMetadataBlock?.Dispose(); _lazyMetadataBlock = null; var peSectionBlocks = _lazyPESectionBlocks; if (peSectionBlocks != null) { foreach (var block in peSectionBlocks) { block?.Dispose(); } _lazyPESectionBlocks = null; } }
/// <summary> /// Gets a <see cref="MetadataReader"/> from a <see cref="MetadataReaderProvider"/>. /// </summary> /// <remarks> /// The caller must keep the <see cref="MetadataReaderProvider"/> undisposed throughout the lifetime of the metadata reader. /// /// If this method is called multiple times each call with arguments equal to the arguments passed to the previous successful call /// returns the same instance of <see cref="MetadataReader"/> as the previous call. /// </remarks> /// <exception cref="ArgumentException">The encoding of <paramref name="utf8Decoder"/> is not <see cref="UTF8Encoding"/>.</exception> /// <exception cref="PlatformNotSupportedException">The current platform is big-endian.</exception> /// <exception cref="IOException">IO error while reading from the underlying stream.</exception> /// <exception cref="ObjectDisposedException">Provider has been disposed.</exception> public unsafe MetadataReader GetMetadataReader(MetadataReaderOptions options = MetadataReaderOptions.Default, MetadataStringDecoder?utf8Decoder = null) { var cachedReader = _lazyMetadataReader; if (CanReuseReader(cachedReader, options, utf8Decoder)) { return(cachedReader !); } // If multiple threads attempt to open a metadata reader with the same options and decoder // it's cheaper to wait for the other thread to finish initializing the reader than to open // two readers and discard one. // Note that it's rare to reader the same metadata using different options. lock (_metadataReaderGuard) { cachedReader = _lazyMetadataReader; if (CanReuseReader(cachedReader, options, utf8Decoder)) { return(cachedReader !); } AbstractMemoryBlock metadata = GetMetadataBlock(); var newReader = new MetadataReader(metadata.Pointer, metadata.Size, options, utf8Decoder, memoryOwner: this); _lazyMetadataReader = newReader; return(newReader); } }
/// <summary> /// Reads all Debug Directory table entries. /// </summary> /// <exception cref="BadImageFormatException">Bad format of the entry.</exception> /// <exception cref="IOException">IO error while reading from the underlying stream.</exception> /// <exception cref="InvalidOperationException">PE image not available.</exception> public ImmutableArray <DebugDirectoryEntry> ReadDebugDirectory() { Debug.Assert(PEHeaders.PEHeader != null); var debugDirectory = PEHeaders.PEHeader.DebugTableDirectory; if (debugDirectory.Size == 0) { return(ImmutableArray <DebugDirectoryEntry> .Empty); } int position; if (!PEHeaders.TryGetDirectoryOffset(debugDirectory, out position)) { throw new BadImageFormatException(SR.InvalidDirectoryRVA); } if (debugDirectory.Size % DebugDirectoryEntry.Size != 0) { throw new BadImageFormatException(SR.InvalidDirectorySize); } using (AbstractMemoryBlock block = GetPEImage().GetMemoryBlock(position, debugDirectory.Size)) { return(ReadDebugDirectoryEntries(block.GetReader())); } }
/// <summary> /// Reads all Debug Directory table entries. /// </summary> /// <exception cref="BadImageFormatException">Bad format of the entry.</exception> /// <exception cref="IOException">IO error while reading from the underlying stream.</exception> public unsafe ImmutableArray <DebugDirectoryEntry> ReadDebugDirectory() { var debugDirectory = PEHeaders.PEHeader.DebugTableDirectory; if (debugDirectory.Size == 0) { return(ImmutableArray <DebugDirectoryEntry> .Empty); } int position; if (!PEHeaders.TryGetDirectoryOffset(debugDirectory, out position)) { throw new BadImageFormatException(SR.InvalidDirectoryRVA); } if (debugDirectory.Size % DebugDirectoryEntry.Size != 0) { throw new BadImageFormatException(SR.InvalidDirectorySize); } using (AbstractMemoryBlock block = _peImage.GetMemoryBlock(position, debugDirectory.Size)) { var reader = new BlobReader(block.Pointer, block.Size); return(ReadDebugDirectoryEntries(reader)); } }
/// <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> 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)); } }
/// <summary> /// Disposes all memory allocated by the reader. /// </summary> /// <remarks> /// <see cref="Dispose"/> can be called multiple times (but not in parallel). /// It is not safe to call <see cref="Dispose"/> in parallel with any other operation on the <see cref="MetadataReaderProvider"/> /// or reading from the underlying memory. /// </remarks> public void Dispose() { _blockProviderOpt?.Dispose(); _blockProviderOpt = null; _lazyMetadataBlock?.Dispose(); _lazyMetadataBlock = null; }
internal PEMemoryBlock(AbstractMemoryBlock block, int offset = 0) { DebugCorlib.Assert(block != null); DebugCorlib.Assert(offset >= 0 && offset < block.Size); this.block = block; this.offset = offset; }
internal PEMemoryBlock(AbstractMemoryBlock block, int offset = 0) { Debug.Assert(block != null); Debug.Assert(offset >= 0 && offset <= block.Size); _block = block; _offset = offset; }
public WinMDModule( ModuleMetadata owner, AbstractMemoryBlock memoryBlock, PEFileReader peFileReader, ReadOnlyArray<AssemblyIdentity> referencedAssemblies, WinMDScenario scenario) : base(owner, memoryBlock, peFileReader, referencedAssemblies) { this.scenario = scenario; this.winMdStartIndex = (uint)referencedAssemblies.Count - WinMdExtraRefs; }
public WinMDModule( ModuleMetadata owner, AbstractMemoryBlock memoryBlock, PEFileReader peFileReader, ReadOnlyArray <AssemblyIdentity> referencedAssemblies, WinMDScenario scenario) : base(owner, memoryBlock, peFileReader, referencedAssemblies) { this.scenario = scenario; this.winMdStartIndex = (uint)referencedAssemblies.Count - WinMdExtraRefs; }
/// <summary> /// Reads all Debug Directory table entries. /// </summary> /// <exception cref="BadImageFormatException">Bad format of the entry.</exception> public unsafe ImmutableArray <DebugDirectoryEntry> ReadDebugDirectory() { var debugDirectory = PEHeaders.PEHeader.DebugTableDirectory; if (debugDirectory.Size == 0) { return(ImmutableArray <DebugDirectoryEntry> .Empty); } int position; if (!PEHeaders.TryGetDirectoryOffset(debugDirectory, out position)) { throw new BadImageFormatException(SR.InvalidDirectoryRVA); } const int entrySize = 0x1c; if (debugDirectory.Size % entrySize != 0) { throw new BadImageFormatException(SR.InvalidDirectorySize); } using (AbstractMemoryBlock block = _peImage.GetMemoryBlock(position, debugDirectory.Size)) { var reader = new BlobReader(block.Pointer, block.Size); int entryCount = debugDirectory.Size / entrySize; var builder = ImmutableArray.CreateBuilder <DebugDirectoryEntry>(entryCount); for (int i = 0; i < entryCount; i++) { // Reserved, must be zero. int characteristics = reader.ReadInt32(); if (characteristics != 0) { throw new BadImageFormatException(SR.InvalidDebugDirectoryEntryCharacteristics); } uint stamp = reader.ReadUInt32(); ushort majorVersion = reader.ReadUInt16(); ushort minorVersion = reader.ReadUInt16(); var type = (DebugDirectoryEntryType)reader.ReadInt32(); int dataSize = reader.ReadInt32(); int dataRva = reader.ReadInt32(); int dataPointer = reader.ReadInt32(); builder.Add(new DebugDirectoryEntry(stamp, majorVersion, minorVersion, type, dataSize, dataRva, dataPointer)); } return(builder.MoveToImmutable()); } }
/// <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)); } }
// internal for testing internal static CodeViewDebugDirectoryData DecodeCodeViewDebugDirectoryData(AbstractMemoryBlock block) { var reader = block.GetReader(); 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(); return(new CodeViewDebugDirectoryData(guid, age, path)); }
/// <summary> /// Disposes all memory allocated by the reader. /// </summary> /// <remarks> /// <see cref="Dispose"/> can be called multiple times (but not in parallel). /// It is not safe to call <see cref="Dispose"/> in parallel with any other operation on the <see cref="PEReader"/> /// or reading from <see cref="PEMemoryBlock"/>s retrieved from the reader. /// </remarks> public void Dispose() { var image = _peImage; if (image != null) { image.Dispose(); _peImage = null; } var imageBlock = _lazyImageBlock; if (imageBlock != null) { imageBlock.Dispose(); _lazyImageBlock = null; } var metadataBlock = _lazyMetadataBlock; if (metadataBlock != null) { metadataBlock.Dispose(); _lazyMetadataBlock = null; } var peSectionBlocks = _lazyPESectionBlocks; if (peSectionBlocks != null) { foreach (var block in peSectionBlocks) { if (block != null) { block.Dispose(); } } _lazyPESectionBlocks = null; } }
// internal for testing internal static unsafe ImmutableArray <byte> DecodeEmbeddedPortablePdbDebugDirectoryData(AbstractMemoryBlock block) { byte[] decompressed; const int headerSize = 2 * sizeof(int); var headerReader = new BlobReader(block.Pointer, headerSize); if (headerReader.ReadUInt32() != PortablePdbVersions.DebugDirectoryEmbeddedSignature) { throw new BadImageFormatException(SR.UnexpectedEmbeddedPortablePdbDataSignature); } int decompressedSize = headerReader.ReadInt32(); try { decompressed = new byte[decompressedSize]; } catch { throw new BadImageFormatException(SR.DataTooBig); } var compressed = new ReadOnlyUnmanagedMemoryStream(block.Pointer + headerSize, block.Size - headerSize); var deflate = new DeflateStream(compressed, CompressionMode.Decompress, leaveOpen: true); if (decompressedSize > 0) { int actualLength; try { actualLength = deflate.TryReadAll(decompressed, 0, decompressed.Length); } catch (InvalidDataException e) { throw new BadImageFormatException(e.Message, e.InnerException); } if (actualLength != decompressed.Length) { throw new BadImageFormatException(SR.SizeMismatch); } } // Check that there is no more compressed data left, // in case the decompressed size specified in the header is smaller // than the actual decompressed size of the data. if (deflate.ReadByte() != -1) { throw new BadImageFormatException(SR.SizeMismatch); } return(ImmutableByteArrayInterop.DangerousCreateFromUnderlyingArray(ref decompressed)); }
/// <summary> /// Creates a Portable Executable reader over a PE image of the given size beginning at the stream's current position. /// </summary> /// <param name="peStream">PE image stream.</param> /// <param name="size">PE image size.</param> /// <param name="options"> /// Options specifying how sections of the PE image are read from the stream. /// /// Unless <see cref="PEStreamOptions.LeaveOpen"/> is specified, ownership of the stream is transferred to the <see cref="PEReader"/> /// upon successful argument validation. It will be disposed by the <see cref="PEReader"/> and the caller must not manipulate it. /// /// Unless <see cref="PEStreamOptions.PrefetchMetadata"/> or <see cref="PEStreamOptions.PrefetchEntireImage"/> is specified no data /// is read from the stream during the construction of the <see cref="PEReader"/>. Furthermore, the stream must not be manipulated /// by caller while the <see cref="PEReader"/> is alive and undisposed. /// /// If <see cref="PEStreamOptions.PrefetchMetadata"/> or <see cref="PEStreamOptions.PrefetchEntireImage"/>, the <see cref="PEReader"/> /// will have read all of the data requested during construction. As such, if <see cref="PEStreamOptions.LeaveOpen"/> is also /// specified, the caller retains full ownership of the stream and is assured that it will not be manipulated by the <see cref="PEReader"/> /// after construction. /// </param> /// <exception cref="ArgumentOutOfRangeException">Size is negative or extends past the end of the stream.</exception> /// <exception cref="IOException">Error reading from the stream (only when prefetching data).</exception> /// <exception cref="BadImageFormatException"><see cref="PEStreamOptions.PrefetchMetadata"/> is specified and the PE headers of the image are invalid.</exception> public unsafe PEReader(Stream peStream, PEStreamOptions options, int size) { if (peStream == null) { throw new ArgumentNullException(nameof(peStream)); } if (!peStream.CanRead || !peStream.CanSeek) { throw new ArgumentException(SR.StreamMustSupportReadAndSeek, nameof(peStream)); } if (!options.IsValid()) { throw new ArgumentOutOfRangeException(nameof(options)); } IsLoadedImage = (options & PEStreamOptions.IsLoadedImage) != 0; long start = peStream.Position; int actualSize = StreamExtensions.GetAndValidateSize(peStream, size, nameof(peStream)); bool closeStream = true; try { bool isFileStream = FileStreamReadLightUp.IsFileStream(peStream); if ((options & (PEStreamOptions.PrefetchMetadata | PEStreamOptions.PrefetchEntireImage)) == 0) { _peImage = new StreamMemoryBlockProvider(peStream, start, actualSize, isFileStream, (options & PEStreamOptions.LeaveOpen) != 0); closeStream = false; } else { // Read in the entire image or metadata blob: if ((options & PEStreamOptions.PrefetchEntireImage) != 0) { var imageBlock = StreamMemoryBlockProvider.ReadMemoryBlockNoLock(peStream, isFileStream, start, actualSize); _lazyImageBlock = imageBlock; _peImage = new ExternalMemoryBlockProvider(imageBlock.Pointer, imageBlock.Size); // if the caller asked for metadata initialize the PE headers (calculates metadata offset): if ((options & PEStreamOptions.PrefetchMetadata) != 0) { InitializePEHeaders(); } } else { // The peImage is left null, but the lazyMetadataBlock is initialized up front. _lazyPEHeaders = new PEHeaders(peStream); _lazyMetadataBlock = StreamMemoryBlockProvider.ReadMemoryBlockNoLock(peStream, isFileStream, _lazyPEHeaders.MetadataStartOffset, _lazyPEHeaders.MetadataSize); } // We read all we need, the stream is going to be closed. } } finally { if (closeStream && (options & PEStreamOptions.LeaveOpen) == 0) { peStream.Dispose(); } } }
// internal for testing internal static PdbChecksumDebugDirectoryData DecodePdbChecksumDebugDirectoryData(AbstractMemoryBlock block) { var reader = block.GetReader(); var algorithmName = reader.ReadUtf8NullTerminated(); byte[]? checksum = reader.ReadBytes(reader.RemainingBytes); if (algorithmName.Length == 0 || checksum.Length == 0) { throw new BadImageFormatException(SR.InvalidPdbChecksumDataFormat); } return(new PdbChecksumDebugDirectoryData( algorithmName, ImmutableByteArrayInterop.DangerousCreateFromUnderlyingArray(ref checksum))); }
private MetadataReaderProvider(AbstractMemoryBlock metadataBlock) { Debug.Assert(metadataBlock != null); _lazyMetadataBlock = metadataBlock; }
// internal for testing internal static unsafe NativeHeapMemoryBlock DecodeEmbeddedPortablePdbDebugDirectoryData(AbstractMemoryBlock block) { NativeHeapMemoryBlock?decompressed; var headerReader = block.GetReader(); if (headerReader.ReadUInt32() != PortablePdbVersions.DebugDirectoryEmbeddedSignature) { throw new BadImageFormatException(SR.UnexpectedEmbeddedPortablePdbDataSignature); } int decompressedSize = headerReader.ReadInt32(); try { decompressed = new NativeHeapMemoryBlock(decompressedSize); } catch { throw new BadImageFormatException(SR.DataTooBig); } bool success = false; try { var compressed = new ReadOnlyUnmanagedMemoryStream(headerReader.CurrentPointer, headerReader.RemainingBytes); var deflate = new DeflateStream(compressed, CompressionMode.Decompress, leaveOpen: true); if (decompressedSize > 0) { int actualLength; try { #if NETCOREAPP3_0_OR_GREATER actualLength = deflate.TryReadAll(new Span <byte>(decompressed.Pointer, decompressed.Size)); #else using var decompressedStream = new UnmanagedMemoryStream(decompressed.Pointer, decompressed.Size, decompressed.Size, FileAccess.Write); deflate.CopyTo(decompressedStream); actualLength = (int)decompressedStream.Position; #endif } catch (Exception e) { throw new BadImageFormatException(e.Message, e.InnerException); } if (actualLength != decompressed.Size) { throw new BadImageFormatException(SR.SizeMismatch); } } // Check that there is no more compressed data left, // in case the decompressed size specified in the header is smaller // than the actual decompressed size of the data. if (deflate.ReadByte() != -1) { throw new BadImageFormatException(SR.SizeMismatch); } success = true; } finally { if (!success) { decompressed.Dispose(); } } return(decompressed); }
private unsafe PEReader(Stream peStream, PEStreamOptions options, int?sizeOpt) { if (peStream == null) { throw new ArgumentNullException("peStream"); } if (!peStream.CanRead || !peStream.CanSeek) { throw new ArgumentException(MetadataResources.StreamMustSupportReadAndSeek, "peStream"); } if (!options.IsValid()) { throw new ArgumentOutOfRangeException("options"); } long start = peStream.Position; int size = PEBinaryReader.GetAndValidateSize(peStream, sizeOpt); bool closeStream = true; try { bool isFileStream = FileStreamReadLightUp.IsFileStream(peStream); if ((options & (PEStreamOptions.PrefetchMetadata | PEStreamOptions.PrefetchEntireImage)) == 0) { this.peImage = new StreamMemoryBlockProvider(peStream, start, size, isFileStream, (options & PEStreamOptions.LeaveOpen) != 0); closeStream = false; } else { // Read in the entire image or metadata blob: if ((options & PEStreamOptions.PrefetchEntireImage) != 0) { var imageBlock = StreamMemoryBlockProvider.ReadMemoryBlockNoLock(peStream, isFileStream, 0, (int)Math.Min(peStream.Length, int.MaxValue)); this.lazyImageBlock = imageBlock; this.peImage = new ExternalMemoryBlockProvider(imageBlock.Pointer, imageBlock.Size); // if the caller asked for metadata initialize the PE headers (calculates metadata offset): if ((options & PEStreamOptions.PrefetchMetadata) != 0) { InitializePEHeaders(); } } else { // The peImage is left null, but the lazyMetadataBlock is initialized up front. this.lazyPEHeaders = new PEHeaders(peStream); this.lazyMetadataBlock = StreamMemoryBlockProvider.ReadMemoryBlockNoLock(peStream, isFileStream, lazyPEHeaders.MetadataStartOffset, lazyPEHeaders.MetadataSize); } // We read all we need, the stream is going to be closed. } } finally { if (closeStream && (options & PEStreamOptions.LeaveOpen) == 0) { peStream.Dispose(); } } }
internal static unsafe ImmutableArray <byte> DecodeEmbeddedPortablePdbDebugDirectoryData(AbstractMemoryBlock block) { byte[] decompressed; var headerReader = block.GetReader(); if (headerReader.ReadUInt32() != PortablePdbVersions.DebugDirectoryEmbeddedSignature) { throw new BadImageFormatException("UnexpectedEmbeddedPortablePdbDataSignature"); } int decompressedSize = headerReader.ReadInt32(); try { decompressed = new byte[decompressedSize]; } catch { throw new BadImageFormatException("DataTooBig"); } var compressed = new ReadOnlyUnmanagedMemoryStream(headerReader.CurrentPointer, headerReader.RemainingBytes); var deflate = new DeflateStream(compressed, CompressionMode.Decompress, leaveOpen: true); if (decompressedSize > 0) { int actualLength; try { actualLength = deflate.TryReadAll(decompressed, 0, decompressed.Length); } catch (InvalidDataException e) { throw new BadImageFormatException(e.Message, e.InnerException); } if (actualLength != decompressed.Length) { throw new BadImageFormatException("SizeMismatch"); } } // Check that there is no more compressed data left, // in case the decompressed size specified in the header is smaller // than the actual decompressed size of the data. if (deflate.ReadByte() != -1) { throw new BadImageFormatException("SizeMismatch"); } return(new ImmutableArray <byte>(decompressed)); }
/// <summary> /// Gets a <see cref="MetadataReader"/> from a <see cref="MetadataReaderProvider"/>. /// </summary> /// <remarks> /// The caller must keep the <see cref="MetadataReaderProvider"/> alive and undisposed throughout the lifetime of the metadata reader. /// </remarks> /// <exception cref="ArgumentException">The encoding of <paramref name="utf8Decoder"/> is not <see cref="UTF8Encoding"/>.</exception> /// <exception cref="PlatformNotSupportedException">The current platform is big-endian.</exception> /// <exception cref="IOException">IO error while reading from the underlying stream.</exception> public unsafe MetadataReader GetMetadataReader(MetadataReaderOptions options = MetadataReaderOptions.Default, MetadataStringDecoder utf8Decoder = null) { AbstractMemoryBlock metadata = GetMetadataBlock(); return(new MetadataReader(metadata.Pointer, metadata.Size, options, utf8Decoder)); }