text: // 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.
  
  //
  // This header contains binder-generated data structures that the runtime consumes.
  //
  #ifndef RHDUMP_TARGET_NEUTRAL
  #include "TargetPtrs.h"
  #endif
  #ifndef RHDUMP
  #include "WellKnownMethods.h"
  #endif
  #if !defined(RHDUMP) || defined(RHDUMP_TARGET_NEUTRAL)
  //
  // Region Relative Addresses (RRAs)
  //
  // Now that RH code can be emitted as a regular object file which can be linked with arbitrary native code,
  // the module header (or any RH code or data) no longer has access to the OS module handle for which it is a
  // part. As a result it's not a good idea to encode pointers as RVAs (relative virtual addresses) in the
  // header or other RH metadata since without the OS module handle we have no mechanism to derive a VA (virtual
  // address) from these RVAs.
  //
  // It's still desirable to utilize relative addresses since this saves space on 64-bit machines. So instead of
  // RVAs we introduce the concept of RRAs (Region Relative Addresses). These are 32-bit offsets from the start
  // of one of several "regions" defined by the Redhawk ModuleHeader. See the RegionTypes enum below for the
  // currently defined regions. These are all contiguous regions of memory emitted by the binder (e.g. the text
  // section which contains all RH method code).
  //
  // To recover a VA from an RRA you simply add the base VA of the correct region to the RRA. One weakness of
  // the current mechanism is that there's no strong type checking to ensure that you use the correct region to
  // interpret a given RRA. Possibly something could be done with templates here. For now we have a relatively
  // small set of RRAs and as much as possible I've tried to abstract access to these via macros and other
  // techniques to limit the places where mistakes can be made. If this turns out to be a bug farm we can
  // revisit the issue.
  //
  
  #ifdef RHDUMP
  // Always use RVAs
  typedef UInt32 RegionPtr;
  #else
  typedef TgtPTR_UInt8 RegionPtr; 
  #endif
  
  struct ModuleHeader
  {
      // A subset of these flags match those that we need in the SectionMethodList at runtime. This set must be kept
      // in sync with the definitions in SectionMethodList::SectionMethodListFlags
      enum ModuleHeaderFlags
      {
          SmallPageListEntriesFlag    = 0x00000001,   // if set, 2-byte page list entries, 4-byte otherwise
          SmallGCInfoListEntriesFlag  = 0x00000002,   // if set, 2-byte gc info list entries, 4-byte otherwise
          SmallEHInfoListEntriesFlag  = 0x00000004,   // if set, 2-byte EH info list entries, 4-byte otherwise
          FlagsMatchingSMLFlagsMask   = 0x00000007,   // Mask for flags that match those in SectionMethodList at runtime
          UsesClrEHFlag               = 0x00000008,   // set if module expects CLR EH model, clear if module expects RH EH model.
          StandaloneExe               = 0x00000010,   // this module represents the only (non-runtime) module in the process
      };
  
      enum ModuleHeaderConstants : UInt32
      {
          CURRENT_VERSION             = 3,            // Version of the module header protocol. Increment on
                                                      // breaking changes
          DELTA_SHORTCUT_TABLE_SIZE   = 16,
          MAX_REGIONS                 = 8,            // Max number of regions described by the Regions array
          MAX_WELL_KNOWN_METHODS      = 8,            // Max number of methods described by the WellKnownMethods array
          NULL_RRA                    = 0xffffffff,   // NULL value for region relative addresses (0 is often a
                                                      // legal RRA)
      };
  
      // The region types defined so far. Each module has at most one of each of these regions.
      enum RegionTypes
      {
          TEXT_REGION                 = 0,            // Code
          DATA_REGION                 = 1,            // Read/write data
          RDATA_REGION                = 2,            // Read-only data
          IAT_REGION                  = 3,            // Import Address Table
      };
  
      UInt32  Version;
      UInt32  Flags;                      // Various flags passed from the binder to the runtime (See ModuleHeaderFlags below).
      UInt32  CountOfMethods;             // Count of method bodies in this module.  This count is used by the SectionMethodList as the size of its various arrays
      UInt32  RraCodeMapInfo;             // RRA to SectionMethodList, includes ip-to-method map and method gc info
      UInt32  RraStaticsGCDataSection;    // RRA to region containing GC statics
      UInt32  RraStaticsGCInfo;           // RRA to GC info for module statics (an array of StaticGcDesc structs)
      UInt32  RraThreadStaticsGCInfo;     // RRA to GC info for module thread statics (an array of StaticGcDesc structs)
  #ifdef FEATURE_CACHED_INTERFACE_DISPATCH
      UInt32  RraInterfaceDispatchCells;  // RRA to array of cache data structures used to dispatch interface calls
      UInt32  CountInterfaceDispatchCells;// Number of elements in above array
  #endif
      UInt32  RraFrozenObjects;           // RRA to the image's string literals (used for object ref verification of frozen strings)
      UInt32  SizeFrozenObjects;          // size, in bytes, of string literals
      UInt32  RraEHInfo;                  // RRA to the EH info, which is past the variable length GC info.
      UInt32  SizeEHTypeTable;            // The EH info starts with a table of types used by the clauses, this is the size (in bytes) of that table.
      UInt32  RraSystemObjectEEType;      // RRA to the IAT entry for the classlib's System.Object EEType. Zero if this is the classlib itself.
      UInt32  RraUnwindInfoBlob;          // RRA to blob used for unwind infos that are referenced by the method GC info
      UInt32  RraCallsiteInfoBlob;        // RRA to blob used for callsite GC root strings that are referenced by the method GC info
      UInt32  SizeStubCode;               // size, in bytes, of stub code at the end of the TEXT_REGION. See ZapImage::SaveModuleHeader for details.
      UInt32  RraReadOnlyBlobs;           // RRA to list of read-only opaque data blobs
      UInt32  SizeReadOnlyBlobs;          // size, in bytes, of the read-only data blobs above
      UInt32  RraNativeInitFunctions;     // RRA to table of function pointers for initialization functions from linked in native code
      UInt32  CountNativeInitFunctions;   // count of the number of entries in the table above
      // info for loop hijacking {
      UInt32  RraLoopIndirCells;          // RRA to start of loop hijacking indirection cells
      UInt32  RraLoopIndirCellChunkBitmap;// RRA to a bitmap which tracks redirected loop hijack indirection cell chunks
      UInt32  RraLoopRedirTargets;        // RRA to start of code block which implements the redirected targets for hijacking loops
      UInt32  RraLoopTargets;             // RRA to start of compressed info describing the original loop targets (prior to redirection)
      UInt32  CountOfLoopIndirCells;      // count of loop hijacking indirection cells
      // } // end info for loop hijacking
      UInt32  RraDispatchMapLookupTable;  // RRA of table of pointers to DispatchMaps
  
      UInt32  WellKnownMethods[MAX_WELL_KNOWN_METHODS];   // Array of methods with well known semantics defined
                                                          // in this module
  
      // These two arrays, RegionSize and RegionPtr are parallel arrays.  They are not simply an array of 
      // structs because that would waste space on 64-bit due to the natural alignment requirement of the 
      // pointers.
      UInt32          RegionSize[MAX_REGIONS];    // sizes of each region in the module
      RegionPtr       RegionPtr[MAX_REGIONS];     // Base addresses for the RRAs above
  
      TgtPTR_UInt32   PointerToTlsIndex;  // Pointer to TLS index if this module uses thread statics (cannot be
                                          // RRA because it's fixed up by the OS loader)
      UInt32          TlsStartOffset;     // Offset into TLS section at which this module's thread statics begin
  
  #ifdef FEATURE_PROFILING
      UInt32          RraProfilingEntries;        // RRA to start of profile info
      UInt32          CountOfProfilingEntries;    // count of profile info records
  #endif // FEATURE_PROFILING
  
      UInt32          RraArrayBaseEEType;       // RRA to the classlib's array base type EEType (usually System.Array), zero if this is not the classlib
  
  #ifdef FEATURE_CUSTOM_IMPORTS
      UInt32          RraCustomImportDescriptors;      // RRA to an array of CustomImportDescriptors
      UInt32          CountCustomImportDescriptors;    // count of entries in the above array
  #endif // FEATURE_CUSTOM_IMPORTS
  
      UInt32          RraGenericUnificationDescs;
      UInt32          CountOfGenericUnificationDescs;
  
      UInt32          RraGenericUnificationIndirCells;
      UInt32          CountOfGenericUnificationIndirCells;
  
      UInt32          RraColdToHotMappingInfo;
  
      // Macro to generate an inline accessor for RRA-based fields.
  #ifdef RHDUMP
  #define DEFINE_GET_ACCESSOR(_field, _region)\
      inline UInt64 Get##_field() { return Rra##_field == NULL_RRA ? NULL : RegionPtr[_region] + Rra##_field; }
  #else
  #define DEFINE_GET_ACCESSOR(_field, _region)\
      inline PTR_UInt8 Get##_field() { return Rra##_field == NULL_RRA ? NULL : RegionPtr[_region] + Rra##_field; }
  #endif
  
      // Similar macro to DEFINE_GET_ACCESSOR that handles data that is read-write normally but read-only if the
      // module is in standalone exe mode.
  #ifdef RHDUMP
  #define DEFINE_GET_ACCESSOR_RO_OR_RW_DATA(_field)\
      inline UInt64 Get##_field() { return Rra##_field == NULL_RRA ? NULL : RegionPtr[(Flags & StandaloneExe) ? RDATA_REGION : DATA_REGION] + Rra##_field; }
  #else
  #define DEFINE_GET_ACCESSOR_RO_OR_RW_DATA(_field)\
      inline PTR_UInt8 Get##_field() { return Rra##_field == NULL_RRA ? NULL : RegionPtr[(Flags & StandaloneExe) ? RDATA_REGION : DATA_REGION] + Rra##_field; }
  #endif
  
      DEFINE_GET_ACCESSOR(SystemObjectEEType,         IAT_REGION);
  
      DEFINE_GET_ACCESSOR(CodeMapInfo,                RDATA_REGION);
      DEFINE_GET_ACCESSOR(StaticsGCInfo,              RDATA_REGION);
      DEFINE_GET_ACCESSOR(ThreadStaticsGCInfo,        RDATA_REGION);
      DEFINE_GET_ACCESSOR(EHInfo,                     RDATA_REGION);
      DEFINE_GET_ACCESSOR(UnwindInfoBlob,             RDATA_REGION);
      DEFINE_GET_ACCESSOR(CallsiteInfoBlob,           RDATA_REGION);
  
      DEFINE_GET_ACCESSOR(StaticsGCDataSection,       DATA_REGION);
  #ifdef FEATURE_CACHED_INTERFACE_DISPATCH
      DEFINE_GET_ACCESSOR(InterfaceDispatchCells,     DATA_REGION);
  #endif
      DEFINE_GET_ACCESSOR(FrozenObjects,             DATA_REGION);
  
      DEFINE_GET_ACCESSOR(LoopIndirCells,             DATA_REGION);
      DEFINE_GET_ACCESSOR(LoopIndirCellChunkBitmap,   DATA_REGION);
      DEFINE_GET_ACCESSOR(LoopRedirTargets,           TEXT_REGION);
      DEFINE_GET_ACCESSOR(LoopTargets,                RDATA_REGION);
  
      DEFINE_GET_ACCESSOR(DispatchMapLookupTable,     RDATA_REGION);
  
  #ifdef FEATURE_PROFILING
      DEFINE_GET_ACCESSOR(ProfilingEntries,           DATA_REGION);
  #endif // FEATURE_PROFILING
  
      DEFINE_GET_ACCESSOR(ReadOnlyBlobs,              RDATA_REGION);
  
      DEFINE_GET_ACCESSOR(NativeInitFunctions,        RDATA_REGION);
  
  #ifdef FEATURE_CUSTOM_IMPORTS
      DEFINE_GET_ACCESSOR(CustomImportDescriptors,    RDATA_REGION);
  #endif // FEATURE_CUSTOM_IMPORTS
  
      DEFINE_GET_ACCESSOR(GenericUnificationDescs,    RDATA_REGION);
      DEFINE_GET_ACCESSOR(GenericUnificationIndirCells,DATA_REGION);
  
      DEFINE_GET_ACCESSOR(ColdToHotMappingInfo,       RDATA_REGION);
  
  #ifndef RHDUMP
      // Macro to generate an inline accessor for well known methods (these are all TEXT-based RRAs since they
      // point to code).
  #define DEFINE_WELL_KNOWN_METHOD(_name)                                 \
      inline PTR_VOID Get_##_name()                                       \
      {                                                                   \
          return WellKnownMethods[WKM_##_name] == NULL_RRA ? NULL : RegionPtr[TEXT_REGION] + WellKnownMethods[WKM_##_name]; \
      }
  #include "WellKnownMethodList.h"
  #undef DEFINE_WELL_KNOWN_METHOD
  #endif // !RHDUMP
  };
  #ifndef RHDUMP
  typedef DPTR(ModuleHeader) PTR_ModuleHeader;
  #endif // !RHDUMP
  
  class GcPollInfo
  {
  public:
  
  #ifndef RHDUMP
      static const UInt32 indirCellsPerBitmapBit  = 64 / POINTER_SIZE;    // one cache line per bit
  #endif // !RHDUMP
  
      static const UInt32 cbChunkCommonCode_X64   = 17;
      static const UInt32 cbChunkCommonCode_X86   = 16;
      static const UInt32 cbChunkCommonCode_ARM   = 32;
  #ifdef _TARGET_ARM_
      // on ARM, the index of the indirection cell can be computed
      // from the pointer to the indirection cell left in R12, 
      // thus we need only one entry point on ARM,
      // thus entries take no space, and you can have as many as you want
      static const UInt32 cbEntry                 = 0;
      static const UInt32 cbBundleCommonCode      = 0;
      static const UInt32 entriesPerBundle        = 0x7fffffff;
      static const UInt32 bundlesPerChunk         = 0x7fffffff;
      static const UInt32 entriesPerChunk         = 0x7fffffff;
  #else
      static const UInt32 cbEntry                 = 4;    // push imm8 / jmp rel8
      static const UInt32 cbBundleCommonCode      = 5;    // jmp rel32
  
      static const UInt32 entriesPerSubBundlePos  = 32;   // for the half with forward jumps
      static const UInt32 entriesPerSubBundleNeg  = 30;   // for the half with negative jumps
      static const UInt32 entriesPerBundle        = entriesPerSubBundlePos + entriesPerSubBundleNeg;
      static const UInt32 bundlesPerChunk         = 4;
      static const UInt32 entriesPerChunk         = bundlesPerChunk * entriesPerBundle;
  #endif
  
      static const UInt32 cbFullBundle            = cbBundleCommonCode + 
                                                    (entriesPerBundle * cbEntry);
  
  #ifndef RHDUMP
      static UInt32 EntryIndexToStubOffset(UInt32 entryIndex)
      {
  # if defined(_TARGET_ARM_)
          return EntryIndexToStubOffset(entryIndex, cbChunkCommonCode_ARM);
  # elif defined(_TARGET_AMD64_)
          return EntryIndexToStubOffset(entryIndex, cbChunkCommonCode_X64);
  # else
          return EntryIndexToStubOffset(entryIndex, cbChunkCommonCode_X86);
  # endif
      }
  #endif
  
      static UInt32 EntryIndexToStubOffset(UInt32 entryIndex, UInt32 cbChunkCommonCode)
      {
  # if defined(_TARGET_ARM_)
          UNREFERENCED_PARAMETER(entryIndex);
          UNREFERENCED_PARAMETER(cbChunkCommonCode);
  
          return 0;
  # else
          UInt32 cbFullChunk              = cbChunkCommonCode + 
                                            (bundlesPerChunk * cbBundleCommonCode) +
                                            (entriesPerChunk * cbEntry);
  
          UInt32 numFullChunks             = entryIndex / entriesPerChunk;
          UInt32 numEntriesInLastChunk     = entryIndex - (numFullChunks * entriesPerChunk);
  
          UInt32 numFullBundles            = numEntriesInLastChunk / entriesPerBundle;
          UInt32 numEntriesInLastBundle    = numEntriesInLastChunk - (numFullBundles * entriesPerBundle);
  
          UInt32 offset                    = (numFullChunks * cbFullChunk) +
                                            cbChunkCommonCode + 
                                            (numFullBundles * cbFullBundle) +
                                            (numEntriesInLastBundle * cbEntry);
  
          if (numEntriesInLastBundle >= entriesPerSubBundlePos)
              offset += cbBundleCommonCode;
  
          return offset;
  # endif
      }
  };
  #endif // !defined(RHDUMP) || defined(RHDUMP_TARGET_NEUTRAL)
  #if       !defined(RHDUMP) || !defined(RHDUMP_TARGET_NEUTRAL)
  
  
  
  struct StaticGcDesc
  {
      struct GCSeries
      {
          UInt32 m_size;
          UInt32 m_startOffset;
      };
  
      UInt32   m_numSeries;
      GCSeries m_series[1];
  
      UInt32 GetSize()
      {
          return (UInt32)(offsetof(StaticGcDesc, m_series) + (m_numSeries * sizeof(GCSeries)));
      }
      
  #ifdef DACCESS_COMPILE
      static UInt32 DacSize(TADDR addr);
  #endif
  };
  
  #ifdef RHDUMP
  typedef StaticGcDesc * PTR_StaticGcDesc;
  typedef StaticGcDesc::GCSeries * PTR_StaticGcDescGCSeries;
  #else
  typedef SPTR(StaticGcDesc) PTR_StaticGcDesc;
  typedef DPTR(StaticGcDesc::GCSeries) PTR_StaticGcDescGCSeries;
  #endif
  
  class EEType;
  
  #ifdef FEATURE_CACHED_INTERFACE_DISPATCH
  
  enum class DispatchCellType
  {
      InterfaceAndSlot = 0x0,
      MetadataToken = 0x1,
      VTableOffset = 0x2,
  };
  
  struct DispatchCellInfo
  {
      DispatchCellType CellType;
      EEType *InterfaceType = nullptr;
      UInt16 InterfaceSlot = 0;
      UInt8 HasCache = 0;
      UInt32 MetadataToken = 0;
      UInt32 VTableOffset = 0;
  };
  
  struct InterfaceDispatchCacheHeader
  {
  private:
      enum Flags
      {
          CH_TypeAndSlotIndex = 0x0,
          CH_MetadataToken = 0x1,
          CH_Mask = 0x3,
          CH_Shift = 0x2,
      };
  
  public:
      void Initialize(EEType *pInterfaceType, UInt16 interfaceSlot, UInt32 metadataToken)
      {
          if (pInterfaceType != nullptr)
          {
              ASSERT(metadataToken == 0);
              m_pInterfaceType = pInterfaceType;
              m_slotIndexOrMetadataTokenEncoded = CH_TypeAndSlotIndex | (((UInt32)interfaceSlot) << CH_Shift);
          }
          else
          {
              ASSERT(pInterfaceType == nullptr);
              ASSERT(interfaceSlot == 0);
              m_pInterfaceType = nullptr;
              m_slotIndexOrMetadataTokenEncoded = CH_MetadataToken | (metadataToken << CH_Shift);
          }
      }
  
      void Initialize(const DispatchCellInfo *pCellInfo)
      {
          ASSERT((pCellInfo->CellType == DispatchCellType::InterfaceAndSlot) ||
                 (pCellInfo->CellType == DispatchCellType::MetadataToken));
          if (pCellInfo->CellType == DispatchCellType::InterfaceAndSlot)
          {
              ASSERT(pCellInfo->MetadataToken == 0);
              Initialize(pCellInfo->InterfaceType, pCellInfo->InterfaceSlot, 0);
          }
          else
          {
              ASSERT(pCellInfo->CellType == DispatchCellType::MetadataToken);
              ASSERT(pCellInfo->InterfaceType == nullptr);
              Initialize(nullptr, 0, pCellInfo->MetadataToken);
          }
      }
  
      DispatchCellInfo GetDispatchCellInfo()
      {
          DispatchCellInfo cellInfo;
          
          if ((m_slotIndexOrMetadataTokenEncoded & CH_Mask) == CH_TypeAndSlotIndex)
          {
              cellInfo.InterfaceType = m_pInterfaceType;
              cellInfo.InterfaceSlot = (UInt16)(m_slotIndexOrMetadataTokenEncoded >> CH_Shift);
              cellInfo.CellType = DispatchCellType::InterfaceAndSlot;
          }
          else
          {
              cellInfo.MetadataToken = m_slotIndexOrMetadataTokenEncoded >> CH_Shift;
              cellInfo.CellType = DispatchCellType::MetadataToken;
          }
          cellInfo.HasCache = 1;
          return cellInfo;
      }
  
  private:
      EEType *    m_pInterfaceType;   // EEType of interface to dispatch on
      UInt32      m_slotIndexOrMetadataTokenEncoded;
  };
  
  // One of these is allocated per interface call site. It holds the stub to call, data to pass to that stub
  // (cache information) and the interface contract, i.e. the interface type and slot being called.
  struct InterfaceDispatchCell
  {
      // The first two fields must remain together and at the beginning of the structure. This is due to the
      // synchronization requirements of the code that updates these at runtime and the instructions generated
      // by the binder for interface call sites.
      UIntTarget      m_pStub;    // Call this code to execute the interface dispatch
      volatile UIntTarget m_pCache;   // Context used by the stub above (one or both of the low two bits are set
                                      // for initial dispatch, and if not set, using this as a cache pointer or 
                                      // as a vtable offset.)
                                      //
                                      // In addition, there is a Slot/Flag use of this field. DispatchCells are
                                      // emitted as a group, and the final one in the group (identified by m_pStub
                                      // having the null value) will have a Slot field is the low 16 bits of the
                                      // m_pCache field, and in the second lowest 16 bits, a Flags field. For the interface
                                      // case Flags shall be 0, and for the metadata token case, Flags shall be 1.
  
      //
      // Keep these in sync with the managed copy in src\Common\src\Internal\Runtime\InterfaceCachePointerType.cs
      //
      enum Flags
      {
          // The low 2 bits of the m_pCache pointer are treated specially so that we can avoid the need for 
          // extra fields on this type.
          // OR if the m_pCache value is less than 0x1000 then this it is a vtable offset and should be used as such
          IDC_CachePointerIsInterfaceRelativePointer = 0x3,
          IDC_CachePointerIsIndirectedInterfaceRelativePointer = 0x2,
          IDC_CachePointerIsInterfacePointerOrMetadataToken = 0x1, // Metadata token is a 30 bit number in this case. 
                                                                   // Tokens are required to have at least one of their upper 20 bits set
                                                                   // But they are not required by this part of the system to follow any specific
                                                                   // token format
          IDC_CachePointerPointsAtCache = 0x0,
          IDC_CachePointerMask = 0x3,
          IDC_CachePointerMaskShift = 0x2,
          IDC_MaxVTableOffsetPlusOne = 0x1000,
      };
  
  #if !defined(RHDUMP) && !defined(BINDER)
      DispatchCellInfo GetDispatchCellInfo()
      {
          // Capture m_pCache into a local for safe access (this is a volatile read of a value that may be
          // modified on another thread while this function is executing.)
          UIntTarget cachePointerValue = m_pCache;
          DispatchCellInfo cellInfo;
  
          if ((cachePointerValue < IDC_MaxVTableOffsetPlusOne) && ((cachePointerValue & IDC_CachePointerMask) == IDC_CachePointerPointsAtCache))
          {
              cellInfo.VTableOffset = (UInt32)cachePointerValue;
              cellInfo.CellType = DispatchCellType::VTableOffset;
              cellInfo.HasCache = 1;
              return cellInfo;
          }
  
          // If there is a real cache pointer, grab the data from there.
          if ((cachePointerValue & IDC_CachePointerMask) == IDC_CachePointerPointsAtCache)
          {
              return ((InterfaceDispatchCacheHeader*)cachePointerValue)->GetDispatchCellInfo();
          }
  
          // Otherwise, walk to cell with Flags and Slot field
  
          // The slot number/flags for a dispatch cell is encoded once per run of DispatchCells
          // The run is terminated by having an dispatch cell with a null stub pointer.
          const InterfaceDispatchCell *currentCell = this;
          while (currentCell->m_pStub != 0)
          {
              currentCell = currentCell + 1;
          } 
          UIntTarget cachePointerValueFlags = currentCell->m_pCache;
  
          DispatchCellType cellType = (DispatchCellType)(cachePointerValueFlags >> 16);
          cellInfo.CellType = cellType;
  
          if (cellType == DispatchCellType::InterfaceAndSlot)
          {
              cellInfo.InterfaceSlot = (UInt16)cachePointerValueFlags;
  
              switch (cachePointerValue & IDC_CachePointerMask)
              {
              case IDC_CachePointerIsInterfacePointerOrMetadataToken:
                  cellInfo.InterfaceType = (EEType*)(cachePointerValue & ~IDC_CachePointerMask);
                  break;
  
              case IDC_CachePointerIsInterfaceRelativePointer:
              case IDC_CachePointerIsIndirectedInterfaceRelativePointer:
                  {
                      UIntTarget interfacePointerValue = (UIntTarget)&m_pCache + (Int32)cachePointerValue;
                      interfacePointerValue &= ~IDC_CachePointerMask;
                      if ((cachePointerValue & IDC_CachePointerMask) == IDC_CachePointerIsInterfaceRelativePointer)
                      {
                          cellInfo.InterfaceType = (EEType*)interfacePointerValue;
                      }
                      else
                      {
                          cellInfo.InterfaceType = *(EEType**)interfacePointerValue;
                      }
                  }
                  break;
              }
          }
          else
          {
              cellInfo.MetadataToken = (UInt32)(cachePointerValue >> IDC_CachePointerMaskShift);
          }
  
          return cellInfo;
      }
  
      static bool IsCache(UIntTarget value)
      {
          if (((value & IDC_CachePointerMask) != 0) || (value < IDC_MaxVTableOffsetPlusOne))
          {
              return false;
          }
          else
          {
              return true;
          }
      }
  
      InterfaceDispatchCacheHeader* GetCache() const
      {
          // Capture m_pCache into a local for safe access (this is a volatile read of a value that may be
          // modified on another thread while this function is executing.)
          UIntTarget cachePointerValue = m_pCache;
          if (IsCache(cachePointerValue))
          {
              return (InterfaceDispatchCacheHeader*)cachePointerValue;
          }
          else
          {
              return 0;
          }
      }
  #endif // !RHDUMP && !BINDER
  };
  
  #endif // FEATURE_CACHED_INTERFACE_DISPATCH
  
  #ifdef _TARGET_ARM_
  // Note for ARM: try and keep the flags in the low 16-bits, since they're not easy to load into a register in
  // a single instruction within our stubs.
  enum PInvokeTransitionFrameFlags
  {
      // NOTE: Keep in sync with ndp\FxCore\CoreRT\src\Native\Runtime\arm\AsmMacros.h
  
      // NOTE: The order in which registers get pushed in the PInvokeTransitionFrame's m_PreservedRegs list has 
      //       to match the order of these flags (that's also the order in which they are read in StackFrameIterator.cpp
  
      // standard preserved registers
      PTFF_SAVE_R4        = 0x00000001,
      PTFF_SAVE_R5        = 0x00000002,
      PTFF_SAVE_R6        = 0x00000004,
      PTFF_SAVE_R7        = 0x00000008,   // should never be used, we require FP frames for methods with 
                                          // pinvoke and it is saved into the frame pointer field instead
      PTFF_SAVE_R8        = 0x00000010,
      PTFF_SAVE_R9        = 0x00000020,
      PTFF_SAVE_R10       = 0x00000040,
      PTFF_SAVE_SP        = 0x00000100,   // Used for 'coop pinvokes' in runtime helper routines.  Methods with
                                          // PInvokes are required to have a frame pointers, but methods which
                                          // call runtime helpers are not.  Therefore, methods that call runtime
                                          // helpers may need SP to seed the stackwalk.
  
      // scratch registers
      PTFF_SAVE_R0        = 0x00000200,
      PTFF_SAVE_R1        = 0x00000400,
      PTFF_SAVE_R2        = 0x00000800,
      PTFF_SAVE_R3        = 0x00001000,
      PTFF_SAVE_LR        = 0x00002000,   // this is useful for the case of loop hijacking where we need both
                                          // a return address pointing into the hijacked method and that method's
                                          // lr register, which may hold a gc pointer
  
      PTFF_R0_IS_GCREF    = 0x00004000,   // used by hijack handler to report return value of hijacked method
      PTFF_R0_IS_BYREF    = 0x00008000,   // used by hijack handler to report return value of hijacked method
  
      PTFF_THREAD_ABORT   = 0x00010000,   // indicates that ThreadAbortException should be thrown when returning from the transition
  };
  #elif defined(_TARGET_ARM64_)
  enum PInvokeTransitionFrameFlags
  {
      // NOTE: Keep in sync with ndp\FxCore\CoreRT\src\Native\Runtime\arm64\AsmMacros.h
  
      // NOTE: The order in which registers get pushed in the PInvokeTransitionFrame's m_PreservedRegs list has 
      //       to match the order of these flags (that's also the order in which they are read in StackFrameIterator.cpp
  
      // standard preserved registers
      PTFF_SAVE_X19       = 0x00000001,
      PTFF_SAVE_X20       = 0x00000002,
      PTFF_SAVE_X21       = 0x00000004,
      PTFF_SAVE_X22       = 0x00000008,
      PTFF_SAVE_X23       = 0x00000010,
      PTFF_SAVE_X24       = 0x00000020,
      PTFF_SAVE_X25       = 0x00000040,
      PTFF_SAVE_X26       = 0x00000080,
      PTFF_SAVE_X27       = 0x00000100,
      PTFF_SAVE_X28       = 0x00000200,
  
      PTFF_SAVE_SP        = 0x00000400,   // Used for 'coop pinvokes' in runtime helper routines.  Methods with
                                          // PInvokes are required to have a frame pointers, but methods which
                                          // call runtime helpers are not.  Therefore, methods that call runtime
                                          // helpers may need SP to seed the stackwalk.
  
      // Scratch registers
      PTFF_SAVE_X0        = 0x00000800,
      PTFF_SAVE_X1        = 0x00001000,
      PTFF_SAVE_X2        = 0x00002000,
      PTFF_SAVE_X3        = 0x00004000,
      PTFF_SAVE_X4        = 0x00008000,
      PTFF_SAVE_X5        = 0x00010000,
      PTFF_SAVE_X6        = 0x00020000,
      PTFF_SAVE_X7        = 0x00040000,
  
      PTFF_SAVE_FP        = 0x00080000,   // should never be used, we require FP frames for methods with 
                                          // pinvoke and it is saved into the frame pointer field instead
  
      PTFF_SAVE_LR        = 0x00100000,   // this is useful for the case of loop hijacking where we need both
                                          // a return address pointing into the hijacked method and that method's
                                          // lr register, which may hold a gc pointer
  
      // Other flags
      PTFF_X0_IS_GCREF    = 0x00200000,   // used by hijack handler to report return value of hijacked method
      PTFF_X0_IS_BYREF    = 0x00400000,   // used by hijack handler to report return value of hijacked method
  
      PTFF_THREAD_ABORT   = 0x00800000,   // indicates that ThreadAbortException should be thrown when returning from the transition
  };
  #else // _TARGET_ARM_
  enum PInvokeTransitionFrameFlags
  {
      // NOTE: Keep in sync with ndp\FxCore\CoreRT\src\Native\Runtime\[amd64|i386]\AsmMacros.inc
  
      // NOTE: The order in which registers get pushed in the PInvokeTransitionFrame's m_PreservedRegs list has 
      //       to match the order of these flags (that's also the order in which they are read in StackFrameIterator.cpp
  
      // standard preserved registers
      PTFF_SAVE_RBX       = 0x00000001,
      PTFF_SAVE_RSI       = 0x00000002,
      PTFF_SAVE_RDI       = 0x00000004,
      PTFF_SAVE_RBP       = 0x00000008,   // should never be used, we require RBP frames for methods with 
                                          // pinvoke and it is saved into the frame pointer field instead
      PTFF_SAVE_R12       = 0x00000010,
      PTFF_SAVE_R13       = 0x00000020,
      PTFF_SAVE_R14       = 0x00000040,
      PTFF_SAVE_R15       = 0x00000080,
  
      PTFF_SAVE_RSP       = 0x00008000,   // Used for 'coop pinvokes' in runtime helper routines.  Methods with
                                          // PInvokes are required to have a frame pointers, but methods which
                                          // call runtime helpers are not.  Therefore, methods that call runtime
                                          // helpers may need RSP to seed the stackwalk.
                                          //
                                          // NOTE: despite the fact that this flag's bit is out of order, it is
                                          // still expected to be saved here after the preserved registers and
                                          // before the scratch registers
      PTFF_SAVE_RAX       = 0x00000100,
      PTFF_SAVE_RCX       = 0x00000200,
      PTFF_SAVE_RDX       = 0x00000400,
      PTFF_SAVE_R8        = 0x00000800,
      PTFF_SAVE_R9        = 0x00001000,
      PTFF_SAVE_R10       = 0x00002000,
      PTFF_SAVE_R11       = 0x00004000,
  
      PTFF_RAX_IS_GCREF   = 0x00010000,   // used by hijack handler to report return value of hijacked method
      PTFF_RAX_IS_BYREF   = 0x00020000,   // used by hijack handler to report return value of hijacked method
  
      PTFF_THREAD_ABORT   = 0x00040000,   // indicates that ThreadAbortException should be thrown when returning from the transition
  };
  #endif // _TARGET_ARM_
  
  #pragma warning(push)
  #pragma warning(disable:4200) // nonstandard extension used: zero-sized array in struct/union
  class Thread;
  #if defined(USE_PORTABLE_HELPERS)
  //the members of this structure are currently unused except m_pThread and exist only to allow compilation
  //of StackFrameIterator their values are not currently being filled in and will require significant rework
  //in order to satisfy the runtime requirements of StackFrameIterator
  struct PInvokeTransitionFrame
  {
      void*       m_RIP;
      Thread*     m_pThread;  // unused by stack crawler, this is so GetThread is only called once per method
                              // can be an invalid pointer in universal transition cases (which never need to call GetThread)
      uint32_t    m_dwFlags;  // PInvokeTransitionFrameFlags
  };
  #else // USE_PORTABLE_HELPERS
  struct PInvokeTransitionFrame
  {
  #ifdef _TARGET_ARM_
      TgtPTR_Void     m_ChainPointer; // R11, used by OS to walk stack quickly
  #endif
  #ifdef _TARGET_ARM64_
      // On arm64, the FP and LR registers are pushed in that order when setting up frames
      TgtPTR_Void     m_FramePointer;
      TgtPTR_Void     m_RIP;
  #else
      TgtPTR_Void     m_RIP;
      TgtPTR_Void     m_FramePointer;
  #endif
      TgtPTR_Thread   m_pThread;  // unused by stack crawler, this is so GetThread is only called once per method
                                  // can be an invalid pointer in universal transition cases (which never need to call GetThread)
      UInt32          m_dwFlags;  // PInvokeTransitionFrameFlags
  #if defined(_TARGET_AMD64_) || defined(_TARGET_ARM64_)
      UInt32          m_dwAlignPad2;
  #endif
      UIntTarget      m_PreservedRegs[];
  };
  #endif // USE_PORTABLE_HELPERS
  #pragma warning(pop)
  
  #ifdef _TARGET_AMD64_
  // RBX, RSI, RDI, R12, R13, R14, R15, RAX, RSP
  #define PInvokeTransitionFrame_SaveRegs_count 9
  #elif defined(_TARGET_X86_)
  // RBX, RSI, RDI, RAX, RSP
  #define PInvokeTransitionFrame_SaveRegs_count 5
  #elif defined(_TARGET_ARM_)
  // R4-R6,R8-R10, R0, SP
  #define PInvokeTransitionFrame_SaveRegs_count 8
  #elif defined(_TARGET_ARM64_)
  // X19-X29, X0, SP
  // ARM64TODO: do we need more here? Verify count when you start using this define.
  #define PInvokeTransitionFrame_SaveRegs_count 12
  #endif
  #define PInvokeTransitionFrame_MAX_SIZE (sizeof(PInvokeTransitionFrame) + (POINTER_SIZE * PInvokeTransitionFrame_SaveRegs_count))
  
  #ifdef _TARGET_AMD64_
  #define OFFSETOF__Thread__m_pTransitionFrame 0x40
  #elif defined(_TARGET_ARM64_)
  #define OFFSETOF__Thread__m_pTransitionFrame 0x40
  #elif defined(_TARGET_X86_)
  #define OFFSETOF__Thread__m_pTransitionFrame 0x2c
  #elif defined(_TARGET_ARM_)
  #define OFFSETOF__Thread__m_pTransitionFrame 0x2c
  #endif
  
  #ifdef RHDUMP
  typedef EEType * PTR_EEType;
  typedef PTR_EEType * PTR_PTR_EEType;
  #else
  typedef DPTR(EEType) PTR_EEType;
  typedef DPTR(PTR_EEType) PTR_PTR_EEType;
  #endif
  
  struct EETypeRef
  {
      union
      {
          EEType *    pEEType;
          EEType **   ppEEType;
          UInt8 *     rawPtr;
          UIntTarget  rawTargetPtr; // x86_amd64: keeps union big enough for target-platform pointer
      };
  
      static const size_t DOUBLE_INDIR_FLAG = 1;
  
      PTR_EEType GetValue()
      {
          if (dac_cast<TADDR>(rawTargetPtr) & DOUBLE_INDIR_FLAG)
              return *dac_cast<PTR_PTR_EEType>(rawTargetPtr - DOUBLE_INDIR_FLAG);
          else
              return dac_cast<PTR_EEType>(rawTargetPtr);
      }
  };
  
  // Generic type parameter variance type (these are allowed only on generic interfaces or delegates). The type
  // values must correspond to those defined in the CLR as CorGenericParamAttr (see corhdr.h).
  enum GenericVarianceType : UInt8
  {
      GVT_NonVariant = 0,
      GVT_Covariant = 1,
      GVT_Contravariant = 2,
      GVT_ArrayCovariant = 0x20,
  };
  
  // Blobs are opaque data passed from the compiler, through the binder and into the native image. At runtime we
  // provide a simple API to retrieve these blobs (they're keyed by a simple integer ID). Blobs are passed to
  // the binder from the compiler and stored in native images by the binder in a sequential stream, each blob
  // having the following header.
  struct BlobHeader
  {
      UInt32 m_flags;  // Flags describing the blob (used by the binder only at the moment)
      UInt32 m_id;     // Unique identifier of the blob (used to access the blob at runtime)
                       // also used by BlobTypeFieldPreInit to identify (at bind time) which field to pre-init.
      UInt32 m_size;   // Size of the individual blob excluding this header (DWORD aligned)
  };
  
  // Structure used in the runtime initialization of deferred static class constructors. Deferred here means
  // executed during normal code execution just prior to a static field on the type being accessed (as opposed
  // to eager cctors, which are run at module load time). This is the fixed portion of the context structure,
  // class libraries can add their own fields to the end.
  struct StaticClassConstructionContext
  {
      // Pointer to the code for the static class constructor method. This is initialized by the
      // binder/runtime.
      TgtPTR_Void m_cctorMethodAddress;
  
      // Initialization state of the class. This is initialized to 0. Every time managed code checks the
      // cctor state the runtime will call the classlibrary's CheckStaticClassConstruction with this context
      // structure unless initialized == 1. This check is specific to allow the classlibrary to store more
      // than a binary state for each cctor if it so desires.
      Int32       m_initialized;
  };
  
  #endif // !defined(RHDUMP) || !defined(RHDUMP_TARGET_NEUTRAL)
  
  #ifdef FEATURE_CUSTOM_IMPORTS
  struct CustomImportDescriptor
  {
      UInt32  RvaEATAddr;  // RVA of the indirection cell of the address of the EAT for that module
      UInt32  RvaIAT;      // RVA of IAT array for that module
      UInt32  CountIAT;    // Count of entries in the above array
  };
  #endif // FEATURE_CUSTOM_IMPORTS
  
  enum RhEHClauseKind
  {
      RH_EH_CLAUSE_TYPED              = 0,
      RH_EH_CLAUSE_FAULT              = 1,
      RH_EH_CLAUSE_FILTER             = 2,
      RH_EH_CLAUSE_UNUSED             = 3
  };
  
  #define RH_EH_CLAUSE_TYPED_INDIRECT RH_EH_CLAUSE_UNUSED 
  
  #ifndef RHDUMP
  // as System::__Canon is not exported by the SharedLibrary.dll, it is represented by a special "pointer" for generic unification
  #ifdef BINDER
  static const UIntTarget CANON_EETYPE = 42;
  #else
  static const EEType * CANON_EETYPE = (EEType *)42;
  #endif
  #endif
  
  #ifndef RHDUMP
  // flags describing what a generic unification descriptor (below) describes or contains
  enum GenericUnificationFlags
  {
      GUF_IS_METHOD       = 0x01,         // GUD represents a method, not a type
      GUF_EETYPE          = 0x02,         // GUD has an indirection cell for the eetype itself
      GUF_DICT            = 0x04,         // GUD has an indirection cell for the dictionary
      GUF_GC_STATICS      = 0x08,         // GUD has 2 indirection cells for the gc statics and their gc desc
      GUF_NONGC_STATICS   = 0x10,         // GUD has an indirection cell for the non gc statics
      GUF_THREAD_STATICS  = 0x20,         // GUD has 3 indirection cells for the tls index, the tls offset and the tls gc desc
      GUF_METHOD_BODIES   = 0x40,         // GUD has indirection cells for method bodies
      GUF_UNBOXING_STUBS  = 0x80,         // GUD has indirection cells for unboxing/instantiating stubs
  };
  
  class GenericComposition;
  
  // describes a generic type or method for the purpose of generic unification
  struct GenericUnificationDesc
  {
      UInt32              m_hashCode;                     // hash code of the type or method
      UInt32              m_flags : 8;                    // GenericUnificationFlags (above)
      UInt32              m_indirCellCountOrOrdinal : 24; // # indir cells used or method ordinal
  #ifdef BINDER
      UIntTarget          m_openType;                     // ref to open type
      UIntTarget          m_genericComposition;           // ref to generic composition
                                                          // (including type args of the enclosing type)
  #else
      EETypeRef           m_openType;                     // ref to open type
      GenericComposition *m_genericComposition;           // ref to generic composition
                                                          // (including type args of the enclosing type)
  #endif // BINDER
  
      inline UInt32 GetIndirCellIndex(GenericUnificationFlags flags)
      {
  #ifdef BINDER
          assert((m_flags & flags) != 0);
  #endif // BINDER
          UInt32 indirCellIndex = 0;
  
          if (flags == GUF_EETYPE)
              return indirCellIndex;
          if (m_flags & GUF_EETYPE)
              indirCellIndex += 1;
  
          if (flags == GUF_DICT)
              return indirCellIndex;
          if (m_flags & GUF_DICT)
              indirCellIndex += 1;
  
          if (flags == GUF_GC_STATICS)
              return indirCellIndex;
          if (m_flags & GUF_GC_STATICS)
              indirCellIndex += 2;
  
          if (flags == GUF_NONGC_STATICS)
              return indirCellIndex;
          if (m_flags & GUF_NONGC_STATICS)
              indirCellIndex += 1;
  
          if (flags == GUF_THREAD_STATICS)
              return indirCellIndex;
          if (m_flags & GUF_THREAD_STATICS)
              indirCellIndex += 3;
  
          if (flags == GUF_METHOD_BODIES)
              return indirCellIndex;
  
  #ifdef BINDER
          // not legal to have unboxing stubs without method bodies
          assert((m_flags & (GUF_METHOD_BODIES| GUF_UNBOXING_STUBS)) == (GUF_METHOD_BODIES | GUF_UNBOXING_STUBS));
  #endif // BINDER
          if (flags & GUF_UNBOXING_STUBS)
          {
              // the remainining indirection cells should be for method bodies and instantiating/unboxing stubs
              // where each method has an associated instantiating/unboxing stub
              UInt32 remainingIndirCellCount = m_indirCellCountOrOrdinal - indirCellIndex;
              // thus the number of remaining indirection cells should be divisible by 2
              assert(remainingIndirCellCount % 2 == 0);
              // the method bodies come first, followed by the unboxing stubs
              return indirCellIndex + remainingIndirCellCount/2;
          }
  
  #ifdef BINDER
          assert(!"bad GUF flag parameter");
  #endif // BINDER
          return indirCellIndex;
      }
  
  #ifdef BINDER
      inline void SetIndirCellCount(UInt32 indirCellCount)
      {
          // generic unification descs for methods always have 1 indirection cell
          assert(!(m_flags & GUF_IS_METHOD));
          m_indirCellCountOrOrdinal = indirCellCount;
          assert(m_indirCellCountOrOrdinal == indirCellCount);
      }
  
      inline void SetOrdinal(UInt32 ordinal)
      {
          assert(m_flags & GUF_IS_METHOD);
          m_indirCellCountOrOrdinal = ordinal;
          assert(m_indirCellCountOrOrdinal == ordinal);
      }
  #endif // !BINDER
  
      inline UInt32 GetIndirCellCount()
      {
          // generic unification descs for methods always have 1 indirection cell
          return (m_flags & GUF_IS_METHOD) ? 1 : m_indirCellCountOrOrdinal;
      }
  
      inline UInt32 GetOrdinal()
      {
          // For methods, we need additional identification, for types, we don't
          // However, we need to make sure no type can match a method, so for
          // types we return a value that would be never legal for a method
          return (m_flags & GUF_IS_METHOD) ? m_indirCellCountOrOrdinal : ~0;
      }
  
      bool Equals(GenericUnificationDesc *that);
  };
  
  
  // mapping of cold code blocks to the corresponding hot entry point RVA
  // format is a as follows:
  // -------------------
  // | subSectionCount |     # of subsections, where each subsection has a run of hot bodies
  // -------------------     followed by a run of cold bodies
  // | hotMethodCount  |     # of hot bodies in subsection
  // | coldMethodCount |     # of cold bodies in subsection
  // -------------------
  // ... possibly repeated on ARM
  // -------------------
  // | hotRVA #1       |     RVA of the hot entry point corresponding to the 1st cold body
  // | hotRVA #2       |     RVA of the hot entry point corresponding to the 2nd cold body
  // ... one entry for each cold body containing the corresponding hot entry point
  
  // number of hot and cold bodies in a subsection of code
  // in x86 and x64 there's only one subsection, on ARM there may be several
  // for large modules with > 16 MB of code
  struct SubSectionDesc
  {
      UInt32          hotMethodCount;
      UInt32          coldMethodCount;
  };
  
  // this is the structure describing the cold to hot mapping info
  struct ColdToHotMapping
  {
      UInt32          subSectionCount;
      SubSectionDesc  subSection[/*subSectionCount*/1];
      //  UINT32   hotRVAofColdMethod[/*coldMethodCount*/];
  };
  #endif
  