| 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 | |
| 22 | void 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 | |
| 30 | DWORD 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 | |
| 40 | void 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 | |
| 50 | void 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 | |
| 103 | ZapRVADataNode * 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 | |
| 117 | struct RVAField |
| 118 | { |
| 119 | PVOID pData; |
| 120 | DWORD cbSize; |
| 121 | DWORD cbAlignment; |
| 122 | }; |
| 123 | |
| 124 | // Used by qsort |
| 125 | int __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 | |
| 138 | void 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 | |
| 179 | void 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 | |
| 237 | void 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 |
| 322 | void 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 | |