Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/coreclr/inc/dacvars.h
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,10 @@ DEFINE_DACVAR(INT32, ArrayBase__s_arrayBoundsZero, ArrayBase::s_arrayBoundsZero)
#ifdef FEATURE_CODE_VERSIONING
DEFINE_DACVAR(BOOL, CodeVersionManager__s_HasNonDefaultILVersions, CodeVersionManager::s_HasNonDefaultILVersions)
#endif // FEATURE_CODE_VERSIONING
#ifdef TARGET_AMD64
// Selects the encoding used by the AMD64 runtime-generated stubs (APX jmpabs vs. mov rax/jmp rax).
DEFINE_DACVAR(bool, dac__g_isJmpAbsAvailable, ::g_isJmpAbsAvailable)
#endif // TARGET_AMD64

DEFINE_DACVAR(PTR_JITNotification, dac__g_pNotificationTable, ::g_pNotificationTable)
DEFINE_DACVAR(ULONG32, dac__g_dacNotificationFlags, ::g_dacNotificationFlags)
Expand Down
83 changes: 50 additions & 33 deletions src/coreclr/vm/amd64/cgenamd64.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -349,11 +349,23 @@ void HijackFrame::UpdateRegDisplay_Impl(const PREGDISPLAY pRD, bool updateFloats
}
#endif // FEATURE_HIJACK

// Selects the APX jmpabs stub encoding; set by EEJitManager::SetCpuInfo() when APX is available.
GVAL_IMPL_INIT(bool, g_isJmpAbsAvailable, false);

bool isBackToBackJump(PCODE pCode)
{
LIMITED_METHOD_CONTRACT;
PTR_BYTE pbCode = PTR_BYTE(pCode);

// Check for jmpabs encoding (APX)
if (0xD5 == pbCode[0] &&
0x00 == pbCode[1] &&
0xA1 == pbCode[2])
{
return true;
}

// Check for legacy encoding: mov rax, imm64; jmp rax
return 0x48 == pbCode[0] &&
0xB8 == pbCode[1] &&
0xFF == pbCode[10] &&
Expand All @@ -364,11 +376,18 @@ PCODE decodeBackToBackJump(PCODE pBuffer)
{
LIMITED_METHOD_CONTRACT;

// mov rax, xxx
// jmp rax
_ASSERTE(isBackToBackJump(pBuffer));

return *PTR_UINT64(pBuffer+2);
PTR_BYTE pbCode = PTR_BYTE(pBuffer);

// jmpabs encoding (APX): D5 00 A1 [8 bytes at offset 3-10]
if (0x00 == pbCode[1])
{
return *PTR_UINT64(pBuffer + 3);
}

// Legacy encoding: 48 B8 [8 bytes at offset 2-9] FF E0
return *PTR_UINT64(pBuffer + 2);
}

#ifdef DACCESS_COMPILE
Expand All @@ -394,41 +413,41 @@ BOOL GetAnyThunkTarget (CONTEXT *pctx, TADDR *pTarget, TADDR *pTargetMethodDesc)

#ifndef DACCESS_COMPILE

void EncodeLoadAndJumpThunk (LPBYTE pBuffer, LPVOID pv, LPVOID pTarget)
void emitBackToBackJump(LPBYTE pBufferRX, LPBYTE pBufferRW, LPVOID target)
{
CONTRACTL
{
THROWS;
GC_NOTRIGGER;
MODE_ANY;

PRECONDITION(CheckPointer(pBuffer));
PRECONDITION(CheckPointer(pBufferRX));
PRECONDITION(CheckPointer(pBufferRW));
}
Comment thread
Copilot marked this conversation as resolved.
CONTRACTL_END;

// mov r10, pv 49 ba xx xx xx xx xx xx xx xx

pBuffer[0] = 0x49;
pBuffer[1] = 0xBA;

SET_UNALIGNED_64(&pBuffer[2], pv);

// mov rax, pTarget 48 b8 xx xx xx xx xx xx xx xx

pBuffer[10] = 0x48;
pBuffer[11] = 0xB8;

SET_UNALIGNED_64(&pBuffer[12], pTarget);
if (IsJmpAbsAvailable())
{
// jmpabs (11 bytes) + nop padding = 12 bytes
emitJmpAbsJump(pBufferRX, pBufferRW, target);
pBufferRW[11] = 0x90; // nop padding
}
Comment thread
DeepakRajendrakumaran marked this conversation as resolved.
else
{
// Fallback: mov rax, imm64; jmp rax (12 bytes)
pBufferRW[0] = 0x48;
pBufferRW[1] = 0xB8;

// jmp rax ff e0
SET_UNALIGNED_64(&pBufferRW[2], target);

pBuffer[20] = 0xFF;
pBuffer[21] = 0xE0;
pBufferRW[10] = 0xFF;
pBufferRW[11] = 0xE0;
}

_ASSERTE(DbgIsExecutable(pBuffer, 22));
_ASSERTE(DbgIsExecutable(pBufferRX, 12));
}

void emitBackToBackJump(LPBYTE pBufferRX, LPBYTE pBufferRW, LPVOID target)
void emitJmpAbsJump(LPBYTE pBufferRX, LPBYTE pBufferRW, LPVOID target)
{
CONTRACTL
{
Expand All @@ -437,21 +456,19 @@ void emitBackToBackJump(LPBYTE pBufferRX, LPBYTE pBufferRW, LPVOID target)
MODE_ANY;

PRECONDITION(CheckPointer(pBufferRX));
PRECONDITION(CheckPointer(pBufferRW));
PRECONDITION(IsJmpAbsAvailable()); // Caller must check APX availability
}
CONTRACTL_END;

// mov rax, 123456789abcdef0h 48 b8 xx xx xx xx xx xx xx xx
// jmp rax ff e0
// jmpabs instruction (APX): D5 00 A1 xx xx xx xx xx xx xx xx
pBufferRW[0] = 0xD5;
pBufferRW[1] = 0x00;
pBufferRW[2] = 0xA1;

pBufferRW[0] = 0x48;
pBufferRW[1] = 0xB8;
SET_UNALIGNED_64(&pBufferRW[3], target);

SET_UNALIGNED_64(&pBufferRW[2], target);

pBufferRW[10] = 0xFF;
pBufferRW[11] = 0xE0;

_ASSERTE(DbgIsExecutable(pBufferRX, 12));
_ASSERTE(DbgIsExecutable(pBufferRX, 11));
}

INT32 rel32UsingJumpStub(INT32 UNALIGNED * pRel32, PCODE target, MethodDesc *pMethod,
Expand Down
16 changes: 11 additions & 5 deletions src/coreclr/vm/amd64/cgencpu.h
Original file line number Diff line number Diff line change
Expand Up @@ -486,12 +486,14 @@ inline TADDR GetSecondArgReg(CONTEXT *context)

extern "C" void* GetCurrentSP();

// Emits:
// mov r10, pv1
// mov rax, pTarget
// jmp rax
void EncodeLoadAndJumpThunk (LPBYTE pBuffer, LPVOID pv, LPVOID pTarget);
// true when the APX jmpabs instruction is available; set by EEJitManager::SetCpuInfo() at startup. A DAC global.
GVAL_DECL(bool, g_isJmpAbsAvailable);

inline bool IsJmpAbsAvailable()
{
LIMITED_METHOD_DAC_CONTRACT;
return g_isJmpAbsAvailable;
}

// Get Rel32 destination, emit jumpStub if necessary
INT32 rel32UsingJumpStub(INT32 UNALIGNED * pRel32, PCODE target, MethodDesc *pMethod,
Expand All @@ -502,6 +504,10 @@ INT32 rel32UsingPreallocatedJumpStub(INT32 UNALIGNED * pRel32, PCODE target, PCO

void emitBackToBackJump(LPBYTE pBufferRX, LPBYTE pBufferRW, LPVOID target);

// Emits raw 11-byte jmpabs instruction (D5 00 A1 + 8-byte immediate)
// Caller must ensure IsJmpAbsAvailable() == true.
void emitJmpAbsJump(LPBYTE pBufferRX, LPBYTE pBufferRW, LPVOID target);

bool isBackToBackJump(PCODE pCode);
PCODE decodeBackToBackJump(PCODE pCode);

Expand Down
Loading
Loading