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// ZapMetadata.cpp
6//
7
8//
9// Metadata zapping
10//
11// ======================================================================================
12
13#include "common.h"
14
15#include "zapmetadata.h"
16
17//-----------------------------------------------------------------------------
18//
19// ZapMetaData is the barebone ZapNode to save metadata scope
20//
21
22void ZapMetaData::SetMetaData(IUnknown * pEmit)
23{
24 _ASSERTE(m_pEmit == NULL);
25 _ASSERTE(pEmit != NULL);
26
27 IfFailThrow(pEmit->QueryInterface(IID_IMetaDataEmit, (void **)&m_pEmit));
28}
29
30DWORD ZapMetaData::GetSize()
31{
32 if (m_dwSize == 0)
33 {
34 IfFailThrow(m_pEmit->GetSaveSize(cssAccurate, &m_dwSize));
35 _ASSERTE(m_dwSize != 0);
36 }
37 return m_dwSize;
38}
39
40void ZapMetaData::Save(ZapWriter * pZapWriter)
41{
42 IfFailThrow(m_pEmit->SaveToStream(pZapWriter, 0));
43}
44
45//-----------------------------------------------------------------------------
46//
47// ZapILMetaData copies both the metadata and IL to the NGEN image.
48//
49
50void ZapILMetaData::Save(ZapWriter * pZapWriter)
51{
52 IMDInternalImport * pMDImport = m_pImage->m_pMDImport;
53
54 HENUMInternalHolder hEnum(pMDImport);
55 hEnum.EnumAllInit(mdtMethodDef);
56
57 mdMethodDef md;
58 while (pMDImport->EnumNext(&hEnum, &md))
59 {
60 DWORD flags;
61 ULONG rva;
62 IfFailThrow(pMDImport->GetMethodImplProps(md, &rva, &flags));
63
64 if (!IsMiIL(flags) || (rva == 0))
65 continue;
66
67 // Set the actual RVA of the method
68 const ILMethod * pILMethod = m_ILMethods.LookupPtr(md);
69
70 IfFailThrow(m_pEmit->SetRVA(md, (pILMethod != NULL) ? pILMethod->m_pIL->GetRVA() : 0));
71 }
72
73 if (IsReadyToRunCompilation())
74 {
75 HENUMInternalHolder hEnum(pMDImport);
76 hEnum.EnumAllInit(mdtFieldDef);
77
78 mdFieldDef fd;
79 while (pMDImport->EnumNext(&hEnum, &fd))
80 {
81 DWORD dwRVA = 0;
82 if (pMDImport->GetFieldRVA(fd, &dwRVA) == S_OK)
83 {
84 PVOID pData = NULL;
85 DWORD cbSize = 0;
86 DWORD cbAlignment = 0;
87
88 m_pImage->m_pPreloader->GetRVAFieldData(fd, &pData, &cbSize, &cbAlignment);
89
90 ZapRVADataNode * pRVADataNode = m_rvaData.Lookup(pData);
91 m_pEmit->SetRVA(fd, pRVADataNode->GetRVA());
92 }
93 }
94 }
95 else
96 {
97 ZapImage::GetImage(pZapWriter)->m_pPreloader->SetRVAsForFields(m_pEmit);
98 }
99
100 ZapMetaData::Save(pZapWriter);
101}
102
103ZapRVADataNode * ZapILMetaData::GetRVAField(void * pData)
104{
105 ZapRVADataNode * pRVADataNode = m_rvaData.Lookup(pData);
106
107 if (pRVADataNode == NULL)
108 {
109 pRVADataNode = new (m_pImage->GetHeap()) ZapRVADataNode(pData);
110
111 m_rvaData.Add(pRVADataNode);
112 }
113
114 return pRVADataNode;
115}
116
117struct RVAField
118{
119 PVOID pData;
120 DWORD cbSize;
121 DWORD cbAlignment;
122};
123
124// Used by qsort
125int __cdecl RVAFieldCmp(const void * a_, const void * b_)
126{
127 RVAField * a = (RVAField *)a_;
128 RVAField * b = (RVAField *)b_;
129
130 if (a->pData != b->pData)
131 {
132 return (a->pData > b->pData) ? 1 : -1;
133 }
134
135 return 0;
136}
137
138void ZapILMetaData::CopyRVAFields()
139{
140 IMDInternalImport * pMDImport = m_pImage->m_pMDImport;
141
142 HENUMInternalHolder hEnum(pMDImport);
143 hEnum.EnumAllInit(mdtFieldDef);
144
145 SArray<RVAField> fields;
146
147 mdFieldDef fd;
148 while (pMDImport->EnumNext(&hEnum, &fd))
149 {
150 DWORD dwRVA = 0;
151 if (pMDImport->GetFieldRVA(fd, &dwRVA) == S_OK)
152 {
153 RVAField field;
154 m_pImage->m_pPreloader->GetRVAFieldData(fd, &field.pData, &field.cbSize, &field.cbAlignment);
155 fields.Append(field);
156 }
157 }
158
159 if (fields.GetCount() == 0)
160 return;
161
162 // Managed C++ binaries depend on the order of RVA fields
163 qsort(&fields[0], fields.GetCount(), sizeof(RVAField), RVAFieldCmp);
164
165 for (COUNT_T i = 0; i < fields.GetCount(); i++)
166 {
167 RVAField field = fields[i];
168
169 ZapRVADataNode * pRVADataNode = GetRVAField(field.pData);
170
171 // Handle overlapping fields by reusing blobs based on the address, and just updating size and alignment.
172 pRVADataNode->UpdateSizeAndAlignment(field.cbSize, field.cbAlignment);
173
174 if (!pRVADataNode->IsPlaced())
175 m_pImage->m_pReadOnlyDataSection->Place(pRVADataNode);
176 }
177}
178
179void ZapILMetaData::CopyIL()
180{
181 // The IL is emited into NGen image in the following priority order:
182 // 1. Public inlineable method (may be needed by JIT inliner)
183 // 2. Generic method (may be needed to compile non-NGened instantiations)
184 // 3. Other potentially warm instances (private inlineable methods, methods that failed to NGen)
185 // 4. Everything else (should be touched in rare scenarios like reflection or profiling only)
186
187 SArray<ZapBlob *> priorityLists[CORCOMPILE_ILREGION_COUNT];
188
189 IMDInternalImport * pMDImport = m_pImage->m_pMDImport;
190
191 HENUMInternalHolder hEnum(pMDImport);
192 hEnum.EnumAllInit(mdtMethodDef);
193
194 //
195 // Build the list for each priority in first pass, and then place
196 // the IL blobs in each list. The two passes are needed because of
197 // interning of IL blobs (one IL blob can be on multiple lists).
198 //
199
200 mdMethodDef md;
201 while (pMDImport->EnumNext(&hEnum, &md))
202 {
203 const ILMethod * pILMethod = m_ILMethods.LookupPtr(md);
204
205 if (pILMethod == NULL)
206 continue;
207
208 CorCompileILRegion region = m_pImage->m_pPreloader->GetILRegion(md);
209 _ASSERTE(region < CORCOMPILE_ILREGION_COUNT);
210
211 // Preallocate space to avoid wasting too much time by reallocations
212 if (priorityLists[region].IsEmpty())
213 priorityLists[region].Preallocate(m_ILMethods.GetCount() / 16);
214
215 priorityLists[region].Append(pILMethod->m_pIL);
216 }
217
218 for (int iList = 0; iList < CORCOMPILE_ILREGION_COUNT; iList++)
219 {
220 SArray<ZapBlob *> & priorityList = priorityLists[iList];
221
222 // Use just one section for IL for now. Once the touches of IL for method preparation are fixed change it to:
223 // ZapVirtualSection * pSection = (iList == CORCOMPILE_ILREGION_COLD) ? m_pImage->m_pColdILSection : m_pImage->m_pILSection;
224
225 ZapVirtualSection * pSection = m_pImage->m_pILSection;
226
227 COUNT_T nBlobs = priorityList.GetCount();
228 for (COUNT_T iBlob = 0; iBlob < nBlobs; iBlob++)
229 {
230 ZapBlob * pIL = priorityList[iBlob];
231 if (!pIL->IsPlaced())
232 pSection->Place(pIL);
233 }
234 }
235}
236
237void ZapILMetaData::CopyMetaData()
238{
239 //
240 // Copy metadata from IL image and open it so we can update IL rva's
241 //
242
243 COUNT_T cMeta;
244 const void *pMeta = m_pImage->m_ModuleDecoder.GetMetadata(&cMeta);
245
246 IMetaDataDispenserEx * pMetaDataDispenser = m_pImage->m_zapper->m_pMetaDataDispenser;
247
248 //
249 // Transfer the metadata version string from IL image to native image
250 //
251 LPCSTR pRuntimeVersionString;
252 IfFailThrow(GetImageRuntimeVersionString((PVOID)pMeta, &pRuntimeVersionString));
253
254 SString ssRuntimeVersion;
255 ssRuntimeVersion.SetUTF8(pRuntimeVersionString);
256
257 BSTRHolder strVersion(SysAllocString(ssRuntimeVersion.GetUnicode()));
258
259 VARIANT versionOption;
260 V_VT(&versionOption) = VT_BSTR;
261 V_BSTR(&versionOption) = strVersion;
262 IfFailThrow(pMetaDataDispenser->SetOption(MetaDataRuntimeVersion, &versionOption));
263
264 // Preserve local refs. WinMD adapter depends on them at runtime.
265 VARIANT preserveLocalRefsOption;
266 V_VT(&preserveLocalRefsOption) = VT_UI4;
267 V_UI4(&preserveLocalRefsOption) = MDPreserveLocalTypeRef | MDPreserveLocalMemberRef;
268 IfFailThrow(pMetaDataDispenser->SetOption(MetaDataPreserveLocalRefs, &preserveLocalRefsOption));
269
270 // ofNoTransform - Get the raw metadata for WinRT, not the adapter view
271 HRESULT hr = pMetaDataDispenser->OpenScopeOnMemory(pMeta, cMeta,
272 ofWrite | ofNoTransform,
273 IID_IMetaDataEmit,
274 (IUnknown **) &m_pEmit);
275 if (hr == CLDB_E_BADUPDATEMODE)
276 {
277 // This must be incrementally-updated metadata. It needs to be opened
278 // specially.
279 VARIANT incOption;
280 V_VT(&incOption) = VT_UI4;
281 V_UI4(&incOption) = MDUpdateIncremental;
282 IfFailThrow(pMetaDataDispenser->SetOption(MetaDataSetUpdate, &incOption));
283
284 hr = pMetaDataDispenser->OpenScopeOnMemory(pMeta, cMeta,
285 ofWrite | ofNoTransform,
286 IID_IMetaDataEmit,
287 (IUnknown **) &m_pEmit);
288 }
289
290 // Check the result of OpenScopeOnMemory()
291 IfFailThrow(hr);
292
293 if (!IsReadyToRunCompilation())
294 {
295 // Communicate the profile data to the meta data emitter so it can hot/cold split it
296 NonVMComHolder<IMetaDataCorProfileData> pIMetaDataCorProfileData;
297 IfFailThrow(m_pEmit->QueryInterface(IID_IMetaDataCorProfileData,
298 (void**)&pIMetaDataCorProfileData));
299
300 // unless we're producing an instrumented version - the IBC logging for meta data doesn't
301 // work for the hot/cold split version.
302 if (m_pImage->m_zapper->m_pOpt->m_compilerFlags.IsSet(CORJIT_FLAGS::CORJIT_FLAG_BBINSTR))
303 IfFailThrow(pIMetaDataCorProfileData->SetCorProfileData(NULL));
304 else
305 IfFailThrow(pIMetaDataCorProfileData->SetCorProfileData(m_pImage->GetProfileData()));
306 }
307
308 // If we are ngening with the tuning option, the IBC data that is
309 // generated gets reordered and may be inconsistent with the
310 // metadata in the original IL image. Let's just skip that case.
311 if (!m_pImage->m_zapper->m_pOpt->m_compilerFlags.IsSet(CORJIT_FLAGS::CORJIT_FLAG_BBINSTR))
312 {
313 // Communicate the reordering option for saving
314 NonVMComHolder<IMDInternalMetadataReorderingOptions> pIMDInternalMetadataReorderingOptions;
315 IfFailThrow(m_pEmit->QueryInterface(IID_IMDInternalMetadataReorderingOptions,
316 (void**)&pIMDInternalMetadataReorderingOptions));
317 IfFailThrow(pIMDInternalMetadataReorderingOptions->SetMetaDataReorderingOptions(ReArrangeStringPool));
318 }
319}
320
321// Emit IL for a method def into the ngen image
322void ZapILMetaData::EmitMethodIL(mdMethodDef md)
323{
324 DWORD flags;
325 ULONG rva;
326 IfFailThrow(m_pImage->m_pMDImport->GetMethodImplProps(md, &rva, &flags));
327
328 if (!IsMiIL(flags) || (rva == 0))
329 return;
330
331 if (!m_pImage->m_ModuleDecoder.CheckILMethod(rva))
332 IfFailThrow(COR_E_BADIMAGEFORMAT); // BFA_BAD_IL_RANGE
333
334 PVOID pMethod = (PVOID)m_pImage->m_ModuleDecoder.GetRvaData(rva);
335
336 SIZE_T cMethod = PEDecoder::ComputeILMethodSize((TADDR)pMethod);
337
338 //
339 // Emit copy of IL method in native image.
340 //
341 ZapBlob * pIL = m_blobs.Lookup(ZapBlob::SHashKey(pMethod, cMethod));
342
343 if (pIL == NULL)
344 {
345 pIL = new (m_pImage->GetHeap()) ILBlob(pMethod, cMethod);
346
347 m_blobs.Add(pIL);
348 }
349
350 ILMethod ilMethod;
351 ilMethod.m_md = md;
352 ilMethod.m_pIL = pIL;
353 m_ILMethods.Add(ilMethod);
354}
355