internal static unsafe MetadataReader GetMetadataReader(byte[] peImage, out int metadataStartOffset, bool isModule = false, MetadataReaderOptions options = MetadataReaderOptions.Default, MetadataStringDecoder decoder = null) { GCHandle pinned = GetPinnedPEImage(peImage); var headers = new PEHeaders(new MemoryStream(peImage)); metadataStartOffset = headers.MetadataStartOffset; return new MetadataReader((byte*)pinned.AddrOfPinnedObject() + headers.MetadataStartOffset, headers.MetadataSize, options, decoder); }
internal static unsafe MetadataReader GetMetadataReader(byte[] peImage, out int metadataStartOffset, bool isModule = false, MetadataStringDecoder decoder = null) { GCHandle pinned; if (!peImages.TryGetValue(peImage, out pinned)) { peImages.Add(peImage, pinned = GCHandle.Alloc(peImage, GCHandleType.Pinned)); } var headers = new PEHeaders(new MemoryStream(peImage)); metadataStartOffset = headers.MetadataStartOffset; return new MetadataReader((byte*)pinned.AddrOfPinnedObject() + headers.MetadataStartOffset, headers.MetadataSize, MetadataReaderOptions.Default, decoder); }
public static string DecodeUtf8(byte *bytes, int byteCount, byte[] prefix, MetadataStringDecoder utf8Decoder) { Debug.Assert(utf8Decoder != null); if (prefix != null) { return(DecodeUtf8Prefixed(bytes, byteCount, prefix, utf8Decoder)); } if (byteCount == 0) { return(string.Empty); } return(utf8Decoder.GetString(bytes, byteCount)); }
private static unsafe MetadataReader TryOpenMetadataFile(string filePath, MetadataStringDecoder stringDecoder, out MemoryMappedViewAccessor mappedViewAccessor) { MemoryMappedFile mappedFile = null; MemoryMappedViewAccessor accessor = null; try { mappedFile = MemoryMappedFile.CreateFromFile(filePath, FileMode.Open, null, 0, MemoryMappedFileAccess.Read); accessor = mappedFile.CreateViewAccessor(0, 0, MemoryMappedFileAccess.Read); var safeBuffer = accessor.SafeMemoryMappedViewHandle; // Check whether this is a real metadata file to avoid thrown and caught exceptions // for non-portable .pdbs if (safeBuffer.Read <byte>(0) != 'B' || // COR20MetadataSignature safeBuffer.Read <byte>(1) != 'S' || safeBuffer.Read <byte>(2) != 'J' || safeBuffer.Read <byte>(3) != 'B') { mappedViewAccessor = null; return(null); } var metadataReader = new MetadataReader((byte *)safeBuffer.DangerousGetHandle(), (int)safeBuffer.ByteLength, MetadataReaderOptions.Default, stringDecoder); // MemoryMappedFile does not need to be kept around. MemoryMappedViewAccessor is enough. mappedViewAccessor = accessor; accessor = null; return(metadataReader); } finally { if (accessor != null) { accessor.Dispose(); } if (mappedFile != null) { mappedFile.Dispose(); } } }
private static unsafe MetadataReader TryOpenMetadataFile(string filePath, MetadataStringDecoder stringDecoder, out MemoryMappedViewAccessor mappedViewAccessor) { FileStream fileStream = null; MemoryMappedFile mappedFile = null; MemoryMappedViewAccessor accessor = null; try { // Create stream because CreateFromFile(string, ...) uses FileShare.None which is too strict fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read, bufferSize: 1); mappedFile = MemoryMappedFile.CreateFromFile( fileStream, null, fileStream.Length, MemoryMappedFileAccess.Read, HandleInheritability.None, true); accessor = mappedFile.CreateViewAccessor(0, 0, MemoryMappedFileAccess.Read); var safeBuffer = accessor.SafeMemoryMappedViewHandle; // Check whether this is a real metadata file to avoid thrown and caught exceptions // for non-portable .pdbs if (safeBuffer.Read <byte>(0) != 'B' || // COR20MetadataSignature safeBuffer.Read <byte>(1) != 'S' || safeBuffer.Read <byte>(2) != 'J' || safeBuffer.Read <byte>(3) != 'B') { mappedViewAccessor = null; return(null); } var metadataReader = new MetadataReader((byte *)safeBuffer.DangerousGetHandle(), (int)safeBuffer.ByteLength, MetadataReaderOptions.Default, stringDecoder); // MemoryMappedFile does not need to be kept around. MemoryMappedViewAccessor is enough. mappedViewAccessor = accessor; accessor = null; return(metadataReader); } finally { accessor?.Dispose(); mappedFile?.Dispose(); fileStream?.Dispose(); } }
internal bool Equals(StringHandle handle, string value, MetadataStringDecoder utf8Decoder) { Debug.Assert(value != null); if (handle.IsVirtual) { // TODO:This can allocate unnecessarily for <WinRT> prefixed handles. return(GetString(handle, utf8Decoder) == value); } if (handle.IsNil) { return(value.Length == 0); } char otherTerminator = handle.StringKind == StringKind.DotTerminated ? '.' : '\0'; return(this.Block.Utf8NullTerminatedEquals(handle.GetHeapOffset(), value, utf8Decoder, otherTerminator)); }
internal bool StartsWith(StringHandle handle, string value, MetadataStringDecoder utf8Decoder, bool ignoreCase) { Debug.Assert(value != null); if (handle.IsVirtual) { // TODO: This can allocate unnecessarily for <WinRT> prefixed handles. return(GetString(handle, utf8Decoder).StartsWith(value, ignoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal)); } if (handle.IsNil) { return(value.Length == 0); } char otherTerminator = handle.StringKind == StringKind.DotTerminated ? '.' : '\0'; return(this.Block.Utf8NullTerminatedStartsWith(handle.GetHeapOffset(), value, utf8Decoder, otherTerminator, ignoreCase)); }
public static PdbSymbolReader TryOpen(string pdbFilename, MetadataStringDecoder stringDecoder, BlobContentId expectedContentId) { MemoryMappedViewAccessor mappedViewAccessor; MetadataReader reader = TryOpenMetadataFile(pdbFilename, stringDecoder, out mappedViewAccessor); if (reader == null) { return(null); } var foundContentId = new BlobContentId(reader.DebugMetadataHeader.Id); if (foundContentId != expectedContentId) { mappedViewAccessor.Dispose(); return(null); } return(new PortablePdbSymbolReader(reader, mappedViewAccessor)); }
// comparison stops at null terminator, terminator parameter, or end-of-block -- whichever comes first. internal bool Utf8NullTerminatedStartsWith(int offset, string text, MetadataStringDecoder utf8Decoder, char terminator, bool ignoreCase) { FastComparisonResult result = Utf8NullTerminatedFastCompare(offset, text, 0, out _, terminator, ignoreCase); switch (result) { case FastComparisonResult.Equal: case FastComparisonResult.BytesStartWithText: return(true); case FastComparisonResult.Unequal: case FastComparisonResult.TextStartsWithBytes: return(false); default: Debug.Assert(result == FastComparisonResult.Inconclusive); string decoded = PeekUtf8NullTerminated(offset, null, utf8Decoder, out _, terminator); return(decoded.StartsWith(text, ignoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal)); } }
// comparison stops at null terminator, terminator parameter, or end-of-block -- whichever comes first. internal bool Utf8NullTerminatedStartsWith(int offset, string text, MetadataStringDecoder utf8Decoder, char terminator = '\0') { FastComparisonResult result = Utf8NullTerminatedFastCompare(offset, text, terminator); switch (result) { case FastComparisonResult.Equal: case FastComparisonResult.IsPrefix: return(true); case FastComparisonResult.Unequal: return(false); default: Debug.Assert(result == FastComparisonResult.Inconclusive); int bytesRead; string decoded = PeekUtf8NullTerminated(offset, null, utf8Decoder, out bytesRead, terminator); return(decoded.StartsWith(text, StringComparison.Ordinal)); } }
internal bool StartsWith(StringHandle handle, string value, MetadataStringDecoder utf8Decoder) { Debug.Assert(value != null); if (handle.IsVirtual) { // TODO:This can allocate unnecessarily for <WinRT> prefixed handles. return(GetString(handle, utf8Decoder).StartsWith(value, StringComparison.Ordinal)); } if (handle.IsNil) { return(value.Length == 0); } // TODO: MetadataStringComparer needs to use the user-supplied encoding. // Need to pass the decoder down and use in Utf8NullTerminatedEquals. char otherTerminator = handle.StringKind == StringKind.DotTerminated ? '.' : '\0'; return(this.Block.Utf8NullTerminatedStartsWith(handle.Index, value, otherTerminator)); }
public unsafe void Utf8NullTerminatedFastCompare() { byte[] heap; MetadataStringDecoder decoder = MetadataStringDecoder.DefaultUTF8; const bool HonorCase = false; const bool IgnoreCase = true; const char terminator_0 = '\0'; const char terminator_F = 'F'; const char terminator_X = 'X'; const char terminator_x = 'x'; fixed(byte *heapPtr = (heap = new byte[] { (byte)'F', 0, (byte)'X', (byte)'Y', /* U+12345 (\ud808\udf45) */ 0xf0, 0x92, 0x8d, 0x85 })) { var block = new MemoryBlock(heapPtr, heap.Length); TestUtf8NullTerminatedFastCompare(block, 0, terminator_0, "F", 0, HonorCase, MemoryBlock.FastComparisonResult.Equal, 1); TestUtf8NullTerminatedFastCompare(block, 0, terminator_0, "f", 0, IgnoreCase, MemoryBlock.FastComparisonResult.Equal, 1); TestUtf8NullTerminatedFastCompare(block, 0, terminator_F, "", 0, IgnoreCase, MemoryBlock.FastComparisonResult.Equal, 0); TestUtf8NullTerminatedFastCompare(block, 0, terminator_F, "*", 1, IgnoreCase, MemoryBlock.FastComparisonResult.Equal, 1); TestUtf8NullTerminatedFastCompare(block, 0, terminator_0, "FF", 0, HonorCase, MemoryBlock.FastComparisonResult.TextStartsWithBytes, 1); TestUtf8NullTerminatedFastCompare(block, 0, terminator_0, "fF", 0, IgnoreCase, MemoryBlock.FastComparisonResult.TextStartsWithBytes, 1); TestUtf8NullTerminatedFastCompare(block, 0, terminator_0, "F\0", 0, HonorCase, MemoryBlock.FastComparisonResult.TextStartsWithBytes, 1); TestUtf8NullTerminatedFastCompare(block, 0, terminator_X, "F\0", 0, HonorCase, MemoryBlock.FastComparisonResult.TextStartsWithBytes, 1); TestUtf8NullTerminatedFastCompare(block, 2, terminator_0, "X", 0, HonorCase, MemoryBlock.FastComparisonResult.BytesStartWithText, 1); TestUtf8NullTerminatedFastCompare(block, 2, terminator_0, "x", 0, IgnoreCase, MemoryBlock.FastComparisonResult.BytesStartWithText, 1); TestUtf8NullTerminatedFastCompare(block, 2, terminator_x, "XY", 0, IgnoreCase, MemoryBlock.FastComparisonResult.BytesStartWithText, 2); TestUtf8NullTerminatedFastCompare(block, 3, terminator_0, "yZ", 0, IgnoreCase, MemoryBlock.FastComparisonResult.Unequal, 1); TestUtf8NullTerminatedFastCompare(block, 4, terminator_0, "a", 0, HonorCase, MemoryBlock.FastComparisonResult.Unequal, 0); TestUtf8NullTerminatedFastCompare(block, 4, terminator_0, "\ud808", 0, HonorCase, MemoryBlock.FastComparisonResult.Inconclusive, 0); TestUtf8NullTerminatedFastCompare(block, 4, terminator_0, "\ud808\udf45", 0, HonorCase, MemoryBlock.FastComparisonResult.Inconclusive, 0); } }
private static string DecodeUtf8Prefixed(byte *bytes, int byteCount, byte[] prefix, MetadataStringDecoder utf8Decoder) { Debug.Assert(utf8Decoder != null); int prefixedByteCount = byteCount + prefix.Length; if (prefixedByteCount == 0) { return(String.Empty); } byte[] buffer = AcquireBuffer(prefixedByteCount); prefix.CopyTo(buffer, 0); Marshal.Copy((IntPtr)bytes, buffer, prefix.Length, byteCount); string result; fixed(byte *prefixedBytes = buffer) { result = utf8Decoder.GetString(prefixedBytes, prefixedByteCount); } ReleaseBuffer(buffer); return(result); }
public unsafe void LightUpTrickFromDifferentAssemblyWorks() { // This is a trick to use our portable light up outside the reader assembly (that // I will use in Roslyn). Check that it works with encoding other than UTF8 and that it // validates arguments like the the real thing. var decoder = new MetadataStringDecoder(Encoding.Unicode); Assert.Throws<ArgumentNullException>(() => decoder.GetString(null, 0)); Assert.Throws<ArgumentOutOfRangeException>(() => decoder.GetString((byte*)1, -1)); byte[] bytes; fixed (byte* ptr = (bytes = Encoding.Unicode.GetBytes("\u00C7a marche tr\u00E8s bien."))) { Assert.Equal("\u00C7a marche tr\u00E8s bien.", decoder.GetString(ptr, bytes.Length)); } }
private static string DecodeUtf8Prefixed(byte *bytes, int byteCount, byte[] prefix, MetadataStringDecoder utf8Decoder) { Debug.Assert(utf8Decoder != null); int prefixedByteCount = byteCount + prefix.Length; if (prefixedByteCount == 0) { return(string.Empty); } byte[] buffer = ArrayPool <byte> .Shared.Rent(prefixedByteCount); prefix.CopyTo(buffer, 0); Marshal.Copy((IntPtr)bytes, buffer, prefix.Length, byteCount); string result; fixed(byte *prefixedBytes = &buffer[0]) { result = utf8Decoder.GetString(prefixedBytes, prefixedByteCount); } ArrayPool <byte> .Shared.Return(buffer); return(result); }
private static unsafe void TestComparison(MemoryBlock block, int offset, string value, string heapSubstr, MetadataStringDecoder decoder, bool ignoreCase) { // equals: bool actualEq = block.Utf8NullTerminatedEquals(offset, value, decoder, terminator: '\0', ignoreCase: ignoreCase); bool expectedEq = string.Equals(heapSubstr, value, ignoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal); Assert.Equal(expectedEq, actualEq); // starts with: bool actualSW = block.Utf8NullTerminatedStartsWith(offset, value, decoder, terminator: '\0', ignoreCase: ignoreCase); bool expectedSW = heapSubstr.StartsWith(value, ignoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal); Assert.Equal(actualSW, expectedSW); }
internal bool StartsWith(StringHandle handle, string value, MetadataStringDecoder utf8Decoder) { Debug.Assert(value != null); if (handle.IsVirtual) { // TODO:This can allocate unnecessarily for <WinRT> prefixed handles. return GetString(handle, utf8Decoder).StartsWith(value, StringComparison.Ordinal); } if (handle.IsNil) { return value.Length == 0; } // TODO: MetadataStringComparer needs to use the user-supplied encoding. // Need to pass the decoder down and use in Utf8NullTerminatedEquals. char otherTerminator = handle.StringKind == StringKind.DotTerminated ? '.' : '\0'; return this.Block.Utf8NullTerminatedStartsWith(handle.Index, value, otherTerminator); }
internal string GetString(StringHandle handle, MetadataStringDecoder utf8Decoder) { return handle.IsVirtual ? GetVirtualHandleString(handle, utf8Decoder) : GetNonVirtualString(handle, utf8Decoder, prefixOpt: null); }
private string GetNonVirtualString(StringHandle handle, MetadataStringDecoder utf8Decoder, byte[] prefixOpt) { Debug.Assert(handle.StringKind != StringKind.Virtual); int bytesRead; char otherTerminator = handle.StringKind == StringKind.DotTerminated ? '.' : '\0'; return Block.PeekUtf8NullTerminated(handle.GetHeapOffset(), prefixOpt, utf8Decoder, out bytesRead, otherTerminator); }
public MetadataStringDecoder GetMetadataStringDecoder() { if (_metadataStringDecoder == null) _metadataStringDecoder = new CachingMetadataStringDecoder(0x10000); // TODO: Tune the size return _metadataStringDecoder; }
internal bool StartsWith(StringHandle handle, string value, MetadataStringDecoder utf8Decoder, bool ignoreCase) { Debug.Assert(value != null); if (handle.IsVirtual) { // TODO: This can allocate unnecessarily for <WinRT> prefixed handles. return GetString(handle, utf8Decoder).StartsWith(value, ignoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal); } if (handle.IsNil) { return value.Length == 0; } char otherTerminator = handle.StringKind == StringKind.DotTerminated ? '.' : '\0'; return this.Block.Utf8NullTerminatedStartsWith(handle.GetHeapOffset(), value, utf8Decoder, otherTerminator, ignoreCase); }
internal bool Equals(StringHandle handle, string value, MetadataStringDecoder utf8Decoder) { Debug.Assert(value != null); if (handle.IsVirtual) { // TODO:This can allocate unnecessarily for <WinRT> prefixed handles. return GetString(handle, utf8Decoder) == value; } if (handle.IsNil) { return value.Length == 0; } char otherTerminator = handle.StringKind == StringKind.DotTerminated ? '.' : '\0'; return this.Block.Utf8NullTerminatedEquals(handle.GetHeapOffset(), value, utf8Decoder, otherTerminator); }
private static unsafe void TestComparison(MemoryBlock block, int offset, string value, string heapSubstr, MetadataStringDecoder decoder, bool ignoreCase) { // equals: bool actualEq = block.Utf8NullTerminatedEquals(offset, value, decoder, terminator: '\0', ignoreCase: ignoreCase); bool expectedEq = string.Equals(heapSubstr, value, ignoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal); Assert.Equal(expectedEq, actualEq); // starts with: bool actualSW = block.Utf8NullTerminatedStartsWith(offset, value, decoder, terminator: '\0', ignoreCase: ignoreCase); bool expectedSW = heapSubstr.StartsWith(value, ignoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal); Assert.Equal(actualSW, expectedSW); }
public void CannotInstantiateReaderWithNonUtf8Decoder() { var decoder = new MetadataStringDecoder(Encoding.ASCII); var exception = Assert.Throws<ArgumentException>(() => GetMetadataReader(Misc.Members, decoder: decoder)); Assert.Equal("utf8Decoder", exception.ParamName); }
internal string GetString(StringHandle handle, MetadataStringDecoder utf8Decoder) { return(handle.IsVirtual ? GetVirtualHandleString(handle, utf8Decoder) : GetNonVirtualString(handle, utf8Decoder, prefixOpt: null)); }
internal static unsafe MetadataReader GetMetadataReader(byte[] peImage, bool isModule = false, MetadataStringDecoder decoder = null) { int _; return GetMetadataReader(peImage, out _, isModule, decoder); }
public unsafe void ReadFromMemoryBlock() { byte[] buffer = new byte[4] { 0, 1, 0, 2 }; fixed(byte *bufferPtr = buffer) { var block = new MemoryBlock(bufferPtr, buffer.Length); Assert.Throws <BadImageFormatException>(() => block.PeekUInt32(Int32.MaxValue)); Assert.Throws <BadImageFormatException>(() => block.PeekUInt32(-1)); Assert.Throws <BadImageFormatException>(() => block.PeekUInt32(Int32.MinValue)); Assert.Throws <BadImageFormatException>(() => block.PeekUInt32(4)); Assert.Throws <BadImageFormatException>(() => block.PeekUInt32(1)); Assert.Equal(0x02000100U, block.PeekUInt32(0)); Assert.Throws <BadImageFormatException>(() => block.PeekUInt16(Int32.MaxValue)); Assert.Throws <BadImageFormatException>(() => block.PeekUInt16(-1)); Assert.Throws <BadImageFormatException>(() => block.PeekUInt16(Int32.MinValue)); Assert.Throws <BadImageFormatException>(() => block.PeekUInt16(4)); Assert.Equal(0x0200, block.PeekUInt16(2)); int bytesRead; MetadataStringDecoder stringDecoder = MetadataStringDecoder.DefaultUTF8; Assert.Throws <BadImageFormatException>(() => block.PeekUtf8NullTerminated(Int32.MaxValue, null, stringDecoder, out bytesRead)); Assert.Throws <BadImageFormatException>(() => block.PeekUtf8NullTerminated(-1, null, stringDecoder, out bytesRead)); Assert.Throws <BadImageFormatException>(() => block.PeekUtf8NullTerminated(Int32.MinValue, null, stringDecoder, out bytesRead)); Assert.Throws <BadImageFormatException>(() => block.PeekUtf8NullTerminated(5, null, stringDecoder, out bytesRead)); Assert.Throws <BadImageFormatException>(() => block.GetMemoryBlockAt(-1, 1)); Assert.Throws <BadImageFormatException>(() => block.GetMemoryBlockAt(1, -1)); Assert.Throws <BadImageFormatException>(() => block.GetMemoryBlockAt(0, -1)); Assert.Throws <BadImageFormatException>(() => block.GetMemoryBlockAt(-1, 0)); Assert.Throws <BadImageFormatException>(() => block.GetMemoryBlockAt(-Int32.MaxValue, Int32.MaxValue)); Assert.Throws <BadImageFormatException>(() => block.GetMemoryBlockAt(Int32.MaxValue, -Int32.MaxValue)); Assert.Throws <BadImageFormatException>(() => block.GetMemoryBlockAt(Int32.MaxValue, Int32.MaxValue)); Assert.Throws <BadImageFormatException>(() => block.GetMemoryBlockAt(block.Length, -1)); Assert.Throws <BadImageFormatException>(() => block.GetMemoryBlockAt(-1, block.Length)); Assert.Equal("\u0001", block.PeekUtf8NullTerminated(1, null, stringDecoder, out bytesRead)); Assert.Equal(bytesRead, 2); Assert.Equal("\u0002", block.PeekUtf8NullTerminated(3, null, stringDecoder, out bytesRead)); Assert.Equal(bytesRead, 1); Assert.Equal("", block.PeekUtf8NullTerminated(4, null, stringDecoder, out bytesRead)); Assert.Equal(bytesRead, 0); byte[] helloPrefix = Encoding.UTF8.GetBytes("Hello"); Assert.Equal("Hello\u0001", block.PeekUtf8NullTerminated(1, helloPrefix, stringDecoder, out bytesRead)); Assert.Equal(bytesRead, 2); Assert.Equal("Hello\u0002", block.PeekUtf8NullTerminated(3, helloPrefix, stringDecoder, out bytesRead)); Assert.Equal(bytesRead, 1); Assert.Equal("Hello", block.PeekUtf8NullTerminated(4, helloPrefix, stringDecoder, out bytesRead)); Assert.Equal(bytesRead, 0); } }
private string GetVirtualHandleString(StringHandle handle, MetadataStringDecoder utf8Decoder) { Debug.Assert(handle.IsVirtual); switch (handle.StringKind) { case StringKind.Virtual: return GetVirtualString(handle.GetVirtualIndex()); case StringKind.WinRTPrefixed: return GetNonVirtualString(handle, utf8Decoder, MetadataReader.WinRTPrefix); } throw ExceptionUtilities.UnexpectedValue(handle.StringKind); }
internal string GetString(StringHandle handle, MetadataStringDecoder utf8Decoder) { int index = handle.Index; byte[] prefix; if (handle.IsVirtual) { switch (handle.StringKind) { case StringKind.Plain: return s_virtualValues[index]; case StringKind.WinRTPrefixed: prefix = MetadataReader.WinRTPrefix; break; default: Debug.Assert(false, "We should not get here"); return null; } } else { prefix = null; } int bytesRead; char otherTerminator = handle.StringKind == StringKind.DotTerminated ? '.' : '\0'; return this.Block.PeekUtf8NullTerminated(index, prefix, utf8Decoder, out bytesRead, otherTerminator); }