Content Supported by Sourcelens Consulting
// 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.
// precode.h
//
//
// Stub that runs before the actual native code
#ifndef __PRECODE_H__
#define __PRECODE_H__
typedef DPTR(class Precode) PTR_Precode;
#ifndef PRECODE_ALIGNMENT
#define PRECODE_ALIGNMENT sizeof(void*)
#endif
enum PrecodeType {
PRECODE_INVALID = InvalidPrecode::Type,
PRECODE_STUB = StubPrecode::Type,
#ifdef HAS_NDIRECT_IMPORT_PRECODE
PRECODE_NDIRECT_IMPORT = NDirectImportPrecode::Type,
#endif // HAS_NDIRECT_IMPORT_PRECODE
#ifdef HAS_REMOTING_PRECODE
PRECODE_REMOTING = RemotingPrecode::Type,
#endif // HAS_REMOTING_PRECODE
#ifdef HAS_FIXUP_PRECODE
PRECODE_FIXUP = FixupPrecode::Type,
#endif // HAS_FIXUP_PRECODE
#ifdef HAS_THISPTR_RETBUF_PRECODE
PRECODE_THISPTR_RETBUF = ThisPtrRetBufPrecode::Type,
#endif // HAS_THISPTR_RETBUF_PRECODE
};
// For more details see. file:../../doc/BookOfTheRuntime/ClassLoader/MethodDescDesign.doc
class Precode {
#ifdef DACCESS_COMPILE
friend class NativeImageDumper;
#endif
BYTE m_data[SIZEOF_PRECODE_BASE];
StubPrecode* AsStubPrecode()
{
LIMITED_METHOD_CONTRACT;
SUPPORTS_DAC;
return dac_cast<PTR_StubPrecode>(this);
}
#ifdef HAS_NDIRECT_IMPORT_PRECODE
public:
// Fake precodes has to be exposed
NDirectImportPrecode* AsNDirectImportPrecode()
{
LIMITED_METHOD_CONTRACT;
SUPPORTS_DAC;
return dac_cast<PTR_NDirectImportPrecode>(this);
}
private:
#endif // HAS_NDIRECT_IMPORT_PRECODE
#ifdef HAS_REMOTING_PRECODE
RemotingPrecode* AsRemotingPrecode()
{
LIMITED_METHOD_CONTRACT;
SUPPORTS_DAC;
return dac_cast<PTR_RemotingPrecode>(this);
}
#endif // HAS_REMOTING_PRECODE
#ifdef HAS_FIXUP_PRECODE
FixupPrecode* AsFixupPrecode()
{
LIMITED_METHOD_CONTRACT;
SUPPORTS_DAC;
return dac_cast<PTR_FixupPrecode>(this);
}
#endif // HAS_FIXUP_PRECODE
#ifdef HAS_THISPTR_RETBUF_PRECODE
ThisPtrRetBufPrecode* AsThisPtrRetBufPrecode()
{
LIMITED_METHOD_CONTRACT;
SUPPORTS_DAC;
return dac_cast<PTR_ThisPtrRetBufPrecode>(this);
}
#endif // HAS_THISPTR_RETBUF_PRECODE
TADDR GetStart()
{
SUPPORTS_DAC;
LIMITED_METHOD_CONTRACT;
return dac_cast<TADDR>(this);
}
static void UnexpectedPrecodeType(const char * originator, PrecodeType precodeType)
{
SUPPORTS_DAC;
#ifdef DACCESS_COMPILE
DacError(E_UNEXPECTED);
#else
#ifdef _PREFIX_
// We only use __UNREACHABLE here since otherwise it would be a hint
// for the compiler to fold this case with the other cases in a switch
// statement. However, we would rather have this case be a separate
// code path so that we will get a clean crash sooner.
__UNREACHABLE("Unexpected precode type");
#endif
CONSISTENCY_CHECK_MSGF(false, ("%s: Unexpected precode type: 0x%02x.", originator, precodeType));
#endif
}
public:
PrecodeType GetType()
{
LIMITED_METHOD_CONTRACT;
SUPPORTS_DAC;
#ifdef OFFSETOF_PRECODE_TYPE
BYTE type = m_data[OFFSETOF_PRECODE_TYPE];
#ifdef _TARGET_X86_
if (type == X86_INSTR_MOV_RM_R)
type = m_data[OFFSETOF_PRECODE_TYPE_MOV_RM_R];
#endif // _TARGET_X86_
#ifdef _TARGET_AMD64_
if (type == (X86_INSTR_MOV_R10_IMM64 & 0xFF))
type = m_data[OFFSETOF_PRECODE_TYPE_MOV_R10];
else if ((type == (X86_INSTR_CALL_REL32 & 0xFF)) || (type == (X86_INSTR_JMP_REL32 & 0xFF)))
type = m_data[OFFSETOF_PRECODE_TYPE_CALL_OR_JMP];
#endif // _AMD64
#if defined(HAS_FIXUP_PRECODE) && (defined(_TARGET_X86_) || defined(_TARGET_AMD64_))
if (type == FixupPrecode::TypePrestub)
type = FixupPrecode::Type;
#endif
#ifdef _TARGET_ARM_
static_assert_no_msg(offsetof(StubPrecode, m_pTarget) == offsetof(NDirectImportPrecode, m_pMethodDesc));
// If the precode does not have thumb bit on target, it must be NDirectImportPrecode.
if (type == StubPrecode::Type && ((AsStubPrecode()->m_pTarget & THUMB_CODE) == 0))
type = NDirectImportPrecode::Type;
#endif
return (PrecodeType)type;
#else // OFFSETOF_PRECODE_TYPE
return PRECODE_STUB;
#endif // OFFSETOF_PRECODE_TYPE
}
static BOOL IsValidType(PrecodeType t);
static int AlignOf(PrecodeType t)
{
SUPPORTS_DAC;
int align = PRECODE_ALIGNMENT;
#if defined(_TARGET_X86_) && defined(HAS_FIXUP_PRECODE)
// Fixup precodes has to be aligned to allow atomic patching
if (t == PRECODE_FIXUP)
align = 8;
#endif // _TARGET_X86_ && HAS_FIXUP_PRECODE
#if defined(_TARGET_ARM_) && defined(HAS_COMPACT_ENTRYPOINTS)
// Precodes have to be aligned to allow fast compact entry points check
_ASSERTE (align >= sizeof(void*));
#endif // _TARGET_ARM_ && HAS_COMPACT_ENTRYPOINTS
return align;
}
static SIZE_T SizeOf(PrecodeType t);
SIZE_T SizeOf()
{
WRAPPER_NO_CONTRACT;
return SizeOf(GetType());
}
// Note: This is immediate target of the precode. It does not follow jump stub if there is one.
PCODE GetTarget();
BOOL IsPointingTo(PCODE target, PCODE addr)
{
WRAPPER_NO_CONTRACT;
SUPPORTS_DAC;
#ifdef CROSSGEN_COMPILE
// Crossgen does not create jump stubs on AMD64, so just return always false here to
// avoid non-deterministic behavior.
return FALSE;
#else // CROSSGEN_COMPILE
if (target == addr)
return TRUE;
#ifdef _TARGET_AMD64_
// Handle jump stubs
if (isJumpRel64(target)) {
target = decodeJump64(target);
if (target == addr)
return TRUE;
}
#endif // _TARGET_AMD64_
return FALSE;
#endif // CROSSGEN_COMPILE
}
BOOL IsPointingToNativeCode(PCODE pNativeCode)
{
WRAPPER_NO_CONTRACT;
SUPPORTS_DAC;
#ifdef HAS_REMOTING_PRECODE
// Remoting precode is special case
if (GetType() == PRECODE_REMOTING)
return FALSE;
#endif
return IsPointingTo(GetTarget(), pNativeCode);
}
BOOL IsPointingToPrestub(PCODE target);
BOOL IsPointingToPrestub()
{
WRAPPER_NO_CONTRACT;
return IsPointingToPrestub(GetTarget());
}
PCODE GetEntryPoint()
{
LIMITED_METHOD_CONTRACT;
return dac_cast<TADDR>(this) + GetEntryPointOffset();
}
static SIZE_T GetEntryPointOffset()
{
LIMITED_METHOD_CONTRACT;
#ifdef _TARGET_ARM_
return THUMB_CODE;
#else
return 0;
#endif
}
MethodDesc * GetMethodDesc(BOOL fSpeculative = FALSE);
BOOL IsCorrectMethodDesc(MethodDesc * pMD);
static Precode* Allocate(PrecodeType t, MethodDesc* pMD,
LoaderAllocator *pLoaderAllocator, AllocMemTracker *pamTracker);
void Init(PrecodeType t, MethodDesc* pMD, LoaderAllocator *pLoaderAllocator);
#ifndef DACCESS_COMPILE
void ResetTargetInterlocked();
BOOL SetTargetInterlocked(PCODE target, BOOL fOnlyRedirectFromPrestub = TRUE);
// Reset precode to point to prestub
void Reset();
#endif // DACCESS_COMPILE
static Precode* GetPrecodeFromEntryPoint(PCODE addr, BOOL fSpeculative = FALSE)
{
LIMITED_METHOD_DAC_CONTRACT;
#ifdef DACCESS_COMPILE
// Always use speculative checks with DAC
fSpeculative = TRUE;
#endif
TADDR pInstr = PCODEToPINSTR(addr);
// Always do consistency check in debug
if (fSpeculative INDEBUG(|| TRUE))
{
if (!IS_ALIGNED(pInstr, PRECODE_ALIGNMENT) || !IsValidType(PTR_Precode(pInstr)->GetType()))
{
if (fSpeculative) return NULL;
_ASSERTE(!"Precode::GetPrecodeFromEntryPoint: Unexpected code in precode");
}
}
Precode* pPrecode = PTR_Precode(pInstr);
if (!fSpeculative)
{
g_IBCLogger.LogMethodPrecodeAccess(pPrecode->GetMethodDesc());
}
return pPrecode;
}
// If addr is patched fixup precode, returns address that it points to. Otherwise returns NULL.
static PCODE TryToSkipFixupPrecode(PCODE addr);
//
// Precode as temporary entrypoint
//
static SIZE_T SizeOfTemporaryEntryPoint(PrecodeType t)
{
LIMITED_METHOD_DAC_CONTRACT;
#ifdef HAS_FIXUP_PRECODE_CHUNKS
_ASSERTE(t != PRECODE_FIXUP);
#endif
return ALIGN_UP(SizeOf(t), AlignOf(t));
}
static Precode * GetPrecodeForTemporaryEntryPoint(TADDR temporaryEntryPoints, int index);
static SIZE_T SizeOfTemporaryEntryPoints(PrecodeType t, bool preallocateJumpStubs, int count);
static SIZE_T SizeOfTemporaryEntryPoints(TADDR temporaryEntryPoints, int count);
static TADDR AllocateTemporaryEntryPoints(MethodDescChunk* pChunk,
LoaderAllocator *pLoaderAllocator, AllocMemTracker *pamTracker);
#ifdef FEATURE_PREJIT
//
// NGEN stuff
//
void Save(DataImage *image);
void Fixup(DataImage *image, MethodDesc * pMD);
BOOL IsPrebound(DataImage *image);
// Helper class for saving precodes in chunks
class SaveChunk
{
#ifdef HAS_FIXUP_PRECODE_CHUNKS
// Array of methods to be saved in the method desc chunk
InlineSArray<MethodDesc *, 20> m_rgPendingChunk;
#endif // HAS_FIXUP_PRECODE_CHUNKS
public:
void Save(DataImage * image, MethodDesc * pMD);
void Flush(DataImage * image);
};
#endif // FEATURE_PREJIT
#ifdef DACCESS_COMPILE
void EnumMemoryRegions(CLRDataEnumMemoryFlags flags);
#endif
#ifdef HAS_FIXUP_PRECODE_CHUNKS
static DWORD GetOffsetOfBase(PrecodeType t, DWORD count)
{
assert(t == PRECODE_FIXUP);
return (DWORD)(count * sizeof(FixupPrecode));
}
static DWORD GetOffset(PrecodeType t, DWORD index, DWORD count)
{
assert(t == PRECODE_FIXUP);
assert(index < count);
return (DWORD)((count - index - 1)* sizeof(FixupPrecode));
}
#endif
};
#endif // __PRECODE_H__