summaryrefslogtreecommitdiff
path: root/packaging/0009-Remove-relocations-from-SECTION_MethodDesc-for-ngene.patch
diff options
context:
space:
mode:
Diffstat (limited to 'packaging/0009-Remove-relocations-from-SECTION_MethodDesc-for-ngene.patch')
-rw-r--r--packaging/0009-Remove-relocations-from-SECTION_MethodDesc-for-ngene.patch595
1 files changed, 595 insertions, 0 deletions
diff --git a/packaging/0009-Remove-relocations-from-SECTION_MethodDesc-for-ngene.patch b/packaging/0009-Remove-relocations-from-SECTION_MethodDesc-for-ngene.patch
new file mode 100644
index 0000000000..d348274583
--- /dev/null
+++ b/packaging/0009-Remove-relocations-from-SECTION_MethodDesc-for-ngene.patch
@@ -0,0 +1,595 @@
+From 37436d59134e8beea0fcb4a7bb2b9c3e1e5e9714 Mon Sep 17 00:00:00 2001
+From: gbalykov <g.balykov@samsung.com>
+Date: Sat, 3 Jun 2017 06:31:28 +0300
+Subject: [PATCH 09/32] Remove relocations from SECTION_MethodDesc for ngened
+ images for fields accessed from jit code for Linux ARM (#11963)
+
+---
+ .../superpmi/superpmi-shared/methodcontext.cpp | 6 ++
+ .../superpmi/superpmi-shared/methodcontext.h | 1 +
+ src/debug/daccess/nidump.cpp | 4 +-
+ src/inc/corinfo.h | 7 ++
+ src/inc/fixuppointer.h | 99 +++++++++++++++++++++-
+ src/jit/importer.cpp | 14 +++
+ src/vm/dllimport.cpp | 12 +++
+ src/vm/genmeth.cpp | 10 +--
+ src/vm/jitinterface.cpp | 12 +++
+ src/vm/method.cpp | 18 +++-
+ src/vm/method.hpp | 32 +++++--
+ src/vm/methodtablebuilder.cpp | 4 +-
+ src/vm/prestub.cpp | 13 +++
+ 13 files changed, 211 insertions(+), 21 deletions(-)
+
+diff --git a/src/ToolBox/superpmi/superpmi-shared/methodcontext.cpp b/src/ToolBox/superpmi/superpmi-shared/methodcontext.cpp
+index e60ef83..4c5fb61 100644
+--- a/src/ToolBox/superpmi/superpmi-shared/methodcontext.cpp
++++ b/src/ToolBox/superpmi/superpmi-shared/methodcontext.cpp
+@@ -1573,6 +1573,7 @@ void MethodContext::recGetCallInfo(CORINFO_RESOLVED_TOKEN* pResolvedToken,
+ value.stubLookup.runtimeLookup.indirections = (DWORD)pResult->stubLookup.runtimeLookup.indirections;
+ value.stubLookup.runtimeLookup.testForNull = (DWORD)pResult->stubLookup.runtimeLookup.testForNull;
+ value.stubLookup.runtimeLookup.testForFixup = (DWORD)pResult->stubLookup.runtimeLookup.testForFixup;
++ value.stubLookup.runtimeLookup.indirectFirstOffset = (DWORD)pResult->stubLookup.runtimeLookup.indirectFirstOffset;
+ for (int i = 0; i < CORINFO_MAXINDIRECTIONS; i++)
+ value.stubLookup.runtimeLookup.offsets[i] = (DWORDLONG)pResult->stubLookup.runtimeLookup.offsets[i];
+ }
+@@ -1583,6 +1584,7 @@ void MethodContext::recGetCallInfo(CORINFO_RESOLVED_TOKEN* pResolvedToken,
+ value.stubLookup.runtimeLookup.indirections = (DWORD)0;
+ value.stubLookup.runtimeLookup.testForNull = (DWORD)0;
+ value.stubLookup.runtimeLookup.testForFixup = (DWORD)0;
++ value.stubLookup.runtimeLookup.indirectFirstOffset = (DWORD)0;
+ for (int i = 0; i < CORINFO_MAXINDIRECTIONS; i++)
+ value.stubLookup.runtimeLookup.offsets[i] = (DWORDLONG)0;
+
+@@ -1761,6 +1763,7 @@ void MethodContext::repGetCallInfo(CORINFO_RESOLVED_TOKEN* pResolvedToken,
+ pResult->stubLookup.runtimeLookup.indirections = (WORD)value.stubLookup.runtimeLookup.indirections;
+ pResult->stubLookup.runtimeLookup.testForNull = value.stubLookup.runtimeLookup.testForNull != 0;
+ pResult->stubLookup.runtimeLookup.testForFixup = value.stubLookup.runtimeLookup.testForFixup != 0;
++ pResult->stubLookup.runtimeLookup.indirectFirstOffset = value.stubLookup.runtimeLookup.indirectFirstOffset != 0;
+ for (int i = 0; i < CORINFO_MAXINDIRECTIONS; i++)
+ pResult->stubLookup.runtimeLookup.offsets[i] = (SIZE_T)value.stubLookup.runtimeLookup.offsets[i];
+ }
+@@ -3218,6 +3221,7 @@ void MethodContext::recEmbedGenericHandle(CORINFO_RESOLVED_TOKEN* pResolve
+ value.lookup.runtimeLookup.indirections = (DWORD)pResult->lookup.runtimeLookup.indirections;
+ value.lookup.runtimeLookup.testForNull = (DWORD)pResult->lookup.runtimeLookup.testForNull;
+ value.lookup.runtimeLookup.testForFixup = (DWORD)pResult->lookup.runtimeLookup.testForFixup;
++ value.lookup.runtimeLookup.indirectFirstOffset = (DWORD)pResult->lookup.runtimeLookup.indirectFirstOffset;
+ for (int i = 0; i < CORINFO_MAXINDIRECTIONS; i++)
+ value.lookup.runtimeLookup.offsets[i] = (DWORDLONG)pResult->lookup.runtimeLookup.offsets[i];
+ }
+@@ -3228,6 +3232,7 @@ void MethodContext::recEmbedGenericHandle(CORINFO_RESOLVED_TOKEN* pResolve
+ value.lookup.runtimeLookup.indirections = (DWORD)0;
+ value.lookup.runtimeLookup.testForNull = (DWORD)0;
+ value.lookup.runtimeLookup.testForFixup = (DWORD)0;
++ value.lookup.runtimeLookup.indirectFirstOffset = (DWORD)0;
+ for (int i = 0; i < CORINFO_MAXINDIRECTIONS; i++)
+ value.lookup.runtimeLookup.offsets[i] = (DWORDLONG)0;
+ // copy the constLookup view of the union
+@@ -3305,6 +3310,7 @@ void MethodContext::repEmbedGenericHandle(CORINFO_RESOLVED_TOKEN* pResolve
+ pResult->lookup.runtimeLookup.indirections = (WORD)value.lookup.runtimeLookup.indirections;
+ pResult->lookup.runtimeLookup.testForNull = value.lookup.runtimeLookup.testForNull != 0;
+ pResult->lookup.runtimeLookup.testForFixup = value.lookup.runtimeLookup.testForFixup != 0;
++ pResult->lookup.runtimeLookup.indirectFirstOffset = value.lookup.runtimeLookup.indirectFirstOffset != 0;
+ for (int i = 0; i < CORINFO_MAXINDIRECTIONS; i++)
+ pResult->lookup.runtimeLookup.offsets[i] = (size_t)value.lookup.runtimeLookup.offsets[i];
+ }
+diff --git a/src/ToolBox/superpmi/superpmi-shared/methodcontext.h b/src/ToolBox/superpmi/superpmi-shared/methodcontext.h
+index f7c0e16..4887522 100644
+--- a/src/ToolBox/superpmi/superpmi-shared/methodcontext.h
++++ b/src/ToolBox/superpmi/superpmi-shared/methodcontext.h
+@@ -240,6 +240,7 @@ public:
+ DWORD testForNull;
+ DWORD testForFixup;
+ DWORDLONG offsets[CORINFO_MAXINDIRECTIONS];
++ DWORD indirectFirstOffset;
+ };
+ struct Agnostic_CORINFO_CONST_LOOKUP
+ {
+diff --git a/src/debug/daccess/nidump.cpp b/src/debug/daccess/nidump.cpp
+index 5e8302f..d1e69f6 100644
+--- a/src/debug/daccess/nidump.cpp
++++ b/src/debug/daccess/nidump.cpp
+@@ -7947,7 +7947,7 @@ void NativeImageDumper::DumpMethodDesc( PTR_MethodDesc md, PTR_Module module )
+ if( !CHECK_OPT(METHODDESCS) )
+ CoverageReadString( dac_cast<TADDR>(ndmd->GetLibNameRaw()) );
+
+- PTR_NDirectWriteableData wnd( nd->m_pWriteableData );
++ PTR_NDirectWriteableData wnd( ndmd->GetWriteableData() );
+ DisplayStartStructureWithOffset( m_pWriteableData,
+ DPtrToPreferredAddr(wnd),
+ sizeof(*wnd),
+@@ -8103,7 +8103,7 @@ void NativeImageDumper::DumpMethodDesc( PTR_MethodDesc md, PTR_Module module )
+ }
+ //now handle the contents of the m_pMethInst/m_pPerInstInfo union.
+ unsigned numSlots = imd->m_wNumGenericArgs;
+- PTR_Dictionary inst(imd->m_pPerInstInfo);
++ PTR_Dictionary inst(imd->IMD_GetMethodDictionary());
+ unsigned dictSize;
+ if( kind == InstantiatedMethodDesc::SharedMethodInstantiation )
+ {
+diff --git a/src/inc/corinfo.h b/src/inc/corinfo.h
+index 2495de2..f6a136c 100644
+--- a/src/inc/corinfo.h
++++ b/src/inc/corinfo.h
+@@ -1323,6 +1323,13 @@ struct CORINFO_RUNTIME_LOOKUP
+ bool testForFixup;
+
+ SIZE_T offsets[CORINFO_MAXINDIRECTIONS];
++
++ // If set, first offset is indirect.
++ // 0 means that value stored at first offset (offsets[0]) from pointer is next pointer, to which the next offset
++ // (offsets[1]) is added and so on.
++ // 1 means that value stored at first offset (offsets[0]) from pointer is offset1, and the next pointer is
++ // stored at pointer+offsets[0]+offset1.
++ bool indirectFirstOffset;
+ } ;
+
+ // Result of calling embedGenericHandle
+diff --git a/src/inc/fixuppointer.h b/src/inc/fixuppointer.h
+index 549023a..38ae348 100644
+--- a/src/inc/fixuppointer.h
++++ b/src/inc/fixuppointer.h
+@@ -30,6 +30,10 @@ template<typename PTR_TYPE>
+ class RelativePointer
+ {
+ public:
++
++ static constexpr bool isRelative = true;
++ typedef PTR_TYPE type;
++
+ #ifndef DACCESS_COMPILE
+ RelativePointer()
+ {
+@@ -173,6 +177,10 @@ template<typename PTR_TYPE>
+ class FixupPointer
+ {
+ public:
++
++ static constexpr bool isRelative = false;
++ typedef PTR_TYPE type;
++
+ // Returns whether the encoded pointer is NULL.
+ BOOL IsNull() const
+ {
+@@ -237,6 +245,10 @@ template<typename PTR_TYPE>
+ class RelativeFixupPointer
+ {
+ public:
++
++ static constexpr bool isRelative = true;
++ typedef PTR_TYPE type;
++
+ // Implicit copy/move is not allowed
+ RelativeFixupPointer<PTR_TYPE>(const RelativeFixupPointer<PTR_TYPE> &) =delete;
+ RelativeFixupPointer<PTR_TYPE>(RelativeFixupPointer<PTR_TYPE> &&) =delete;
+@@ -384,7 +396,7 @@ private:
+ // Fixup used for RelativePointer
+ #define IMAGE_REL_BASED_RelativePointer IMAGE_REL_BASED_RELPTR
+
+-#else // FEATURE_PREJIT
++#endif // FEATURE_PREJIT
+
+ //----------------------------------------------------------------------------
+ // PlainPointer is simple pointer wrapper to support compilation without indirections
+@@ -393,6 +405,10 @@ template<typename PTR_TYPE>
+ class PlainPointer
+ {
+ public:
++
++ static constexpr bool isRelative = false;
++ typedef PTR_TYPE type;
++
+ // Returns whether the encoded pointer is NULL.
+ BOOL IsNull() const
+ {
+@@ -499,11 +515,13 @@ private:
+ TADDR m_ptr;
+ };
+
++#ifndef FEATURE_PREJIT
++
+ #define FixupPointer PlainPointer
+ #define RelativePointer PlainPointer
+ #define RelativeFixupPointer PlainPointer
+
+-#endif // FEATURE_PREJIT
++#endif // !FEATURE_PREJIT
+
+ //----------------------------------------------------------------------------
+ // RelativePointer32 is pointer encoded as relative 32-bit offset. It is used
+@@ -513,6 +531,10 @@ template<typename PTR_TYPE>
+ class RelativePointer32
+ {
+ public:
++
++ static constexpr bool isRelative = true;
++ typedef PTR_TYPE type;
++
+ // Returns whether the encoded pointer is NULL.
+ BOOL IsNull() const
+ {
+@@ -581,4 +603,77 @@ private:
+ INT32 m_delta;
+ };
+
++template<bool isMaybeNull, typename T, typename PT>
++typename PT::type
++ReadPointer(const T *base, const PT T::* pPointerFieldMember)
++{
++ LIMITED_METHOD_DAC_CONTRACT;
++
++ uintptr_t offset = (uintptr_t) &(base->*pPointerFieldMember) - (uintptr_t) base;
++
++ if (isMaybeNull)
++ {
++ return PT::GetValueMaybeNullAtPtr(dac_cast<TADDR>(base) + offset);
++ }
++ else
++ {
++ return PT::GetValueAtPtr(dac_cast<TADDR>(base) + offset);
++ }
++}
++
++template<typename T, typename PT>
++typename PT::type
++ReadPointerMaybeNull(const T *base, const PT T::* pPointerFieldMember)
++{
++ LIMITED_METHOD_DAC_CONTRACT;
++
++ return ReadPointer<true>(base, pPointerFieldMember);
++}
++
++template<typename T, typename PT>
++typename PT::type
++ReadPointer(const T *base, const PT T::* pPointerFieldMember)
++{
++ LIMITED_METHOD_DAC_CONTRACT;
++
++ return ReadPointer<false>(base, pPointerFieldMember);
++}
++
++template<bool isMaybeNull, typename T, typename C, typename PT>
++typename PT::type
++ReadPointer(const T *base, const C T::* pFirstPointerFieldMember, const PT C::* pSecondPointerFieldMember)
++{
++ LIMITED_METHOD_DAC_CONTRACT;
++
++ const PT *ptr = &(base->*pFirstPointerFieldMember.*pSecondPointerFieldMember);
++ uintptr_t offset = (uintptr_t) ptr - (uintptr_t) base;
++
++ if (isMaybeNull)
++ {
++ return PT::GetValueMaybeNullAtPtr(dac_cast<TADDR>(base) + offset);
++ }
++ else
++ {
++ return PT::GetValueAtPtr(dac_cast<TADDR>(base) + offset);
++ }
++}
++
++template<typename T, typename C, typename PT>
++typename PT::type
++ReadPointerMaybeNull(const T *base, const C T::* pFirstPointerFieldMember, const PT C::* pSecondPointerFieldMember)
++{
++ LIMITED_METHOD_DAC_CONTRACT;
++
++ return ReadPointer<true>(base, pFirstPointerFieldMember, pSecondPointerFieldMember);
++}
++
++template<typename T, typename C, typename PT>
++typename PT::type
++ReadPointer(const T *base, const C T::* pFirstPointerFieldMember, const PT C::* pSecondPointerFieldMember)
++{
++ LIMITED_METHOD_DAC_CONTRACT;
++
++ return ReadPointer<false>(base, pFirstPointerFieldMember, pSecondPointerFieldMember);
++}
++
+ #endif //_FIXUPPOINTER_H
+diff --git a/src/jit/importer.cpp b/src/jit/importer.cpp
+index 2d50741..80c0b75 100644
+--- a/src/jit/importer.cpp
++++ b/src/jit/importer.cpp
+@@ -1975,15 +1975,29 @@ GenTreePtr Compiler::impRuntimeLookupToTree(CORINFO_RESOLVED_TOKEN* pResolvedTok
+ nullptr DEBUGARG("impRuntimeLookup slot"));
+ }
+
++ GenTreePtr indOffTree = nullptr;
++
+ // Applied repeated indirections
+ for (WORD i = 0; i < pRuntimeLookup->indirections; i++)
+ {
++ if (i == 1 && pRuntimeLookup->indirectFirstOffset)
++ {
++ indOffTree = impCloneExpr(slotPtrTree, &slotPtrTree, NO_CLASS_HANDLE, (unsigned)CHECK_SPILL_ALL,
++ nullptr DEBUGARG("impRuntimeLookup indirectFirstOffset"));
++ }
++
+ if (i != 0)
+ {
+ slotPtrTree = gtNewOperNode(GT_IND, TYP_I_IMPL, slotPtrTree);
+ slotPtrTree->gtFlags |= GTF_IND_NONFAULTING;
+ slotPtrTree->gtFlags |= GTF_IND_INVARIANT;
+ }
++
++ if (i == 1 && pRuntimeLookup->indirectFirstOffset)
++ {
++ slotPtrTree = gtNewOperNode(GT_ADD, TYP_I_IMPL, indOffTree, slotPtrTree);
++ }
++
+ if (pRuntimeLookup->offsets[i] != 0)
+ {
+ slotPtrTree =
+diff --git a/src/vm/dllimport.cpp b/src/vm/dllimport.cpp
+index cf546cd..3ec8b5f 100644
+--- a/src/vm/dllimport.cpp
++++ b/src/vm/dllimport.cpp
+@@ -2294,7 +2294,19 @@ void NDirectStubLinker::DoNDirect(ILCodeStream *pcsEmit, DWORD dwStubFlags, Meth
+ //pcsEmit->EmitCALL(METHOD__STUBHELPERS__GET_NDIRECT_TARGET, 1, 1);
+ pcsEmit->EmitLDC(offsetof(NDirectMethodDesc, ndirect.m_pWriteableData));
+ pcsEmit->EmitADD();
++
++ if (decltype(NDirectMethodDesc::ndirect.m_pWriteableData)::isRelative)
++ {
++ pcsEmit->EmitDUP();
++ }
++
+ pcsEmit->EmitLDIND_I();
++
++ if (decltype(NDirectMethodDesc::ndirect.m_pWriteableData)::isRelative)
++ {
++ pcsEmit->EmitADD();
++ }
++
+ pcsEmit->EmitLDIND_I();
+ }
+ }
+diff --git a/src/vm/genmeth.cpp b/src/vm/genmeth.cpp
+index d5b435b..dc55221 100644
+--- a/src/vm/genmeth.cpp
++++ b/src/vm/genmeth.cpp
+@@ -1518,9 +1518,9 @@ void InstantiatedMethodDesc::SetupGenericMethodDefinition(IMDInternalImport *pIM
+ S_SIZE_T dwAllocSize = S_SIZE_T(numTyPars) * S_SIZE_T(sizeof(TypeHandle));
+
+ // the memory allocated for m_pMethInst will be freed if the declaring type fails to load
+- m_pPerInstInfo = (Dictionary *) pamTracker->Track(pAllocator->GetLowFrequencyHeap()->AllocMem(dwAllocSize));
++ m_pPerInstInfo.SetValue((Dictionary *) pamTracker->Track(pAllocator->GetLowFrequencyHeap()->AllocMem(dwAllocSize)));
+
+- TypeHandle * pInstDest = (TypeHandle *)m_pPerInstInfo;
++ TypeHandle * pInstDest = (TypeHandle *) IMD_GetMethodDictionaryNonNull();
+ for(unsigned int i = 0; i < numTyPars; i++)
+ {
+ hEnumTyPars.EnumNext(&tkTyPar);
+@@ -1553,7 +1553,7 @@ void InstantiatedMethodDesc::SetupWrapperStubWithInstantiations(MethodDesc* wrap
+
+ m_pWrappedMethodDesc.SetValue(wrappedMD);
+ m_wFlags2 = WrapperStubWithInstantiations | (m_wFlags2 & ~KindMask);
+- m_pPerInstInfo = (Dictionary*)pInst;
++ m_pPerInstInfo.SetValueMaybeNull((Dictionary*)pInst);
+
+ _ASSERTE(FitsIn<WORD>(numGenericArgs));
+ m_wNumGenericArgs = static_cast<WORD>(numGenericArgs);
+@@ -1571,7 +1571,7 @@ void InstantiatedMethodDesc::SetupSharedMethodInstantiation(DWORD numGenericArgs
+ _ASSERTE(numGenericArgs != 0);
+ // Initially the dictionary layout is empty
+ m_wFlags2 = SharedMethodInstantiation | (m_wFlags2 & ~KindMask);
+- m_pPerInstInfo = (Dictionary *)pPerInstInfo;
++ m_pPerInstInfo.SetValueMaybeNull((Dictionary *)pPerInstInfo);
+
+ _ASSERTE(FitsIn<WORD>(numGenericArgs));
+ m_wNumGenericArgs = static_cast<WORD>(numGenericArgs);
+@@ -1589,7 +1589,7 @@ void InstantiatedMethodDesc::SetupUnsharedMethodInstantiation(DWORD numGenericAr
+
+ // The first field is never used
+ m_wFlags2 = UnsharedMethodInstantiation | (m_wFlags2 & ~KindMask);
+- m_pPerInstInfo = (Dictionary *)pInst;
++ m_pPerInstInfo.SetValueMaybeNull((Dictionary *)pInst);
+
+ _ASSERTE(FitsIn<WORD>(numGenericArgs));
+ m_wNumGenericArgs = static_cast<WORD>(numGenericArgs);
+diff --git a/src/vm/jitinterface.cpp b/src/vm/jitinterface.cpp
+index f7617ad..08965a7 100644
+--- a/src/vm/jitinterface.cpp
++++ b/src/vm/jitinterface.cpp
+@@ -3141,6 +3141,8 @@ void CEEInfo::ComputeRuntimeLookupForSharedGenericToken(DictionaryEntryKind entr
+ CORINFO_RUNTIME_LOOKUP *pResult = &pResultLookup->runtimeLookup;
+ pResult->signature = NULL;
+
++ pResult->indirectFirstOffset = 0;
++
+ // Unless we decide otherwise, just do the lookup via a helper function
+ pResult->indirections = CORINFO_USEHELPER;
+
+@@ -3264,6 +3266,11 @@ void CEEInfo::ComputeRuntimeLookupForSharedGenericToken(DictionaryEntryKind entr
+ #endif
+ pResult->offsets[0] = offsetof(InstantiatedMethodDesc, m_pPerInstInfo);
+
++ if (decltype(InstantiatedMethodDesc::m_pPerInstInfo)::isRelative)
++ {
++ pResult->indirectFirstOffset = 1;
++ }
++
+ ULONG data;
+ IfFailThrow(sigptr.GetData(&data));
+ pResult->offsets[1] = sizeof(TypeHandle) * data;
+@@ -3568,6 +3575,11 @@ NoSpecialCase:
+
+ // Indirect through dictionary table pointer in InstantiatedMethodDesc
+ pResult->offsets[0] = offsetof(InstantiatedMethodDesc, m_pPerInstInfo);
++
++ if (decltype(InstantiatedMethodDesc::m_pPerInstInfo)::isRelative)
++ {
++ pResult->indirectFirstOffset = 1;
++ }
+ }
+ }
+
+diff --git a/src/vm/method.cpp b/src/vm/method.cpp
+index c8c1b9f..1407264 100644
+--- a/src/vm/method.cpp
++++ b/src/vm/method.cpp
+@@ -3651,7 +3651,14 @@ MethodDesc::Fixup(
+ }
+ }
+
+- image->FixupPointerField(this, offsetof(InstantiatedMethodDesc, m_pPerInstInfo));
++ if (decltype(InstantiatedMethodDesc::m_pPerInstInfo)::isRelative)
++ {
++ image->FixupRelativePointerField(this, offsetof(InstantiatedMethodDesc, m_pPerInstInfo));
++ }
++ else
++ {
++ image->FixupPointerField(this, offsetof(InstantiatedMethodDesc, m_pPerInstInfo));
++ }
+
+ // Generic methods are dealt with specially to avoid encoding the formal method type parameters
+ if (IsTypicalMethodDefinition())
+@@ -3730,7 +3737,14 @@ MethodDesc::Fixup(
+
+ NDirectMethodDesc *pNMD = (NDirectMethodDesc *)this;
+
+- image->FixupPointerField(this, offsetof(NDirectMethodDesc, ndirect.m_pWriteableData));
++ if (decltype(NDirectMethodDesc::ndirect.m_pWriteableData)::isRelative)
++ {
++ image->FixupRelativePointerField(this, offsetof(NDirectMethodDesc, ndirect.m_pWriteableData));
++ }
++ else
++ {
++ image->FixupPointerField(this, offsetof(NDirectMethodDesc, ndirect.m_pWriteableData));
++ }
+
+ NDirectWriteableData *pWriteableData = pNMD->GetWriteableData();
+ NDirectImportThunkGlue *pImportThunkGlue = pNMD->GetNDirectImportThunkGlue();
+diff --git a/src/vm/method.hpp b/src/vm/method.hpp
+index ae65f30..9023a1b 100644
+--- a/src/vm/method.hpp
++++ b/src/vm/method.hpp
+@@ -2590,7 +2590,11 @@ public:
+ };
+
+ // The writeable part of the methoddesc.
+- PTR_NDirectWriteableData m_pWriteableData;
++#if defined(PLATFORM_UNIX) && defined(_TARGET_ARM_)
++ RelativePointer<PTR_NDirectWriteableData> m_pWriteableData;
++#else
++ PlainPointer<PTR_NDirectWriteableData> m_pWriteableData;
++#endif
+
+ #ifdef HAS_NDIRECT_IMPORT_PRECODE
+ RelativePointer<PTR_NDirectImportThunkGlue> m_pImportThunkGlue;
+@@ -2812,11 +2816,11 @@ public:
+ return (ndirect.m_wFlags & kStdCallWithRetBuf) != 0;
+ }
+
+- NDirectWriteableData* GetWriteableData() const
++ PTR_NDirectWriteableData GetWriteableData() const
+ {
+- LIMITED_METHOD_CONTRACT;
++ LIMITED_METHOD_DAC_CONTRACT;
+
+- return ndirect.m_pWriteableData;
++ return ReadPointer(this, &NDirectMethodDesc::ndirect, &decltype(NDirectMethodDesc::ndirect)::m_pWriteableData);
+ }
+
+ PTR_NDirectImportThunkGlue GetNDirectImportThunkGlue()
+@@ -3221,7 +3225,7 @@ public:
+ if (IMD_IsGenericMethodDefinition())
+ return TRUE;
+ else
+- return m_pPerInstInfo != NULL;
++ return !m_pPerInstInfo.IsNull();
+ }
+
+ // All varieties of InstantiatedMethodDesc's support this method.
+@@ -3229,13 +3233,21 @@ public:
+ {
+ LIMITED_METHOD_DAC_CONTRACT;
+
+- return Instantiation(m_pPerInstInfo->GetInstantiation(), m_wNumGenericArgs);
++ return Instantiation(IMD_GetMethodDictionary()->GetInstantiation(), m_wNumGenericArgs);
+ }
+
+ PTR_Dictionary IMD_GetMethodDictionary()
+ {
+ LIMITED_METHOD_DAC_CONTRACT;
+- return m_pPerInstInfo;
++
++ return ReadPointerMaybeNull(this, &InstantiatedMethodDesc::m_pPerInstInfo);
++ }
++
++ PTR_Dictionary IMD_GetMethodDictionaryNonNull()
++ {
++ LIMITED_METHOD_DAC_CONTRACT;
++
++ return ReadPointer(this, &InstantiatedMethodDesc::m_pPerInstInfo);
+ }
+
+ BOOL IMD_IsGenericMethodDefinition()
+@@ -3394,7 +3406,11 @@ public: // <TODO>make private: JITinterface.cpp accesses through this </TODO>
+ //
+ // For generic method definitions that are not the typical method definition (e.g. C<int>.m<U>)
+ // this field is null; to obtain the instantiation use LoadMethodInstantiation
+- PTR_Dictionary m_pPerInstInfo; //SHARED
++#if defined(PLATFORM_UNIX) && defined(_TARGET_ARM_)
++ RelativePointer<PTR_Dictionary> m_pPerInstInfo; //SHARED
++#else
++ PlainPointer<PTR_Dictionary> m_pPerInstInfo; //SHARED
++#endif
+
+ private:
+ WORD m_wFlags2;
+diff --git a/src/vm/methodtablebuilder.cpp b/src/vm/methodtablebuilder.cpp
+index 3162f7c..fdf4f48 100644
+--- a/src/vm/methodtablebuilder.cpp
++++ b/src/vm/methodtablebuilder.cpp
+@@ -6121,8 +6121,8 @@ MethodTableBuilder::InitMethodDesc(
+ NDirectMethodDesc *pNewNMD = (NDirectMethodDesc*)pNewMD;
+
+ // Allocate writeable data
+- pNewNMD->ndirect.m_pWriteableData = (NDirectWriteableData*)
+- AllocateFromHighFrequencyHeap(S_SIZE_T(sizeof(NDirectWriteableData)));
++ pNewNMD->ndirect.m_pWriteableData.SetValue((NDirectWriteableData*)
++ AllocateFromHighFrequencyHeap(S_SIZE_T(sizeof(NDirectWriteableData))));
+
+ #ifdef HAS_NDIRECT_IMPORT_PRECODE
+ pNewNMD->ndirect.m_pImportThunkGlue.SetValue(Precode::Allocate(PRECODE_NDIRECT_IMPORT, pNewMD,
+diff --git a/src/vm/prestub.cpp b/src/vm/prestub.cpp
+index 88bd9de..af3f190 100644
+--- a/src/vm/prestub.cpp
++++ b/src/vm/prestub.cpp
+@@ -2405,6 +2405,9 @@ void ProcessDynamicDictionaryLookup(TransitionBlock * pTransitionBlock
+
+ pResult->testForFixup = pResult->testForNull = false;
+ pResult->signature = NULL;
++
++ pResult->indirectFirstOffset = 0;
++
+ pResult->indirections = CORINFO_USEHELPER;
+
+ DWORD numGenericArgs = 0;
+@@ -2455,6 +2458,11 @@ void ProcessDynamicDictionaryLookup(TransitionBlock * pTransitionBlock
+ pResult->indirections = 2;
+ pResult->offsets[0] = offsetof(InstantiatedMethodDesc, m_pPerInstInfo);
+
++ if (decltype(InstantiatedMethodDesc::m_pPerInstInfo)::isRelative)
++ {
++ pResult->indirectFirstOffset = 1;
++ }
++
+ ULONG data;
+ IfFailThrow(sigptr.GetData(&data));
+ pResult->offsets[1] = sizeof(TypeHandle) * data;
+@@ -2494,6 +2502,11 @@ void ProcessDynamicDictionaryLookup(TransitionBlock * pTransitionBlock
+ // Indirect through dictionary table pointer in InstantiatedMethodDesc
+ pResult->offsets[0] = offsetof(InstantiatedMethodDesc, m_pPerInstInfo);
+
++ if (decltype(InstantiatedMethodDesc::m_pPerInstInfo)::isRelative)
++ {
++ pResult->indirectFirstOffset = 1;
++ }
++
+ *pDictionaryIndexAndSlot |= dictionarySlot;
+ }
+ }
+--
+2.7.4
+