// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. // // ZapCode.cpp // // // Everything directly related to zapping of native code // - The code itself // - Code headers // - All XXX infos: GC Info, EH Info, Unwind Info, ... // // ====================================================================================== #include "common.h" #include "zapcode.h" #include "zapimport.h" #include "zapinnerptr.h" #ifdef FEATURE_READYTORUN_COMPILER #include "zapreadytorun.h" #endif #ifdef REDHAWK #include "rhcodeinfo.h" #include "rhbinder.h" #include "modulegcinfoencoder.h" #endif // REDHAWK // // The image layout algorithm // ZapVirtualSection * ZapImage::GetCodeSection(CodeType codeType) { switch (codeType) { case ProfiledHot: return m_pHotCodeSection; case ProfiledCold: return m_pColdCodeSection; case Unprofiled: return m_pCodeSection; } UNREACHABLE(); } #if defined(WIN64EXCEPTIONS) ZapVirtualSection * ZapImage::GetUnwindDataSection(CodeType codeType) { #ifdef REDHAWK return m_pUnwindDataSection; #else switch (codeType) { case ProfiledHot: return m_pHotUnwindDataSection; case ProfiledCold: return m_pColdUnwindDataSection; case Unprofiled: return m_pUnwindDataSection; } #endif // REDHAWK UNREACHABLE(); } #endif // defined(WIN64EXCEPTIONS) ZapVirtualSection * ZapImage::GetRuntimeFunctionSection(CodeType codeType) { switch (codeType) { case ProfiledHot: return m_pHotRuntimeFunctionSection; case ProfiledCold: return m_pColdRuntimeFunctionSection; case Unprofiled: return m_pRuntimeFunctionSection; } UNREACHABLE(); } ZapVirtualSection * ZapImage::GetCodeMethodDescSection(CodeType codeType) { switch (codeType) { case ProfiledHot: return m_pHotCodeMethodDescsSection; case Unprofiled: return m_pCodeMethodDescsSection; default: UNREACHABLE(); } } ZapVirtualSection* ZapImage::GetUnwindInfoLookupSection(CodeType codeType) { switch(codeType) { case ProfiledHot: return m_pHotRuntimeFunctionLookupSection; case Unprofiled: return m_pRuntimeFunctionLookupSection; default: UNREACHABLE(); } } void ZapImage::GetCodeCompilationRange(CodeType codeType, COUNT_T * start, COUNT_T * end) { _ASSERTE(start && end); #ifdef REDHAWK *start = 0; *end = m_MethodCompilationOrder.GetCount(); #else switch (codeType) { case ProfiledHot: *start = 0; *end = m_iUntrainedMethod; break; case ProfiledCold: *start = 0; *end = m_MethodCompilationOrder.GetCount(); break; case Unprofiled: *start = m_iUntrainedMethod; *end = m_MethodCompilationOrder.GetCount(); break; } #endif // REDHAWK } void ZapImage::OutputCode(CodeType codeType) { // Note there are three codeTypes: ProfiledHot, Unprofiled and ProfiledCold #if defined(REDHAWK) SectionMethodListGenerator map; #endif bool fCold = (codeType == ProfiledCold); CorInfoRegionKind regionKind = (codeType == ProfiledHot) ? CORINFO_REGION_HOT : CORINFO_REGION_COLD; BeginRegion(regionKind); ZapVirtualSection * pCodeSection = GetCodeSection(codeType); ZapVirtualSection * pRuntimeFunctionSection = GetRuntimeFunctionSection(codeType); #if defined (WIN64EXCEPTIONS) ZapVirtualSection * pUnwindDataSection = GetUnwindDataSection(codeType); #endif // defined (WIN64EXCEPTIONS) DWORD codeSize = 0; // We should start with empty code section _ASSERTE(pRuntimeFunctionSection->GetNodeCount() == 0); _ASSERTE(pCodeSection->GetNodeCount() == 0); COUNT_T startMethod, endMethod; #ifdef REDHAWK // TritonTBD DWORD currentOffset = 0; #endif // REDHAWK GetCodeCompilationRange(codeType, &startMethod, &endMethod); DWORD dwStartMethodIndex = (codeType == Unprofiled) ? m_pHotRuntimeFunctionSection->GetNodeCount() : 0; for (COUNT_T curMethod = startMethod; curMethod < endMethod; curMethod++) { ZapMethodHeader * pMethod = m_MethodCompilationOrder[curMethod]; ZapBlobWithRelocs * pCode = fCold ? pMethod->m_pColdCode : pMethod->m_pCode; if (pCode == NULL) { continue; } if (!fCold) { pMethod->m_methodIndex = dwStartMethodIndex + pRuntimeFunctionSection->GetNodeCount(); } else { pMethod->m_methodIndex = (DWORD)-1; } //Count the method size for use by ZapUnwindInfoLookupTable codeSize = AlignUp(codeSize, pCode->GetAlignment()); codeSize += pCode->GetSize(); pCodeSection->Place(pCode); #ifdef REDHAWK DWORD codeOffset = AlignUp(currentOffset, pCode->GetAlignment()); codeOffset = map.AlignToMethodStartGranularity(codeOffset); map.NoticeMethod(codeOffset, pCode->GetSize()); currentOffset = codeOffset + pCode->GetSize(); #endif ZapReloc * pRelocs = pCode->GetRelocs(); if (pRelocs != NULL) { for (ZapReloc * pReloc = pRelocs; pReloc->m_type != IMAGE_REL_INVALID; pReloc++) { ZapNode * pTarget = pReloc->m_pTargetNode; ZapNodeType type = pTarget->GetType(); if (type == ZapNodeType_InnerPtr) { pTarget = ((ZapInnerPtr *)pTarget)->GetBase(); type = pTarget->GetType(); } switch (type) { case ZapNodeType_StubDispatchCell: // Optimizations may create redundant references to the StubDispatchCell if (!pTarget->IsPlaced()) { m_pStubDispatchDataTable->PlaceStubDispatchCell((ZapImport *)pTarget); } break; case ZapNodeType_MethodEntryPoint: pTarget = m_pMethodEntryPoints->CanDirectCall((ZapMethodEntryPoint *)pTarget, pMethod); if (pTarget != NULL) { pReloc->m_pTargetNode = pTarget; } break; case ZapNodeType_Stub: if (!pTarget->IsPlaced()) { m_pStubsSection->Place(pTarget); } break; case ZapNodeType_HelperThunk: if (!pTarget->IsPlaced()) { // This should place the most frequently used JIT helpers first and together m_pHelperTableSection->Place(pTarget); } break; case ZapNodeType_LazyHelperThunk: if (!pTarget->IsPlaced()) { ((ZapLazyHelperThunk *)pTarget)->Place(this); } break; case ZapNodeType_Import_ModuleHandle: case ZapNodeType_Import_ClassHandle: case ZapNodeType_Import_StringHandle: case ZapNodeType_Import_Helper: // Place all potentially eager imports if (!pTarget->IsPlaced()) m_pImportTable->PlaceImport((ZapImport *)pTarget); break; case ZapNodeType_ExternalMethodThunk: if (!pTarget->IsPlaced()) m_pExternalMethodDataTable->PlaceExternalMethodThunk((ZapImport *)pTarget); break; case ZapNodeType_ExternalMethodCell: if (!pTarget->IsPlaced()) m_pExternalMethodDataTable->PlaceExternalMethodCell((ZapImport *)pTarget); break; #ifdef FEATURE_READYTORUN_COMPILER case ZapNodeType_DynamicHelperCell: if (!pTarget->IsPlaced()) m_pDynamicHelperDataTable->PlaceDynamicHelperCell((ZapImport *)pTarget); break; case ZapNodeType_IndirectHelperThunk: if (!pTarget->IsPlaced()) m_pImportTable->PlaceIndirectHelperThunk(pTarget); break; case ZapNodeType_RVAFieldData: if (!pTarget->IsPlaced()) m_pReadOnlyDataSection->Place(pTarget); break; #endif case ZapNodeType_GenericSignature: if (!pTarget->IsPlaced()) m_pImportTable->PlaceBlob((ZapBlob *)pTarget); break; default: break; } } } #if defined (WIN64EXCEPTIONS) // // Place unwind data // InlineSArray unwindInfos; ZapUnwindInfo * pFragment; // Go over all fragments and append their unwind infos in this section for (pFragment = pMethod->m_pUnwindInfoFragments; pFragment != NULL; pFragment = pFragment->GetNextFragment()) { ZapNode * pFragmentCode = pFragment->GetCode(); _ASSERTE(pFragmentCode == pMethod->m_pCode || pFragmentCode == pMethod->m_pColdCode); if (pFragmentCode == pCode) { unwindInfos.Append(pFragment); } } // The runtime function section must be ordered correctly relative to code layout // in the image. Sort the unwind infos by their offset _ASSERTE(unwindInfos.GetCount() > 0); qsort(&unwindInfos[0], unwindInfos.GetCount(), sizeof(ZapUnwindInfo *), ZapUnwindInfo::CompareUnwindInfo); // Set the initial unwind info for the hot and cold sections if (fCold) { _ASSERTE(pMethod->m_pColdUnwindInfo == NULL); pMethod->m_pColdUnwindInfo = unwindInfos[0]; } else { _ASSERTE(pMethod->m_pUnwindInfo == NULL); pMethod->m_pUnwindInfo = unwindInfos[0]; } for (COUNT_T iUnwindInfo = 0; iUnwindInfo < unwindInfos.GetCount(); iUnwindInfo++) { ZapUnwindInfo * pUnwindInfo = unwindInfos[iUnwindInfo]; pRuntimeFunctionSection->Place(pUnwindInfo); ZapNode * pUnwindData = pUnwindInfo->GetUnwindData(); if (!pUnwindData->IsPlaced()) { pUnwindDataSection->Place(pUnwindData); } } #else // defined (WIN64EXCEPTIONS) ZapUnwindInfo * pUnwindInfo; if (fCold) { // Chained unwind info pUnwindInfo = new (GetHeap()) ZapUnwindInfo(pCode, 0, 0, pMethod->m_pUnwindInfo); pMethod->m_pColdUnwindInfo = pUnwindInfo; } else { pUnwindInfo = new (GetHeap()) ZapUnwindInfo(pCode, 0, 0, pMethod->m_pGCInfo); pMethod->m_pUnwindInfo = pUnwindInfo; } pRuntimeFunctionSection->Place(pUnwindInfo); #endif // defined (WIN64EXCEPTIONS) if (m_stats != NULL) { CorInfoIndirectCallReason reason; BOOL direct = m_pPreloader->CanSkipMethodPreparation(NULL, pMethod->GetHandle(), &reason); if (direct && pMethod->m_pFixupList != NULL) { reason = CORINFO_INDIRECT_CALL_FIXUPS; direct = FALSE; } if (direct) { m_stats->m_directMethods++; } else { m_stats->m_prestubMethods++; m_stats->m_indirectMethodReasons[reason]++; } } } #ifdef REDHAWK // Redhawk needs any trailing padding to be 0xcc DWORD cbPad = AlignUp(currentOffset, sizeof(DWORD)) - currentOffset; if (cbPad != 0) { ZapBlob * pBlob = ZapBlob::NewBlob(this, NULL, cbPad); memset(pBlob->GetData(), DEFAULT_CODE_BUFFER_INIT, cbPad); pCodeSection->Place(pBlob); currentOffset += cbPad; } map.Output(this, m_pCodeMgrSection, numMethods); #else COUNT_T nUnwindInfos = pRuntimeFunctionSection->GetNodeCount(); if (nUnwindInfos != 0) { if (IsReadyToRunCompilation()) { // TODO: Implement } else if (!fCold) { ZapVirtualSection * pCodeMethodDescSection = GetCodeMethodDescSection(codeType); pCodeMethodDescSection->Place(new (GetHeap()) ZapCodeMethodDescs(startMethod, endMethod, nUnwindInfos)); ZapVirtualSection* pUnwindInfoLookupSection = GetUnwindInfoLookupSection(codeType); pUnwindInfoLookupSection->Place(new (GetHeap()) ZapUnwindInfoLookupTable(pRuntimeFunctionSection, pCodeSection, codeSize)); } else { m_pColdCodeMapSection->Place(new (GetHeap()) ZapColdCodeMap(pRuntimeFunctionSection)); } } #endif EndRegion(regionKind); } void ZapImage::OutputCodeInfo(CodeType codeType) { CorInfoRegionKind regionKind = (codeType == ProfiledHot) ? CORINFO_REGION_HOT : CORINFO_REGION_COLD; BeginRegion(regionKind); for (COUNT_T i = 0; i < m_MethodCompilationOrder.GetCount(); i++) { ZapMethodHeader * pMethod = m_MethodCompilationOrder[i]; // // We are either outputing the ProfiledHot methods // or the unprofiled and cold methods // if ((pMethod->m_ProfilingDataFlags & (1 << ReadMethodCode)) != (codeType == ProfiledHot)) { // Wrong kind so skip continue; } if (pMethod->m_pROData != NULL) m_pReadOnlyDataSection->Place(pMethod->m_pROData); #ifndef REDHAWK // Note: for Redhawk we place EH info via OutputEHInfo(). if (pMethod->m_pExceptionInfo != NULL) { ZapNode* pCode = pMethod->m_pCode; m_pExceptionInfoLookupTable->PlaceExceptionInfoEntry(pCode, pMethod->m_pExceptionInfo); } #endif // REDHAWK if (pMethod->m_pFixupList != NULL && !IsReadyToRunCompilation()) pMethod->m_pFixupInfo = m_pImportTable->PlaceFixups(pMethod->m_pFixupList); } EndRegion(regionKind); } void ZapImage::OutputProfileData() { if (m_pInstrumentSection == NULL) { return; } ZapProfileData * pPrevious = NULL; for (COUNT_T i = 0; i < m_MethodCompilationOrder.GetCount(); i++) { ZapMethodHeader * pMethod = m_MethodCompilationOrder[i]; if (pMethod->m_pProfileData == NULL) { continue; } ZapProfileData * pHeader = new (GetHeap()) ZapProfileData(pMethod); m_pInstrumentSection->Place(pHeader); m_pInstrumentSection->Place(pMethod->m_pProfileData); if (pPrevious != NULL) { pPrevious->SetNext(pHeader); } pPrevious = pHeader; } } void ZapImage::OutputDebugInfo() { m_pDebugInfoTable->PrepareLayout(); for (COUNT_T i = 0; i < m_MethodCompilationOrder.GetCount(); i++) { m_pDebugInfoTable->PlaceDebugInfo(m_MethodCompilationOrder[i]); } m_pDebugInfoTable->FinishLayout(); } void ZapImage::OutputGCInfo() { #ifndef REDHAWK struct MaskValue { DWORD mask; DWORD value; }; static const MaskValue gcInfoSequence[] = { { (1 << CommonReadGCInfo) , (1 << CommonReadGCInfo) }, // c flag on, r flag don't care { (1 << CommonReadGCInfo)|(1 << ReadGCInfo), (1 << ReadGCInfo) }, // r flag on, c flag off { (1 << CommonReadGCInfo)|(1 << ReadGCInfo), 0 }, // both flags off { 0, 0 } }; // Make three passes over the gc infos, emitting them in order of decreasing hotness, // and for stuff that wasn't touched by anyone we put it in the cold section for (const MaskValue *pMaskValue = gcInfoSequence; pMaskValue->mask; pMaskValue++) { for (COUNT_T i = 0; i < m_MethodCompilationOrder.GetCount(); i++) { ZapMethodHeader * pMethod = m_MethodCompilationOrder[i]; if ((pMethod->m_ProfilingDataFlags & pMaskValue->mask) != pMaskValue->value) { continue; } ZapGCInfo * pGCInfo = pMethod->m_pGCInfo; // Given that GC Info can be interned it may have been placed already on // this or a previous pass through the compiled methods. If it hasn't already // been placed then we place it in the appropriate section. if (!pGCInfo->IsPlaced()) { // A) it was touched, and here they are placed in order of flags above if (pMaskValue->value) { m_pHotTouchedGCSection->Place(pGCInfo); } // B) the method that it is attached to is in the trained section else if (iPlace(pGCInfo); } // C) it wasn't touched _and_ it is related to untrained code else { m_pGCSection->Place(pGCInfo); } } } // Just after placing those touched in an IBC scenario, place those that // should be prioritized regardless of the corresponding method's IBC information. // (Currently, this is used to pack the gc info of IL stubs that cannot be directly tracked by IBC.) if (pMaskValue->value == (1 << ReadGCInfo)) { for (COUNT_T i = 0; i < m_PrioritizedGCInfo.GetCount(); i++) { ZapGCInfo * pGCInfo = m_PrioritizedGCInfo[i]; if (!pGCInfo->IsPlaced()) { m_pHotGCSection->Place(pGCInfo); } } } } #else // REDHAWK // ModuleGcInfoEncoder * pEncoder = GetGcInfoEncoder(); m_pUnwindInfoBlob = pEncoder->ConstructUnwindInfoBlob(this); m_pCallsiteInfoBlob = pEncoder->ConstructCallsiteInfoBlob(this); ZapBlob * pShortcutMap = pEncoder->ConstructDeltaShortcutMap(this); // @TODO: we could fold this loop into ConstructMethodInfoBlob, but then we'd have to keep a separate // list of method infos inside the ModuleGcInfoEncoder.. for (COUNT_T i = 0; i < m_MethodCompilationOrder.GetCount(); i++) { ZapMethodHeader * pMethod = m_MethodCompilationOrder[i]; pEncoder->EncodeMethodInfo(pMethod->m_pGCInfo); } ZapBlob * pMethodInfos = pEncoder->ConstructMethodInfoBlob(this); for (COUNT_T i = 0; i < m_MethodCompilationOrder.GetCount(); i++) { ZapMethodHeader * pMethod = m_MethodCompilationOrder[i]; // At this point pMethod->m_pGCInfo is really a pointer that the encoder owns. // We must pass it back to the encoder so it can encode it and pass back a proper ZapBlob * pMethod->m_pGCInfo = pEncoder->FindMethodInfo(this, pMethod->m_pGCInfo); } m_pGCSection->Place(pShortcutMap); m_pGCSection->Place(pMethodInfos); if (m_pUnwindInfoBlob) m_pGCSection->Place(m_pUnwindInfoBlob); if (m_pCallsiteInfoBlob) m_pGCSection->Place(m_pCallsiteInfoBlob); // // Create the method-number-to-gc-info table // UINT32 methodInfoSize = pMethodInfos->GetSize(); COUNT_T nMethods = m_MethodCompilationOrder.GetCount(); UINT16 elemSize = 4; if (methodInfoSize <= 0x10000) { elemSize = 2; // Remember the element size for this map in the module header m_moduleHeaderFlags |= ModuleHeader::SmallGCInfoListEntriesFlag; } // Create the table SIZE_T tableSize = elemSize * nMethods; ZapBlob * pMethodToGcInfoMap = ZapBlob::NewBlob(this, NULL, tableSize); UINT16* pwTableEntries = (UINT16*) pMethodToGcInfoMap->GetData(); UINT32* pdwTableEntries = (UINT32*) pwTableEntries; for (COUNT_T i = 0; i < nMethods; i++) { ZapMethodHeader * pMethod = m_MethodCompilationOrder[i]; ZapGCInfo * pGCInfo = pMethod->m_pGCInfo; UINT32 uOffset = 0; if (pGCInfo->GetType() == ZapNodeType_InnerPtr) { uOffset = ((ZapInnerPtr*)pGCInfo)->GetOffset(); } else { assert(ZapNodeType_Blob == pGCInfo->GetType()); assert(pGCInfo == pMethodInfos); } if (2 == elemSize) { assert(uOffset <= 0xFFFF); pwTableEntries[i] = uOffset; } else { pdwTableEntries[i] = uOffset; } } m_pMethodToGCInfoMap->Place(pMethodToGcInfoMap); #endif // REDHAWK } #ifdef REDHAWK // Place all ZapExceptionInfo blobs into the exception section, and form the lookup table that we'll // use at runtime to find EH info for a given method. void ZapImage::OutputEHInfo() { // For non-REDHAWK builds, we output EH info with the other per-method data in OutputCodeInfo(). // @TODO: consider emitting EH info in order of increasing hotness, like we do for GC info. // Place EH info for every method that has EH. for (COUNT_T i = 0; i < m_MethodCompilationOrder.GetCount(); i++) { ZapMethodHeader * pMethod = m_MethodCompilationOrder[i]; ZapExceptionInfo * pEHInfo = pMethod->m_pExceptionInfo; if ((pEHInfo != NULL) && !pEHInfo->IsPlaced()) { // We add relocs to the exception info here, at the last possible momement before placing // them. That's because adding relocs changes the exception info, but prior to this we // want to be able to use the data of the exception info as a hash key for interning. AddRelocsForEHClauses(pEHInfo); m_pExceptionSection->Place(pEHInfo); } } // Get the offsets for each EH blob that we will emit. MapSHash ehinfoOffsets; UINT32 ehinfoSize; ehinfoSize = m_pExceptionSection->FillInNodeOffsetMap(&ehinfoOffsets); // Chose a table entry size. COUNT_T nMethods = m_MethodCompilationOrder.GetCount(); UINT16 elemSize = 4; if (ehinfoSize <= 0x10000) { elemSize = 2; // Remember the element size for this map in the module header m_moduleHeaderFlags |= ModuleHeader::SmallEHInfoListEntriesFlag; } // Create the table. SIZE_T tableSize = elemSize * nMethods; SArray tableData(tableSize); UINT16* pwTableEntries = (UINT16*)&tableData[0]; UINT32* pdwTableEntries = (UINT32*) pwTableEntries; // Fill in the offset for each method that has EH info. For methods that have no // EH info, we will use a sentinel offset of -1. for (COUNT_T i = 0; i < nMethods; i++) { ZapMethodHeader * pMethod = m_MethodCompilationOrder[i]; ZapExceptionInfo * pEHInfo = pMethod->m_pExceptionInfo; UINT32 uOffset = -1; if (pEHInfo != NULL) { ehinfoOffsets.Lookup(pEHInfo, &uOffset); assert(uOffset != -1); // Can't have a valid offset match the sentinel! assert((4 == elemSize) || (uOffset <= 0xFFFF)); // Size must fit in 2 bytes if we're using hte small rep. } if (2 == elemSize) { pwTableEntries[i] = uOffset; } else { pdwTableEntries[i] = uOffset; } } m_pMethodToEHInfoMap->Place(ZapBlob::NewBlob(this, &tableData[0], tableSize)); } #endif // REDHAWK #ifdef REDHAWK // Add relocs for any EEType references in any typed EH clauses for the given EH Info. void ZapImage::AddRelocsForEHClauses(ZapExceptionInfo * pExceptionInfo) { EE_ILEXCEPTION *pEHInfo = (EE_ILEXCEPTION *)pExceptionInfo->GetData(); _ASSERTE(pEHInfo != NULL); // One set of relocs for the entire set of clauses. Size assuming that every clause has a token. ZapReloc * pRelocs = (ZapReloc *) new (GetHeap()) BYTE[sizeof(ZapReloc) * pEHInfo->EHCount() + sizeof(ZapRelocationType)]; DWORD relocIndex = 0; // Add relocs for EEType references each typed clause. for (int i = 0; i < pEHInfo->EHCount(); i++) { EE_ILEXCEPTION_CLAUSE *pClause = pEHInfo->EHClause(i); if ((pClause->Flags == COR_ILEXCEPTION_CLAUSE_NONE) || (pClause->Flags == COR_ILEXCEPTION_CLAUSE_INDIRECT_TYPE_REFERENCE)) { ZapNode *pEETypeNode = (ZapNode*)pClause->EETypeReference; // @TODO: we're using a full pointer for each EEType reference in the EH clause. This will be // 64bits on a 64bit system, though, which is twice as large as it needs to be. We should make // these 32bit RVA's and compute the final address at runtime when we start supporting 64bit // systems. See comments in ZapInfo::setEHinfo() for more details. // // N.B! If we move to RVAs, then the runtime structure that matches the EE_ILEXCEPTION struct // needs to have a padding field removed. (The C++ compiler introduced 4 bytes of padding between // 'DataSize' and 'Clauses' because 'Clauses' has a pointer field in it. This padding will // disappear when we change the pointers to RVAs.) pRelocs[relocIndex].m_type = IMAGE_REL_BASED_PTR; pRelocs[relocIndex].m_pTargetNode = pEETypeNode; pRelocs[relocIndex].m_offset = (BYTE*)pClause - (BYTE*)pEHInfo + offsetof(EE_ILEXCEPTION_CLAUSE, EETypeReference); pExceptionInfo->ZeroPointer(pRelocs[relocIndex].m_offset); relocIndex++; } } // Did we end up with any relocs? If so, then add them to the blob. if (relocIndex > 0) { // Set sentinel C_ASSERT(offsetof(ZapReloc, m_type) == 0); pRelocs[relocIndex].m_type = IMAGE_REL_INVALID; pExceptionInfo->SetRelocs(pRelocs); } } #endif // REDHAWK // // ZapMethodHeader // #ifdef _TARGET_ARM_ // Avoid ARM hazard due to QualComm Krait processor bug. #define ARM_HAZARD_AVOIDANCE #endif #ifdef ARM_HAZARD_AVOIDANCE // // This code was stolen from the C++ linker (vctools\Link\src\arm.cpp) // bool F32BitInstr(PBYTE pbInstr) { const WORD wInstr = *((const WORD UNALIGNED *) pbInstr); return (wInstr >> 11) >= 0x1D; } bool FHazardCandidate(PBYTE pbInstr) /*++ Routine Description: If the following 4-instruction/10-byte pattern begins at the 0xFB8 offset from a page, then it would cause the hazard. Address Contents ------------------------------ 0x*****FB8 T16 instruction 0x*****FBA T16 instruction 0x*****FBC T16 instruction 0x*****FBE T32 instruction that can branch Finding all such instruction sequences could be very difficult, because the function could have literal's which are mixed with instructions and for now we don't have an easy way to find where literal is. So instead of detecting such instruction sequence, we assume an instr starts at 0x*****FB8 and check whether such sequence exists. This may introduce false positive's. Arguments: pbCon - pointer to the instruction at 0x*****FB8 Return Value: false no hazard true otherwise --*/ { // Check whether there are three 16-bit instructions. for (int i = 0; i < 3; i++) { if (F32BitInstr(pbInstr)) { return false; } pbInstr += sizeof(WORD); } // Check whether next is a 32-bit unconditional PC relative branch. if (!F32BitInstr(pbInstr)) { return false; } // Check 32-bit branch. const DWORD dwInstr = *((const DWORD UNALIGNED *) pbInstr); return // B (A8.6.16, encoding T3) ((dwInstr & 0xD000F800) == 0x8000F000 && (dwInstr & 0x3C0) != 0x380) || // B (A8.6.16, encoding T4) ((dwInstr & 0xD000F800) == 0x9000F000) || // BL (A8.6.23, encoding T1) ((dwInstr & 0xD000F800) == 0xD000F000) || // BLX (A8.6.23, encoding T2) ((dwInstr & 0xD001F800) == 0xC000F000) || // BXJ (A8.6.26, encoding T1) ((dwInstr & 0xFFFFFFF0) == 0x8F00F3C0) || // LDM/LDMIA/LDMFD, with PC in target reg list (A8.6.53, encoding T2) ((dwInstr & 0xA000FFD0) == 0x8000E890 && (dwInstr & 0x02F) != 0x02D) || // LDMDB/LDMEA, with PC in target reg list (A.8.6.55, encoding T1) ((dwInstr & 0xA000FFD0) == 0x8000E910) || // LDR immediate, with PC as target reg (A8.6.57, encoding T3) ((dwInstr & 0xF000FFF0) == 0xF000F8D0 && (dwInstr & 0x0F) != 0x0F) || // LDR immediate, with PC as target reg (A8.6.57, encoding T4) ((dwInstr & 0xF800FFF0) == 0xF800F850 && (dwInstr & 0x0F) != 0x0F) || // LDR literal, with PC as target reg (A8.6.59, encoding T2) ((dwInstr & 0xF000FF7F) == 0xF000F85F) || // LDR register, with PC as target reg (A8.6.60, encoding T2) ((dwInstr & 0xFFC0FFF0) == 0xF000F850) || // POP, with PC in target reg list (A8.6.122, encoding T2) ((dwInstr & 0xA000FFFF) == 0x8000E8BD) || // POP, with PC as target reg (A8.6.122, encoding T3) (dwInstr == 0xFB04F85D) || // TBB/TBH (A8.6.226, encoding T1) ((dwInstr & 0xFFE0FFF0) == 0xF000E8D0); } // // End of code stolen from the C++ linker // #endif // ARM_HAZARD_AVOIDANCE #if defined(_TARGET_X86_) || defined(ARM_HAZARD_AVOIDANCE) DWORD ZapCodeBlob::ComputeRVA(ZapWriter * pZapWriter, DWORD dwPos) { void * pData = GetData(); SIZE_T size = GetSize(); DWORD dwAlignment = GetAlignment(); dwPos = AlignUp(dwPos, dwAlignment); #ifdef _TARGET_X86_ // // Padding for straddler relocations. // // The maximum size of padding const DWORD cbAdjustForDynamicBaseMax = 256; // Find padding that gives us minimum number of straddlers DWORD nMinStraddlers = MAXDWORD; DWORD bestPad = 0; for (DWORD pad = 0; pad < cbAdjustForDynamicBaseMax; pad += dwAlignment) { COUNT_T nStraddlers = GetCountOfStraddlerRelocations(dwPos + pad); if (nStraddlers < nMinStraddlers) { nMinStraddlers = nStraddlers; bestPad = pad; // It won't get better than this. if (nMinStraddlers == 0) break; } } DWORD dwPaddedPos = dwPos + bestPad; SetRVA(dwPaddedPos); return dwPaddedPos + size; #endif // _TARGET_X86_ #ifdef ARM_HAZARD_AVOIDANCE for (DWORD dwPadding = 0; dwPadding < 0x1000; dwPadding += dwAlignment) { DWORD dwPaddedPos = dwPos + dwPadding; BOOL fHasHazard = FALSE; // // Go through all possible places where the hazard may occur within the data block // // The possible occurences of the hazard are always at offset 0xFB8 within the 4k page. // Start with the first page containing the block. DWORD dwFirstHazardPos = AlignDown(dwPaddedPos, 0x1000) + 0xFB8; if (dwFirstHazardPos < dwPaddedPos) dwFirstHazardPos += 0x1000; // The last possible occurence of the hazard is 10 bytes before the end of the block DWORD dwLastHazardPos = (dwPaddedPos + size) - 10; for (DWORD dwHazardPos = dwFirstHazardPos; dwHazardPos <= dwLastHazardPos; dwHazardPos += 0x1000) { int offset = dwHazardPos - dwPaddedPos; if (FHazardCandidate((BYTE * )pData + offset)) { fHasHazard = TRUE; break; } } if (!fHasHazard) { SetRVA(dwPaddedPos); return dwPaddedPos + size; } } // There is a theoretical chance that we may not be able to find a suitable padding // to workaround the bug. In this case don't attempt to workaround the bug, // and simply place the code at the next natural RVM. It should happen // very rarely for very large methods only, and there should be no retail devices // with this processor bug. SetRVA(dwPos); return dwPos + size; #endif } template class ZapCodeBlobConst : public ZapCodeBlob { protected: ZapCodeBlobConst(SIZE_T cbSize) : ZapCodeBlob(cbSize) { } public: virtual UINT GetAlignment() { return alignment; } static ZapCodeBlob * NewBlob(ZapWriter * pWriter, PVOID pData, SIZE_T cbSize) { S_SIZE_T cbAllocSize = S_SIZE_T(sizeof(ZapCodeBlobConst)) + S_SIZE_T(cbSize); if(cbAllocSize.IsOverflow()) ThrowHR(COR_E_OVERFLOW); void * pMemory = new (pWriter->GetHeap()) BYTE[cbAllocSize.Value()]; ZapCodeBlob * pZapCodeBlob = new (pMemory) ZapCodeBlobConst(cbSize); if (pData != NULL) memcpy((void*)(pZapCodeBlob + 1), pData, cbSize); return pZapCodeBlob; } }; ZapCodeBlob * ZapCodeBlob::NewAlignedBlob(ZapWriter * pWriter, PVOID pData, SIZE_T cbSize, SIZE_T cbAlignment) { switch (cbAlignment) { case 1: return ZapCodeBlobConst<1>::NewBlob(pWriter, pData, cbSize); case 2: return ZapCodeBlobConst<2>::NewBlob(pWriter, pData, cbSize); case 4: return ZapCodeBlobConst<4>::NewBlob(pWriter, pData, cbSize); case 8: return ZapCodeBlobConst<8>::NewBlob(pWriter, pData, cbSize); case 16: return ZapCodeBlobConst<16>::NewBlob(pWriter, pData, cbSize); default: _ASSERTE(!"Requested alignment not supported"); return NULL; } } #endif // See function prototype for details on why this iterator is "partial" BOOL ZapMethodHeader::PartialTargetMethodIterator::GetNext(CORINFO_METHOD_HANDLE *pHnd) { _ASSERTE(pHnd != NULL); if (m_pCurReloc == NULL) { return FALSE; } while (m_pCurReloc->m_type != IMAGE_REL_INVALID) { ZapNode * pTarget = m_pCurReloc->m_pTargetNode; ZapNodeType type = pTarget->GetType(); m_pCurReloc++; if (type == ZapNodeType_InnerPtr) { pTarget = ((ZapInnerPtr *)pTarget)->GetBase(); type = pTarget->GetType(); } if (type == ZapNodeType_MethodEntryPoint) { *pHnd = ((ZapMethodEntryPoint *)pTarget)->GetHandle(); return TRUE; } } return FALSE; } void ZapCodeMethodDescs::Save(ZapWriter * pZapWriter) { ZapImage * pImage = ZapImage::GetImage(pZapWriter); COUNT_T nUnwindInfos = 0; for (COUNT_T curMethod = m_iStartMethod; curMethod < m_iEndMethod; curMethod++) { ZapMethodHeader * pMethod = pImage->m_MethodCompilationOrder[curMethod]; DWORD dwRVA = pImage->m_pPreloader->MapMethodHandle(pMethod->GetHandle()); if (pMethod->m_pExceptionInfo != NULL) dwRVA |= HAS_EXCEPTION_INFO_MASK; pImage->Write(&dwRVA, sizeof(dwRVA)); nUnwindInfos++; #ifdef WIN64EXCEPTIONS ZapUnwindInfo * pFragment = pMethod->m_pUnwindInfoFragments; while (pFragment != NULL) { if (pFragment != pMethod->m_pUnwindInfo && pFragment->GetCode() == pMethod->m_pCode) { dwRVA = 0; pImage->Write(&dwRVA, sizeof(dwRVA)); nUnwindInfos++; } pFragment = pFragment->GetNextFragment(); } #endif } _ASSERTE(nUnwindInfos == m_nUnwindInfos); } // // ZapMethodEntryPoint // void ZapMethodEntryPoint::Resolve(ZapImage * pImage) { DWORD rvaValue = pImage->m_pPreloader->MapMethodEntryPoint(GetHandle()); #ifdef _DEBUG if (rvaValue == NULL) { mdMethodDef token; pImage->GetCompileInfo()->GetMethodDef(GetHandle(), &token); pImage->Error(token, S_OK, W("MapMethodEntryPoint failed")); } else #endif { SetRVA(rvaValue); } } ZapMethodEntryPoint * ZapMethodEntryPointTable::GetMethodEntryPoint(CORINFO_METHOD_HANDLE handle, CORINFO_ACCESS_FLAGS accessFlags) { ZapMethodEntryPoint * pMethodEntryPoint = m_entries.Lookup(MethodEntryPointKey(handle, accessFlags)); if (pMethodEntryPoint != NULL) return pMethodEntryPoint; #ifdef _DEBUG mdMethodDef token; m_pImage->GetCompileInfo()->GetMethodDef(handle, &token); #endif pMethodEntryPoint = new (m_pImage->GetHeap()) ZapMethodEntryPoint(handle, accessFlags); m_entries.Add(pMethodEntryPoint); return pMethodEntryPoint; } void ZapMethodEntryPointTable::Resolve() { for (MethodEntryPointTable::Iterator i = m_entries.Begin(), end = m_entries.End(); i != end; i++) { ZapMethodEntryPoint * pMethodEntryPoint = *i; // Skip unused entrypoints - they may be omitted in the image if (!pMethodEntryPoint->IsUsed()) continue; pMethodEntryPoint->Resolve(m_pImage); } } ZapNode * ZapMethodEntryPointTable::CanDirectCall(ZapMethodEntryPoint * pMethodEntryPoint, ZapMethodHeader * pCaller) { CORINFO_METHOD_HANDLE caller = pCaller->GetHandle(); CORINFO_METHOD_HANDLE callee = pMethodEntryPoint->GetHandle(); CorInfoIndirectCallReason reason; if (m_pImage->canIntraModuleDirectCall(caller, callee, &reason, pMethodEntryPoint->GetAccessFlags())) { ZapNode * pCode = m_pImage->GetCompiledMethod(callee)->GetCode(); #ifdef _TARGET_ARM_ pCode = m_pImage->GetInnerPtr(pCode, THUMB_CODE); #endif // _TARGET_ARM_ return pCode; } else { if (!pMethodEntryPoint->IsUsed()) { // This method entry point is going to be used for indirect call. // Record this so that later we will assign it an RVA. pMethodEntryPoint->SetIsUsed(); } return NULL; } } #ifdef WIN64EXCEPTIONS ZapGCInfo * ZapGCInfoTable::GetGCInfo(PVOID pGCInfo, SIZE_T cbGCInfo, PVOID pUnwindInfo, SIZE_T cbUnwindInfo) { ZapGCInfo * pNode = m_blobs.Lookup(GCInfoKey(pGCInfo, cbGCInfo, pUnwindInfo, cbUnwindInfo)); if (pNode != NULL) { return pNode; } pNode = ZapGCInfo::NewGCInfo(m_pImage, pGCInfo, cbGCInfo, pUnwindInfo, cbUnwindInfo); m_blobs.Add(pNode); return pNode; } ZapGCInfo * ZapGCInfo::NewGCInfo(ZapWriter * pWriter, PVOID pGCInfo, SIZE_T cbGCInfo, PVOID pUnwindInfo, SIZE_T cbUnwindInfo) { S_SIZE_T cbAllocSize = S_SIZE_T(sizeof(ZapGCInfo)) + S_SIZE_T(cbGCInfo) + S_SIZE_T(cbUnwindInfo); if(cbAllocSize.IsOverflow()) ThrowHR(COR_E_OVERFLOW); void * pMemory = new (pWriter->GetHeap()) BYTE[cbAllocSize.Value()]; ZapGCInfo * pZapGCInfo = new (pMemory) ZapGCInfo(cbGCInfo, cbUnwindInfo); memcpy(pZapGCInfo->GetGCInfo(), pGCInfo, cbGCInfo); memcpy(pZapGCInfo->GetUnwindInfo(), pUnwindInfo, cbUnwindInfo); #if !defined(_TARGET_X86_) // Make sure the personality routine thunk is created pZapGCInfo->GetPersonalityRoutine(ZapImage::GetImage(pWriter)); #endif // !defined(_TARGET_X86_) return pZapGCInfo; } #else ZapGCInfo * ZapGCInfoTable::GetGCInfo(PVOID pBlob, SIZE_T cbBlob) { ZapGCInfo * pNode = m_blobs.Lookup(ZapBlob::SHashKey(pBlob, cbBlob)); if (pNode != NULL) { return pNode; } pNode = ZapBlob::NewBlob(m_pImage, pBlob, cbBlob); m_blobs.Add(pNode); return pNode; } #endif // // ZapUnwindInfo // void ZapUnwindInfo::Save(ZapWriter * pZapWriter) { T_RUNTIME_FUNCTION runtimeFunction; #if defined(_TARGET_ARM_) || defined(_TARGET_ARM64_) RUNTIME_FUNCTION__SetBeginAddress(&runtimeFunction, GetStartAddress()); runtimeFunction.UnwindData = m_pUnwindData->GetRVA(); #elif defined(_TARGET_AMD64_) runtimeFunction.BeginAddress = GetStartAddress(); runtimeFunction.EndAddress = GetEndAddress(); ULONG unwindData = m_pUnwindData->GetRVA(); if (m_pUnwindData->GetType() == ZapNodeType_UnwindInfo) // Chained unwind info unwindData |= RUNTIME_FUNCTION_INDIRECT; runtimeFunction.UnwindData = unwindData; #elif defined(_TARGET_X86_) runtimeFunction.BeginAddress = GetStartAddress(); ULONG unwindData = m_pUnwindData->GetRVA(); if (m_pUnwindData->GetType() == ZapNodeType_UnwindInfo) // Chained unwind info unwindData |= RUNTIME_FUNCTION_INDIRECT; runtimeFunction.UnwindData = unwindData; #else PORTABILITY_ASSERT("ZapUnwindInfo"); #endif pZapWriter->Write(&runtimeFunction, sizeof(runtimeFunction)); } #ifdef WIN64EXCEPTIONS // Compare the unwind infos by their offset int __cdecl ZapUnwindInfo::CompareUnwindInfo(const void* a_, const void* b_) { ZapUnwindInfo * a = *(ZapUnwindInfo **)a_; ZapUnwindInfo * b = *(ZapUnwindInfo **)b_; if (a->GetStartOffset() > b->GetStartOffset()) { _ASSERTE(a->GetStartOffset() >= b->GetEndOffset()); return 1; } if (a->GetStartOffset() < b->GetStartOffset()) { _ASSERTE(a->GetEndOffset() <= b->GetEndOffset()); return -1; } _ASSERTE(a == b); return 0; } #if defined(_TARGET_AMD64_) UINT ZapUnwindData::GetAlignment() { return sizeof(ULONG); } DWORD ZapUnwindData::GetSize() { DWORD dwSize = ZapBlob::GetSize(); #ifndef REDHAWK // Add space for personality routine, it must be 4-byte aligned. // Everything in the UNWIND_INFO has already had its size included in size dwSize = AlignUp(dwSize, sizeof(ULONG)); dwSize += sizeof(ULONG); #endif //REDHAWK return dwSize; } void ZapUnwindData::Save(ZapWriter * pZapWriter) { ZapImage * pImage = ZapImage::GetImage(pZapWriter); PVOID pData = GetData(); DWORD dwSize = GetBlobSize(); UNWIND_INFO * pUnwindInfo = (UNWIND_INFO *)pData; // Check whether the size is what we expect it to be _ASSERTE(dwSize == offsetof(UNWIND_INFO, UnwindCode) + pUnwindInfo->CountOfUnwindCodes * sizeof(UNWIND_CODE)); #ifndef REDHAWK pUnwindInfo->Flags = UNW_FLAG_EHANDLER | UNW_FLAG_UHANDLER; #endif //REDHAWK pZapWriter->Write(pData, dwSize); #ifndef REDHAWK DWORD dwPad = AlignmentPad(dwSize, sizeof(DWORD)); if (dwPad != 0) pZapWriter->WritePad(dwPad); ULONG personalityRoutine = GetPersonalityRoutine(pImage)->GetRVA(); pZapWriter->Write(&personalityRoutine, sizeof(personalityRoutine)); #endif //REDHAWK } #elif defined(_TARGET_ARM_) || defined(_TARGET_ARM64_) UINT ZapUnwindData::GetAlignment() { return sizeof(ULONG); } DWORD ZapUnwindData::GetSize() { DWORD dwSize = ZapBlob::GetSize(); // Add space for personality routine, it must be 4-byte aligned. // Everything in the UNWIND_INFO has already had its size included in size dwSize = AlignUp(dwSize, sizeof(ULONG)); dwSize += sizeof(ULONG); return dwSize; } void ZapUnwindData::Save(ZapWriter * pZapWriter) { ZapImage * pImage = ZapImage::GetImage(pZapWriter); PVOID pData = GetData(); DWORD dwSize = GetBlobSize(); UNWIND_INFO * pUnwindInfo = (UNWIND_INFO *)pData; // Set the 'X' bit to indicate that there is a personality routine associated with this method *(LONG *)pUnwindInfo |= (1<<20); pZapWriter->Write(pData, dwSize); DWORD dwPad = AlignmentPad(dwSize, sizeof(DWORD)); if (dwPad != 0) pZapWriter->WritePad(dwPad); ULONG personalityRoutine = GetPersonalityRoutine(pImage)->GetRVA(); pZapWriter->Write(&personalityRoutine, sizeof(personalityRoutine)); } #else UINT ZapUnwindData::GetAlignment() { PORTABILITY_ASSERT("ZapUnwindData::GetAlignment"); return sizeof(ULONG); } DWORD ZapUnwindData::GetSize() { PORTABILITY_ASSERT("ZapUnwindData::GetSize"); return -1; } void ZapUnwindData::Save(ZapWriter * pZapWriter) { PORTABILITY_ASSERT("ZapUnwindData::Save"); } #endif ZapNode * ZapUnwindData::GetPersonalityRoutine(ZapImage * pImage) { // Use different personality routine pointer for filter funclets so that we can quickly tell at runtime // whether funclet is a filter. #ifdef FEATURE_READYTORUN_COMPILER if (IsReadyToRunCompilation()) { ReadyToRunHelper helperNum = IsFilterFunclet() ? READYTORUN_HELPER_PersonalityRoutineFilterFunclet : READYTORUN_HELPER_PersonalityRoutine; return pImage->GetImportTable()->GetPlacedIndirectHelperThunk(helperNum); } #endif return pImage->GetHelperThunk(IsFilterFunclet() ? CORINFO_HELP_EE_PERSONALITY_ROUTINE_FILTER_FUNCLET : CORINFO_HELP_EE_PERSONALITY_ROUTINE); } ZapUnwindData * ZapUnwindData::NewUnwindData(ZapWriter * pWriter, PVOID pData, SIZE_T cbSize, BOOL fIsFilterFunclet) { SIZE_T cbAllocSize = sizeof(ZapUnwindData) + cbSize; void * pMemory = new (pWriter->GetHeap()) BYTE[cbAllocSize]; ZapUnwindData * pZapUnwindData = fIsFilterFunclet ? (new (pMemory) ZapFilterFuncletUnwindData(cbSize)) : (new (pMemory) ZapUnwindData(cbSize)); memcpy((void*)(pZapUnwindData + 1), pData, cbSize); #if !defined(_TARGET_X86_) // Make sure the personality routine thunk is created pZapUnwindData->GetPersonalityRoutine(ZapImage::GetImage(pWriter)); #endif // !defined(_TARGET_X86_) return pZapUnwindData; } ZapUnwindData * ZapUnwindDataTable::GetUnwindData(PVOID pBlob, SIZE_T cbBlob, BOOL fIsFilterFunclet) { ZapUnwindData * pNode = (ZapUnwindData *)m_blobs.Lookup(ZapUnwindDataKey(pBlob, cbBlob, fIsFilterFunclet)); if (pNode != NULL) { return pNode; } pNode = ZapUnwindData::NewUnwindData(m_pImage, pBlob, cbBlob, fIsFilterFunclet); m_blobs.Add(pNode); return pNode; } #endif // WIN64EXCEPTIONS // // ZapDebugInfo // ZapDebugInfo * ZapDebugInfoTable::GetDebugInfo(PVOID pBlob, SIZE_T cbBlob) { ZapDebugInfo * pNode = m_blobs.Lookup(ZapBlob::SHashKey(pBlob, cbBlob)); m_nCount++; if (pNode != NULL) { return pNode; } pNode = ZapBlob::NewBlob(m_pImage, pBlob, cbBlob); m_blobs.Add(pNode); return pNode; } void ZapDebugInfoTable::PrepareLayout() { if (m_nCount == 0) return; // Make sure that the number of methods is odd number m_nCount |= 1; m_pTable = new (m_pImage->GetHeap()) ZapNode * [m_nCount]; } void ZapDebugInfoTable::PlaceDebugInfo(ZapMethodHeader * pMethod) { // Place the debug info blob if it is not placed yet ZapBlob * pDebugInfo = pMethod->GetDebugInfo(); if (pDebugInfo == NULL) { return; } if (!pDebugInfo->IsPlaced()) { m_pImage->m_pDebugSection->Place(pDebugInfo); } mdMethodDef md; IfFailThrow(m_pImage->GetCompileInfo()->GetMethodDef(pMethod->GetHandle(), &md)); COUNT_T index = GetDebugRidEntryHash(md) % m_nCount; ZapNode * pHead = m_pTable[index]; if (pHead == NULL) { // The common case - single rid entry. m_pTable[index] = pMethod; return; } // Create linked list of labelled entries if we do not have one yet if (pHead->GetType() != ZapNodeType_DebugInfoLabelledEntry) { m_pTable[index] = new (m_pImage->GetHeap()) LabelledEntry((ZapMethodHeader *)pHead); } // Insert the method at the end of the linked list LabelledEntry * pEntry = (LabelledEntry *)m_pTable[index]; while (pEntry->m_pNext != NULL) pEntry = pEntry->m_pNext; pEntry->m_pNext = new (m_pImage->GetHeap()) LabelledEntry(pMethod); } void ZapDebugInfoTable::FinishLayout() { // Go over the table again and place all labelled entries for (COUNT_T i = 0; i < m_nCount; i++) { ZapNode * pNode = m_pTable[i]; if (pNode == NULL || pNode->GetType() != ZapNodeType_DebugInfoLabelledEntry) continue; LabelledEntry * pEntry = (LabelledEntry *)pNode; while (pEntry != NULL) { m_pImage->m_pDebugSection->Place(pEntry); pEntry = pEntry->m_pNext; } } } void ZapDebugInfoTable::Save(ZapWriter * pZapWriter) { for (COUNT_T i = 0; i < m_nCount; i++) { CORCOMPILE_DEBUG_ENTRY entry = 0; ZapNode * pNode = m_pTable[i]; if (pNode != NULL) { if (pNode->GetType() == ZapNodeType_DebugInfoLabelledEntry) entry |= pNode->GetRVA() | CORCOMPILE_DEBUG_MULTIPLE_ENTRIES; else entry = ((ZapMethodHeader *)pNode)->GetDebugInfo()->GetRVA(); } pZapWriter->Write(&entry, sizeof(entry)); } } void ZapDebugInfoTable::LabelledEntry::Save(ZapWriter * pZapWriter) { CORCOMPILE_DEBUG_LABELLED_ENTRY entry; entry.nativeCodeRVA = m_pMethod->GetCode()->GetRVA(); entry.debugInfoOffset = m_pMethod->GetDebugInfo()->GetRVA(); if (m_pNext != NULL) entry.debugInfoOffset |= CORCOMPILE_DEBUG_MULTIPLE_ENTRIES; pZapWriter->Write(&entry, sizeof(entry)); } // // ZapProfileData // void ZapProfileData::Save(ZapWriter * pZapWriter) { ZapImage * pImage = ZapImage::GetImage(pZapWriter); CORCOMPILE_METHOD_PROFILE_LIST profileData; ZeroMemory(&profileData, sizeof(CORCOMPILE_METHOD_PROFILE_LIST)); if (m_pNext != NULL) pImage->WriteReloc(&profileData, offsetof(CORCOMPILE_METHOD_PROFILE_LIST, next), m_pNext, 0, IMAGE_REL_BASED_PTR); pZapWriter->Write(&profileData, sizeof(CORCOMPILE_METHOD_PROFILE_LIST)); } // Zapping of ExeptionInfoTable ZapExceptionInfoLookupTable::ZapExceptionInfoLookupTable(ZapImage *pImage) : m_pImage(pImage) { _ASSERTE(m_pImage->m_pExceptionSection != NULL); m_pImage->m_pExceptionSection->Place(this); } void ZapExceptionInfoLookupTable::PlaceExceptionInfoEntry(ZapNode* pCode, ZapExceptionInfo* pExceptionInfo) { ExceptionInfoEntry entry; entry.m_pCode = pCode; entry.m_pExceptionInfo = pExceptionInfo; m_exceptionInfoEntries.Append(entry); m_pImage->m_pExceptionSection->Place(pExceptionInfo); } DWORD ZapExceptionInfoLookupTable::GetSize() { if (m_exceptionInfoEntries.GetCount() == 0) return 0; DWORD numExceptionInfoEntries = m_exceptionInfoEntries.GetCount(); // 1 sentential entry at the end of the table. return (numExceptionInfoEntries + 1) * sizeof(CORCOMPILE_EXCEPTION_LOOKUP_TABLE_ENTRY); } void ZapExceptionInfoLookupTable::Save(ZapWriter* pZapWriter) { if(m_exceptionInfoEntries.GetCount() == 0) return; for(COUNT_T i = 0; i < m_exceptionInfoEntries.GetCount(); ++i) { DWORD methodStartRVA = m_exceptionInfoEntries[i].m_pCode->GetRVA(); ZapExceptionInfo* pExceptionInfo = m_exceptionInfoEntries[i].m_pExceptionInfo; CORCOMPILE_EXCEPTION_LOOKUP_TABLE_ENTRY lookupEntry; lookupEntry.MethodStartRVA = methodStartRVA; lookupEntry.ExceptionInfoRVA = pExceptionInfo->GetRVA(); pZapWriter->Write(&lookupEntry, sizeof(CORCOMPILE_EXCEPTION_LOOKUP_TABLE_ENTRY)); #ifdef _DEBUG // Make sure there are no gaps between 2 consecutive CORCOMPILE_EXCEPTION_CLAUSE // We use pointer arithmatic to calculate the number of EHClause for a method. if (i != 0) { ZapExceptionInfo* pPreviousExceptionInfo = m_exceptionInfoEntries[i-1].m_pExceptionInfo; DWORD size = pExceptionInfo->GetRVA() - pPreviousExceptionInfo->GetRVA(); DWORD ehClauseSize = size % sizeof(CORCOMPILE_EXCEPTION_CLAUSE); CONSISTENCY_CHECK_MSG(ehClauseSize == 0, "There must be no gaps between 2 successive clause arrays, please check ZapExceptionInfo alignment"); } #endif } // write a sentinal entry.. this entry helps to find the number of EHClauses for the last entry CORCOMPILE_EXCEPTION_LOOKUP_TABLE_ENTRY sentinalEntry; ExceptionInfoEntry lastEntry = m_exceptionInfoEntries[m_exceptionInfoEntries.GetCount() -1]; ZapExceptionInfo* pLastExceptionInfo = lastEntry.m_pExceptionInfo; sentinalEntry.MethodStartRVA = (DWORD)-1; // points just after the end of the Exception table // the sentinal node m_pExceptionInfo pointer actually points to an invalid CORCOMPILE_EXCEPTION_CLAUSE // area. The lookup algorithm will never dereference the sentinal pointer, and hence this is safe sentinalEntry.ExceptionInfoRVA = pLastExceptionInfo->GetRVA() + pLastExceptionInfo->GetSize(); pZapWriter->Write(&sentinalEntry, sizeof(CORCOMPILE_EXCEPTION_LOOKUP_TABLE_ENTRY)); } DWORD ZapUnwindInfoLookupTable::GetSize() { // Sentinal entry at the end return (GetNumEntries() + 1) * sizeof (DWORD); } void ZapUnwindInfoLookupTable::Save(ZapWriter* pZapWriter) { ZapVirtualSection * pRuntimeFunctionSection = m_pRuntimeFunctionSection; // Create Lookup entries. // 1 lookup entry for each RUNTIME_FUNCTION_LOOKUP_STRIDE K of code. COUNT_T nUnwindInfos = pRuntimeFunctionSection->GetNodeCount(); DWORD dwCodeSectionStartAddress = m_pCodeSection->GetRVA(); DWORD nLookupEntries = 0; DWORD entry; DWORD nTotalLookupEntries = GetNumEntries(); // write out the first entry entry = 0; pZapWriter->Write(&entry, sizeof(DWORD)); nLookupEntries++; if (nLookupEntries == nTotalLookupEntries) goto WriteSentinel; for (COUNT_T i = 1; i < nUnwindInfos; ++i) { ZapUnwindInfo* pUnwindInfo = (ZapUnwindInfo*)pRuntimeFunctionSection->GetNode(i); DWORD RelativePC = pUnwindInfo->GetStartAddress() - dwCodeSectionStartAddress; COUNT_T iCurrentIndex = RelativePC / RUNTIME_FUNCTION_LOOKUP_STRIDE; // Note that we should not be using pUnwindInfo->GetEndAddress() here. The binary search // in the VM that's accelerated by this table does not look at the EndAddress either, and // so not using EndAddress here assures consistency. COUNT_T iPreviousIndex = (RelativePC - 1)/ RUNTIME_FUNCTION_LOOKUP_STRIDE; while(iPreviousIndex >= nLookupEntries) { entry = i - 1; pZapWriter->Write(&entry, sizeof(DWORD)); nLookupEntries++; if (nLookupEntries == nTotalLookupEntries) goto WriteSentinel; } if (iCurrentIndex == nLookupEntries) { entry = i; pZapWriter->Write(&entry, sizeof(DWORD)); nLookupEntries++; if (nLookupEntries == nTotalLookupEntries) goto WriteSentinel; } } WriteSentinel: // There should always be one sentinel entry at the end. The sentinel entry will // be good to cover the rest of the section to account for extra padding. _ASSERTE(nLookupEntries <= nTotalLookupEntries); while (nLookupEntries <= nTotalLookupEntries) { entry = nUnwindInfos - 1; pZapWriter->Write(&entry, sizeof (DWORD)); nLookupEntries ++; } } DWORD ZapColdCodeMap::GetSize() { return m_pRuntimeFunctionSection->GetNodeCount() * sizeof(CORCOMPILE_COLD_METHOD_ENTRY); } void ZapColdCodeMap::Save(ZapWriter* pZapWriter) { ZapImage * pImage = ZapImage::GetImage(pZapWriter); ZapNode * pPendingCode = NULL; COUNT_T curMethod = 0; COUNT_T nUnwindInfos = m_pRuntimeFunctionSection->GetNodeCount(); for (COUNT_T i = 0; i < nUnwindInfos; ++i) { CORCOMPILE_COLD_METHOD_ENTRY entry; ZapUnwindInfo* pUnwindInfo = (ZapUnwindInfo*)m_pRuntimeFunctionSection->GetNode(i); #ifdef WIN64EXCEPTIONS if (pUnwindInfo->GetCode() == pPendingCode) { entry.mainFunctionEntryRVA = 0; entry.hotCodeSize = 0; } else #endif { pPendingCode = pUnwindInfo->GetCode(); ZapMethodHeader * pMethod; for (;;) { pMethod = pImage->m_MethodCompilationOrder[curMethod]; if (pMethod->m_pColdCode == pPendingCode) break; curMethod++; } #ifdef WIN64EXCEPTIONS entry.mainFunctionEntryRVA = pMethod->m_pUnwindInfo->GetRVA(); #endif entry.hotCodeSize = pMethod->m_pCode->GetSize(); } pZapWriter->Write(&entry, sizeof(entry)); } } DWORD ZapHelperThunk::GetSize() { return (m_dwHelper & CORCOMPILE_HELPER_PTR) ? sizeof(TADDR) : HELPER_TABLE_ENTRY_LEN; } void ZapHelperThunk::Save(ZapWriter * pZapWriter) { #ifdef _DEBUG LOG((LF_ZAP, LL_INFO1000000, "Emitting JIT helper table entry for helper %3d (%s)\n", (USHORT) m_dwHelper, s_rgHelperNames[(USHORT) m_dwHelper])); #endif // _DEBUG // Save the index of the helper, the actual code for the thunk will be generated at runtime pZapWriter->Write(&m_dwHelper, sizeof(DWORD)); DWORD pad = GetSize() - sizeof(DWORD); if (pad > 0) { void * pPad = _alloca(pad); memset(pPad, DEFAULT_CODE_BUFFER_INIT, pad); pZapWriter->Write(pPad, pad); } } void ZapLazyHelperThunk::Place(ZapImage * pImage) { m_pArg = pImage->m_pPreloadSections[CORCOMPILE_SECTION_MODULE]; m_pTarget = pImage->GetHelperThunk(m_dwHelper); pImage->m_pLazyHelperSection->Place(this); } DWORD ZapLazyHelperThunk::GetSize() { return SaveWorker(NULL); } void ZapLazyHelperThunk::Save(ZapWriter * pZapWriter) { SaveWorker(pZapWriter); } DWORD ZapLazyHelperThunk::SaveWorker(ZapWriter * pZapWriter) { ZapImage * pImage = ZapImage::GetImage(pZapWriter); BYTE buffer[42]; // Buffer big enough to hold any reasonable helper thunk sequence BYTE * p = buffer; #if defined(_TARGET_X86_) // mov edx, module *p++ = 0xBA; if (pImage != NULL) pImage->WriteReloc(buffer, (int)(p - buffer), m_pArg, 0, IMAGE_REL_BASED_PTR); p += 4; // jmp JIT_StrCns *p++ = 0xE9; if (pImage != NULL) pImage->WriteReloc(buffer, (int)(p - buffer), m_pTarget, 0, IMAGE_REL_BASED_REL32); p += 4; #elif defined(_TARGET_AMD64_) *p++ = 0x48; *p++ = 0x8D; #ifdef UNIX_AMD64_ABI // lea rsi, module *p++ = 0x35; #else // lea rdx, module *p++ = 0x15; #endif if (pImage != NULL) pImage->WriteReloc(buffer, (int)(p - buffer), m_pArg, 0, IMAGE_REL_BASED_REL32); p += 4; // jmp JIT_StrCns *p++ = 0xE9; if (pImage != NULL) pImage->WriteReloc(buffer, (int)(p - buffer), m_pTarget, 0, IMAGE_REL_BASED_REL32); p += 4; #elif defined(_TARGET_ARM_) // movw r1, module *(WORD *)(p + 0) = 0xf240; *(WORD *)(p + 2) = 1 << 8; // movt r1, module *(WORD *)(p + 4) = 0xf2c0; *(WORD *)(p + 6) = 1 << 8; if (pImage != NULL) pImage->WriteReloc(buffer, (int)(p - buffer), m_pArg, 0, IMAGE_REL_BASED_THUMB_MOV32); p += 8; // b JIT_StrCns *(WORD *)(p + 0) = 0xf000; *(WORD *)(p + 2) = 0xb800; if (pImage != NULL) pImage->WriteReloc(buffer, (int)(p - buffer), m_pTarget, 0, IMAGE_REL_BASED_THUMB_BRANCH24); p += 4; #elif defined(_TARGET_ARM64_) // ldr x1, [PC+8] *(DWORD *)(p) =0x58000041; p += 4; // b JIT_StrCns *(DWORD *)(p) = 0x14000000; if (pImage != NULL) pImage->WriteReloc(buffer, (int)(p - buffer), m_pTarget, 0, IMAGE_REL_ARM64_BRANCH26); p += 4; if (pImage != NULL) pImage->WriteReloc(buffer, (int)(p - buffer), m_pArg, 0, IMAGE_REL_BASED_PTR); p += 8; #else PORTABILITY_ASSERT("ZapLazyHelperThunk::Save"); #endif _ASSERTE(p - buffer <= sizeof(buffer)); if (pZapWriter != NULL) pZapWriter->Write(&buffer, (int)(p - buffer)); return (DWORD) (p - buffer); }