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 | |