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
22HRESULT _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//*****************************************************************************
30template <class MiniMd>
31__checkReturn
32HRESULT
33CLiteWeightStgdb<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
205ErrExit:
206 return hr;
207} // CLiteWeightStgdb<MiniMd>::InitOnMem
208
209
210template <class MiniMd>
211__checkReturn
212HRESULT
213CLiteWeightStgdb<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