| 1 | // Licensed to the .NET Foundation under one or more agreements. |
| 2 | // The .NET Foundation licenses this file to you under the MIT license. |
| 3 | // See the LICENSE file in the project root for more information. |
| 4 | //***************************************************************************** |
| 5 | // LiteWeightStgdb.cpp |
| 6 | // |
| 7 | |
| 8 | // |
| 9 | // This contains definition of class CLiteWeightStgDB. This is light weight |
| 10 | // read-only implementation for accessing compressed meta data format. |
| 11 | // |
| 12 | //***************************************************************************** |
| 13 | #include "stdafx.h" // Precompiled header. |
| 14 | #include "mdfileformat.h" |
| 15 | #include "metamodelro.h" |
| 16 | #include "liteweightstgdb.h" |
| 17 | #include "metadatatracker.h" |
| 18 | |
| 19 | #include "../hotdata/export.h" |
| 20 | |
| 21 | __checkReturn |
| 22 | HRESULT _CallInitOnMemHelper(CLiteWeightStgdb<CMiniMd> *pStgdb, ULONG cbData, LPCVOID pData) |
| 23 | { |
| 24 | return pStgdb->InitOnMem(cbData,pData); |
| 25 | } |
| 26 | |
| 27 | //***************************************************************************** |
| 28 | // Open an in-memory metadata section for read |
| 29 | //***************************************************************************** |
| 30 | template <class MiniMd> |
| 31 | __checkReturn |
| 32 | HRESULT |
| 33 | CLiteWeightStgdb<MiniMd>::InitOnMem( |
| 34 | ULONG cbData, // count of bytes in pData |
| 35 | LPCVOID pData) // points to meta data section in memory |
| 36 | { |
| 37 | STORAGEHEADER sHdr; // Header for the storage. |
| 38 | PSTORAGESTREAM pStream; // Pointer to each stream. |
| 39 | int bFoundMd = false; // true when compressed data found. |
| 40 | int i; // Loop control. |
| 41 | HRESULT hr = S_OK; |
| 42 | ULONG cbStreamBuffer; |
| 43 | |
| 44 | // Don't double open. |
| 45 | _ASSERTE((m_pvMd == NULL) && (m_cbMd == 0)); |
| 46 | |
| 47 | // Validate the signature of the format, or it isn't ours. |
| 48 | IfFailGo(MDFormat::VerifySignature((PSTORAGESIGNATURE)pData, cbData)); |
| 49 | |
| 50 | #ifdef FEATURE_PREJIT |
| 51 | m_MiniMd.m_pHotTablesDirectory = NULL; |
| 52 | #endif //FEATURE_PREJIT |
| 53 | |
| 54 | // Remaining buffer size behind the stream header (pStream). |
| 55 | cbStreamBuffer = cbData; |
| 56 | // Get back the first stream. |
| 57 | pStream = MDFormat::GetFirstStream_Verify(&sHdr, pData, &cbStreamBuffer); |
| 58 | if (pStream == NULL) |
| 59 | { |
| 60 | Debug_ReportError("Invalid MetaData storage signature - cannot get the first stream header." ); |
| 61 | IfFailGo(CLDB_E_FILE_CORRUPT); |
| 62 | } |
| 63 | |
| 64 | // Loop through each stream and pick off the ones we need. |
| 65 | for (i = 0; i < sHdr.GetiStreams(); i++) |
| 66 | { |
| 67 | // Do we have enough buffer to read stream header? |
| 68 | if (cbStreamBuffer < sizeof(*pStream)) |
| 69 | { |
| 70 | Debug_ReportError("Stream header is not within MetaData block." ); |
| 71 | IfFailGo(CLDB_E_FILE_CORRUPT); |
| 72 | } |
| 73 | // Pick off the location and size of the data. |
| 74 | if (pStream->GetOffset() >= cbData) |
| 75 | { // Stream data are not in the buffer. Stream header is corrupted. |
| 76 | Debug_ReportError("Stream data are not within MetaData block." ); |
| 77 | IfFailGo(CLDB_E_FILE_CORRUPT); |
| 78 | } |
| 79 | void *pvCurrentData = (void *)((BYTE *)pData + pStream->GetOffset()); |
| 80 | ULONG cbCurrentData = pStream->GetSize(); |
| 81 | |
| 82 | // Get next stream. |
| 83 | PSTORAGESTREAM pNext = pStream->NextStream_Verify(); |
| 84 | if (pNext == NULL) |
| 85 | { // Stream header is corrupted. |
| 86 | Debug_ReportError("Invalid stream header - cannot get next stream header." ); |
| 87 | IfFailGo(CLDB_E_FILE_CORRUPT); |
| 88 | } |
| 89 | |
| 90 | // Range check |
| 91 | if ((LPBYTE)pNext > ((LPBYTE)pData + cbData)) |
| 92 | { |
| 93 | Debug_ReportError("Stream header is not within MetaData block." ); |
| 94 | IfFailGo(CLDB_E_FILE_CORRUPT); |
| 95 | } |
| 96 | |
| 97 | // Stream end must fit into the buffer and we have to check integer overflow (stream start is already checked) |
| 98 | if ((((LPBYTE)pvCurrentData + cbCurrentData) < (LPBYTE)pvCurrentData) || |
| 99 | (((LPBYTE)pvCurrentData + cbCurrentData) > ((LPBYTE)pData + cbData))) |
| 100 | { |
| 101 | Debug_ReportError("Stream data are not within MetaData block." ); |
| 102 | IfFailGo(CLDB_E_FILE_CORRUPT); |
| 103 | } |
| 104 | |
| 105 | // String pool. |
| 106 | if (strcmp(pStream->GetName(), STRING_POOL_STREAM_A) == 0) |
| 107 | { |
| 108 | METADATATRACKER_ONLY(MetaDataTracker::NoteSection(TBL_COUNT + MDPoolStrings, pvCurrentData, cbCurrentData, 1)); |
| 109 | // String pool has to end with a null-terminator, therefore we don't have to check string pool content on access. |
| 110 | // Shrink size of the pool to the last null-terminator found. |
| 111 | while (cbCurrentData != 0) |
| 112 | { |
| 113 | if (((LPBYTE)pvCurrentData)[cbCurrentData - 1] == 0) |
| 114 | { // We have found last null terminator |
| 115 | break; |
| 116 | } |
| 117 | // Shrink size of the pool |
| 118 | cbCurrentData--; |
| 119 | Debug_ReportError("String heap/pool does not end with null-terminator ... shrinking the heap." ); |
| 120 | } |
| 121 | // Initialize string heap with null-terminated block of data |
| 122 | IfFailGo(m_MiniMd.m_StringHeap.Initialize( |
| 123 | MetaData::DataBlob((BYTE *)pvCurrentData, cbCurrentData), |
| 124 | FALSE)); // fCopyData |
| 125 | } |
| 126 | |
| 127 | // Literal String Blob pool. |
| 128 | else if (strcmp(pStream->GetName(), US_BLOB_POOL_STREAM_A) == 0) |
| 129 | { |
| 130 | METADATATRACKER_ONLY(MetaDataTracker::NoteSection(TBL_COUNT + MDPoolUSBlobs, pvCurrentData, cbCurrentData, 1)); |
| 131 | // Initialize user string heap with block of data |
| 132 | IfFailGo(m_MiniMd.m_UserStringHeap.Initialize( |
| 133 | MetaData::DataBlob((BYTE *)pvCurrentData, cbCurrentData), |
| 134 | FALSE)); // fCopyData |
| 135 | } |
| 136 | |
| 137 | // GUID pool. |
| 138 | else if (strcmp(pStream->GetName(), GUID_POOL_STREAM_A) == 0) |
| 139 | { |
| 140 | METADATATRACKER_ONLY(MetaDataTracker::NoteSection(TBL_COUNT + MDPoolGuids, pvCurrentData, cbCurrentData, 1)); |
| 141 | // Initialize guid heap with block of data |
| 142 | IfFailGo(m_MiniMd.m_GuidHeap.Initialize( |
| 143 | MetaData::DataBlob((BYTE *)pvCurrentData, cbCurrentData), |
| 144 | FALSE)); // fCopyData |
| 145 | } |
| 146 | |
| 147 | // Blob pool. |
| 148 | else if (strcmp(pStream->GetName(), BLOB_POOL_STREAM_A) == 0) |
| 149 | { |
| 150 | METADATATRACKER_ONLY(MetaDataTracker::NoteSection(TBL_COUNT + MDPoolBlobs, pvCurrentData, cbCurrentData, 1)); |
| 151 | // Initialize blob heap with block of data |
| 152 | IfFailGo(m_MiniMd.m_BlobHeap.Initialize( |
| 153 | MetaData::DataBlob((BYTE *)pvCurrentData, cbCurrentData), |
| 154 | FALSE)); // fCopyData |
| 155 | } |
| 156 | |
| 157 | // Found the compressed meta data stream. |
| 158 | else if (strcmp(pStream->GetName(), COMPRESSED_MODEL_STREAM_A) == 0) |
| 159 | { |
| 160 | IfFailGo( m_MiniMd.InitOnMem(pvCurrentData, cbCurrentData) ); |
| 161 | bFoundMd = true; |
| 162 | } |
| 163 | |
| 164 | // Found the hot meta data stream |
| 165 | else if (strcmp(pStream->GetName(), HOT_MODEL_STREAM_A) == 0) |
| 166 | { |
| 167 | #ifdef FEATURE_PREJIT |
| 168 | BYTE * hotStreamEnd = reinterpret_cast< BYTE * >( pvCurrentData ) + cbCurrentData; |
| 169 | ULONG * hotMetadataDir = reinterpret_cast< ULONG * >( hotStreamEnd ) - 2; |
| 170 | ULONG hotPoolsSize = *hotMetadataDir; |
| 171 | |
| 172 | m_MiniMd.m_pHotTablesDirectory = (struct MetaData::HotTablesDirectory *) |
| 173 | (reinterpret_cast<BYTE *>(hotMetadataDir) - hotPoolsSize - sizeof(struct MetaData::HotTablesDirectory)); |
| 174 | MetaData::HotTable::CheckTables(m_MiniMd.m_pHotTablesDirectory); |
| 175 | |
| 176 | DataBuffer hotMetaData( |
| 177 | reinterpret_cast<BYTE *>(pvCurrentData), |
| 178 | cbCurrentData); |
| 179 | IfFailGo(InitHotPools(hotMetaData)); |
| 180 | #else //!FEATURE_PREJIT |
| 181 | Debug_ReportError("MetaData hot stream is peresent, but ngen is not supported." ); |
| 182 | // Ignore the stream |
| 183 | #endif //!FEATURE_PREJIT |
| 184 | } |
| 185 | // Pick off the next stream if there is one. |
| 186 | pStream = pNext; |
| 187 | cbStreamBuffer = (ULONG)((LPBYTE)pData + cbData - (LPBYTE)pNext); |
| 188 | } |
| 189 | |
| 190 | // If the meta data wasn't found, we can't handle this file. |
| 191 | if (!bFoundMd) |
| 192 | { |
| 193 | Debug_ReportError("MetaData compressed model stream #~ not found." ); |
| 194 | IfFailGo(CLDB_E_FILE_CORRUPT); |
| 195 | } |
| 196 | else |
| 197 | { // Validate sensible heaps. |
| 198 | IfFailGo(m_MiniMd.PostInit(0)); |
| 199 | } |
| 200 | |
| 201 | // Save off the location. |
| 202 | m_pvMd = pData; |
| 203 | m_cbMd = cbData; |
| 204 | |
| 205 | ErrExit: |
| 206 | return hr; |
| 207 | } // CLiteWeightStgdb<MiniMd>::InitOnMem |
| 208 | |
| 209 | |
| 210 | template <class MiniMd> |
| 211 | __checkReturn |
| 212 | HRESULT |
| 213 | CLiteWeightStgdb<MiniMd>::InitHotPools( |
| 214 | DataBuffer hotMetaDataBuffer) |
| 215 | { |
| 216 | HRESULT hr; |
| 217 | MetaData::HotMetaData hotMetaData; |
| 218 | MetaData::HotHeapsDirectoryIterator heapsIterator; |
| 219 | |
| 220 | IfFailRet(hotMetaData.Initialize(hotMetaDataBuffer)); |
| 221 | |
| 222 | IfFailRet(hotMetaData.GetHeapsDirectoryIterator(&heapsIterator)); |
| 223 | |
| 224 | for (;;) |
| 225 | { |
| 226 | MetaData::HotHeap hotHeap; |
| 227 | MetaData::HeapIndex hotHeapIndex; |
| 228 | |
| 229 | hr = heapsIterator.GetNext(&hotHeap, &hotHeapIndex); |
| 230 | if (hr == S_FALSE) |
| 231 | { // End of iteration |
| 232 | return S_OK; |
| 233 | } |
| 234 | |
| 235 | switch (hotHeapIndex.Get()) |
| 236 | { |
| 237 | case MetaData::HeapIndex::StringHeapIndex: |
| 238 | { |
| 239 | m_MiniMd.m_StringHeap.InitializeHotData(hotHeap); |
| 240 | break; |
| 241 | } |
| 242 | case MetaData::HeapIndex::GuidHeapIndex: |
| 243 | { |
| 244 | m_MiniMd.m_GuidHeap.InitializeHotData(hotHeap); |
| 245 | break; |
| 246 | } |
| 247 | case MetaData::HeapIndex::UserStringHeapIndex: |
| 248 | { |
| 249 | m_MiniMd.m_UserStringHeap.InitializeHotData(hotHeap); |
| 250 | break; |
| 251 | } |
| 252 | case MetaData::HeapIndex::BlobHeapIndex: |
| 253 | { |
| 254 | m_MiniMd.m_BlobHeap.InitializeHotData(hotHeap); |
| 255 | break; |
| 256 | } |
| 257 | default: |
| 258 | Debug_ReportInternalError("There's a bug in HotHeapsDirectoryIterator - it should verify the heap index." ); |
| 259 | IfFailRet(METADATA_E_INTERNAL_ERROR); |
| 260 | } |
| 261 | } |
| 262 | } // CLiteWeightStgdb<MiniMd>::InitHotPools |
| 263 | |