summaryrefslogtreecommitdiff
path: root/src/vm/nativeformatreader.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/vm/nativeformatreader.h')
-rw-r--r--src/vm/nativeformatreader.h526
1 files changed, 526 insertions, 0 deletions
diff --git a/src/vm/nativeformatreader.h b/src/vm/nativeformatreader.h
new file mode 100644
index 0000000000..4182b032c4
--- /dev/null
+++ b/src/vm/nativeformatreader.h
@@ -0,0 +1,526 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+// ---------------------------------------------------------------------------
+// NativeFormatReader
+//
+// Utilities to read native data from images
+// ---------------------------------------------------------------------------
+
+#pragma once
+
+// To reduce differences between C# and C++ versions
+#define byte uint8_t
+#define uint uint32_t
+
+#define UInt16 uint16_t
+#define UInt32 uint32_t
+#define UInt64 uint64_t
+
+namespace NativeFormat
+{
+ class NativeReader;
+ typedef DPTR(NativeReader) PTR_NativeReader;
+
+ class NativeReader
+ {
+ PTR_BYTE _base;
+ uint _size;
+
+ public:
+ NativeReader()
+ {
+ _base = NULL;
+ _size = 0;
+ }
+
+ NativeReader(PTR_BYTE base_, uint size)
+ {
+ _base = base_;
+ _size = size;
+ }
+
+ void ThrowBadImageFormatException()
+ {
+ _ASSERTE(false);
+
+#ifndef DACCESS_COMPILE
+ // Failfast instead of throwing, to avoid violating NOTHROW contracts of callers
+ EEPOLICY_HANDLE_FATAL_ERROR(COR_E_BADIMAGEFORMAT);
+#endif
+ }
+
+ uint EnsureOffsetInRange(uint offset, uint lookAhead)
+ {
+ if ((int)offset < 0 || offset + lookAhead >= _size)
+ ThrowBadImageFormatException();
+ return offset;
+ }
+
+ byte ReadUInt8(uint offset)
+ {
+ if (offset >= _size)
+ ThrowBadImageFormatException();
+ return *(_base + offset); // Assumes little endian and unaligned access
+ }
+
+ UInt16 ReadUInt16(uint offset)
+ {
+ if ((int)offset < 0 || offset + 1 >= _size)
+ ThrowBadImageFormatException();
+ return *dac_cast<PTR_USHORT>(_base + offset); // Assumes little endian and unaligned access
+ }
+
+ UInt32 ReadUInt32(uint offset)
+ {
+ if ((int)offset < 0 || offset + 3 >= _size)
+ ThrowBadImageFormatException();
+ return *dac_cast<PTR_UINT32>(_base + offset); // Assumes little endian and unaligned access
+ }
+
+ uint DecodeUnsigned(uint offset, uint * pValue)
+ {
+ if (offset >= _size)
+ ThrowBadImageFormatException();
+
+ uint val = *(_base + offset);
+ if ((val & 1) == 0)
+ {
+ *pValue = (val >> 1);
+ offset += 1;
+ }
+ else
+ if ((val & 2) == 0)
+ {
+ if (offset + 1 >= _size)
+ ThrowBadImageFormatException();
+ *pValue = (val >> 2) |
+ (((uint)*(_base + offset + 1)) << 6);
+ offset += 2;
+ }
+ else
+ if ((val & 4) == 0)
+ {
+ if (offset + 2 >= _size)
+ ThrowBadImageFormatException();
+ *pValue = (val >> 3) |
+ (((uint)*(_base + offset + 1)) << 5) |
+ (((uint)*(_base + offset + 2)) << 13);
+ offset += 3;
+ }
+ else
+ if ((val & 8) == 0)
+ {
+ if (offset + 3 >= _size)
+ ThrowBadImageFormatException();
+ *pValue = (val >> 4) |
+ (((uint)*(_base + offset + 1)) << 4) |
+ (((uint)*(_base + offset + 2)) << 12) |
+ (((uint)*(_base + offset + 3)) << 20);
+ offset += 4;
+ }
+ else
+ if ((val & 16) == 0)
+ {
+ *pValue = ReadUInt32(offset + 1);
+ offset += 5;
+ }
+ else
+ {
+ ThrowBadImageFormatException();
+ }
+
+ return offset;
+ }
+
+ int DecodeSigned(uint offset, int * pValue)
+ {
+ if (offset >= _size)
+ ThrowBadImageFormatException();
+
+ int val = *(_base + offset);
+ if ((val & 1) == 0)
+ {
+ *pValue = val >> 1;
+ offset += 1;
+ }
+ else if ((val & 2) == 0)
+ {
+ if (offset + 1 >= _size)
+ ThrowBadImageFormatException();
+ *pValue = (val >> 2) |
+ (((int)*(_base + offset + 1)) << 6);
+ offset += 2;
+ }
+ else if ((val & 4) == 0)
+ {
+ if (offset + 2 >= _size)
+ ThrowBadImageFormatException();
+ *pValue = (val >> 3) |
+ (((int)*(_base + offset + 1)) << 5) |
+ (((int)*(_base + offset + 2)) << 13);
+ offset += 3;
+ }
+ else if ((val & 8) == 0)
+ {
+ if (offset + 3 >= _size)
+ ThrowBadImageFormatException();
+ *pValue = (val >> 4) |
+ (((int)*(_base + offset + 1)) << 4) |
+ (((int)*(_base + offset + 2)) << 12) |
+ (((int)*(_base + offset + 3)) << 20);
+ offset += 4;
+ }
+ else if ((val & 16) == 0)
+ {
+ *pValue = (int)ReadUInt32(offset + 1);
+ offset += 5;
+ }
+ else
+ {
+ ThrowBadImageFormatException();
+ }
+
+ return offset;
+ }
+
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable : 4702) // Disable unreachable code warning
+#endif
+ uint SkipInteger(uint offset)
+ {
+ EnsureOffsetInRange(offset, 0);
+
+ PTR_BYTE data = (_base + offset);
+ if ((*data & 1) == 0)
+ {
+ return offset + 1;
+ }
+ else if ((*data & 2) == 0)
+ {
+ return offset + 2;
+ }
+ else if ((*data & 4) == 0)
+ {
+ return offset + 3;
+ }
+ else if ((*data & 8) == 0)
+ {
+ return offset + 4;
+ }
+ else if ((*data & 16) == 0)
+ {
+ return offset + 5;
+ }
+ else if ((*data & 32) == 0)
+ {
+ return offset + 9;
+ }
+ else
+ {
+ ThrowBadImageFormatException();
+ return offset;
+ }
+ }
+
+#ifndef DACCESS_COMPILE
+ const BYTE* GetBlob(uint offset)
+ {
+ EnsureOffsetInRange(offset, 0);
+ return _base + offset;
+ }
+#endif
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+ };
+
+ class NativeParser
+ {
+ PTR_NativeReader _pReader;
+ uint _offset;
+
+ public:
+ NativeParser()
+ : _pReader(PTR_NULL), _offset(0)
+ {
+ }
+
+ NativeParser(PTR_NativeReader pReader, uint offset)
+ {
+ _pReader = pReader;
+ _offset = offset;
+ }
+
+ NativeReader * GetNativeReader() { return _pReader; }
+
+ uint GetOffset() { return _offset; }
+ void SetOffset(uint value) { _offset = value; }
+
+ void ThrowBadImageFormatException()
+ {
+ _pReader->ThrowBadImageFormatException();
+ }
+
+ byte GetUInt8()
+ {
+ byte val = _pReader->ReadUInt8(_offset);
+ _offset += 1;
+ return val;
+ }
+
+ uint GetUnsigned()
+ {
+ uint value;
+ _offset = _pReader->DecodeUnsigned(_offset, &value);
+ return value;
+ }
+
+ int GetSigned()
+ {
+ int value;
+ _offset = _pReader->DecodeSigned(_offset, &value);
+ return value;
+ }
+
+ uint GetRelativeOffset()
+ {
+ uint pos = _offset;
+
+ int delta;
+ _offset = _pReader->DecodeSigned(_offset, &delta);
+
+ return pos + (uint)delta;
+ }
+
+#ifndef DACCESS_COMPILE
+ const BYTE * GetBlob()
+ {
+ return _pReader->GetBlob(_offset);
+ }
+#endif
+
+ void SkipInteger()
+ {
+ _offset = _pReader->SkipInteger(_offset);
+ }
+
+ NativeParser GetParserFromRelativeOffset()
+ {
+ return NativeParser(_pReader, GetRelativeOffset());
+ }
+ };
+
+ class NativeArray
+ {
+ PTR_NativeReader _pReader;
+ uint _baseOffset;
+ uint _nElements;
+ byte _entryIndexSize;
+
+ static const uint _blockSize = 16;
+
+ public:
+ NativeArray()
+ : _pReader(PTR_NULL), _nElements(0)
+ {
+ }
+
+ NativeArray(PTR_NativeReader pReader, uint offset)
+ : _pReader(pReader)
+ {
+ uint val;
+ _baseOffset = pReader->DecodeUnsigned(offset, &val);
+
+ _nElements = (val >> 2);
+ _entryIndexSize = (val & 3);
+ }
+
+ uint GetCount()
+ {
+ return _nElements;
+ }
+
+ bool TryGetAt(uint index, uint * pOffset)
+ {
+ if (index >= _nElements)
+ return false;
+
+ uint offset;
+ if (_entryIndexSize == 0)
+ {
+ offset = _pReader->ReadUInt8(_baseOffset + (index / _blockSize));
+ }
+ else if (_entryIndexSize == 1)
+ {
+ offset = _pReader->ReadUInt16(_baseOffset + 2 * (index / _blockSize));
+ }
+ else
+ {
+ offset = _pReader->ReadUInt32(_baseOffset + 4 * (index / _blockSize));
+ }
+ offset += _baseOffset;
+
+ for (uint bit = _blockSize >> 1; bit > 0; bit >>= 1)
+ {
+ uint val;
+ uint offset2 = _pReader->DecodeUnsigned(offset, &val);
+ if (index & bit)
+ {
+ if ((val & 2) != 0)
+ {
+ offset = offset + (val >> 2);
+ continue;
+ }
+ }
+ else
+ {
+ if ((val & 1) != 0)
+ {
+ offset = offset2;
+ continue;
+ }
+ }
+
+ // Not found
+ if ((val & 3) == 0)
+ {
+ // Matching special leaf node?
+ if ((val >> 2) == (index & (_blockSize - 1)))
+ {
+ offset = offset2;
+ break;
+ }
+ }
+ return false;
+ }
+
+ *pOffset = offset;
+ return true;
+ }
+ };
+
+ class NativeHashtable
+ {
+ PTR_NativeReader _pReader;
+ uint _baseOffset;
+ uint _bucketMask;
+ byte _entryIndexSize;
+
+ NativeParser GetParserForBucket(uint bucket, uint * pEndOffset)
+ {
+ uint start, end;
+
+ if (_entryIndexSize == 0)
+ {
+ uint bucketOffset = _baseOffset + bucket;
+ start = _pReader->ReadUInt8(bucketOffset);
+ end = _pReader->ReadUInt8(bucketOffset + 1);
+ }
+ else if (_entryIndexSize == 1)
+ {
+ uint bucketOffset = _baseOffset + 2 * bucket;
+ start = _pReader->ReadUInt16(bucketOffset);
+ end = _pReader->ReadUInt16(bucketOffset + 2);
+ }
+ else
+ {
+ uint bucketOffset = _baseOffset + 4 * bucket;
+ start = _pReader->ReadUInt32(bucketOffset);
+ end = _pReader->ReadUInt32(bucketOffset + 4);
+ }
+
+ *pEndOffset = end + _baseOffset;
+ return NativeParser(_pReader, _baseOffset + start);
+ }
+
+ public:
+ NativeHashtable() : _pReader(PTR_NULL), _baseOffset(0), _bucketMask(0), _entryIndexSize(0)
+ {
+ }
+
+ NativeHashtable(NativeParser& parser)
+ {
+ uint header = parser.GetUInt8();
+
+ _pReader = dac_cast<PTR_NativeReader>(parser.GetNativeReader());
+ _baseOffset = parser.GetOffset();
+
+ int numberOfBucketsShift = (int)(header >> 2);
+ if (numberOfBucketsShift > 31)
+ _pReader->ThrowBadImageFormatException();
+ _bucketMask = (uint)((1 << numberOfBucketsShift) - 1);
+
+ byte entryIndexSize = (byte)(header & 3);
+ if (entryIndexSize > 2)
+ _pReader->ThrowBadImageFormatException();
+ _entryIndexSize = entryIndexSize;
+ }
+
+ bool IsNull() { return _pReader == NULL; }
+
+ //
+ // The enumerator does not conform to the regular C# enumerator pattern to avoid paying
+ // its performance penalty (allocation, multiple calls per iteration)
+ //
+ class Enumerator
+ {
+ NativeParser _parser;
+ uint _endOffset;
+ byte _lowHashcode;
+
+ public:
+ Enumerator(NativeParser parser, uint endOffset, byte lowHashcode)
+ {
+ _parser = parser;
+ _endOffset = endOffset;
+ _lowHashcode = lowHashcode;
+ }
+
+ bool GetNext(NativeParser& entryParser)
+ {
+ while (_parser.GetOffset() < _endOffset)
+ {
+ byte lowHashcode = _parser.GetUInt8();
+
+ if (lowHashcode == _lowHashcode)
+ {
+ entryParser = _parser.GetParserFromRelativeOffset();
+ return true;
+ }
+
+ // The entries are sorted by hashcode within the bucket. It allows us to terminate the lookup prematurely.
+ if (lowHashcode > _lowHashcode)
+ {
+ _endOffset = _parser.GetOffset(); // Ensure that extra call to GetNext returns null parser again
+ break;
+ }
+
+ _parser.SkipInteger();
+ }
+
+ return false;
+ }
+ };
+
+ // The recommended code pattern to perform lookup is:
+ //
+ // NativeHashtable::Enumerator lookup = hashtable.Lookup(dwHashCode);
+ // NativeParser entryParser;
+ // while (lookup.GetNext(entryParser))
+ // {
+ // ... read entry using entryParser ...
+ // }
+ //
+ Enumerator Lookup(int hashcode)
+ {
+ uint endOffset;
+ uint bucket = ((uint)hashcode >> 8) & _bucketMask;
+ NativeParser parser = GetParserForBucket(bucket, &endOffset);
+
+ return Enumerator(parser, endOffset, (byte)hashcode);
+ }
+ };
+}