diff --git a/src/coreclr/inc/dacvars.h b/src/coreclr/inc/dacvars.h index 68e24e965ed57b..f6cfe11e376d65 100644 --- a/src/coreclr/inc/dacvars.h +++ b/src/coreclr/inc/dacvars.h @@ -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) diff --git a/src/coreclr/vm/amd64/cgenamd64.cpp b/src/coreclr/vm/amd64/cgenamd64.cpp index 221234925988ea..2a087b6459275c 100644 --- a/src/coreclr/vm/amd64/cgenamd64.cpp +++ b/src/coreclr/vm/amd64/cgenamd64.cpp @@ -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] && @@ -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 @@ -403,23 +422,55 @@ void emitBackToBackJump(LPBYTE pBufferRX, LPBYTE pBufferRW, LPVOID target) MODE_ANY; PRECONDITION(CheckPointer(pBufferRX)); + PRECONDITION(CheckPointer(pBufferRW)); } CONTRACTL_END; - // mov rax, 123456789abcdef0h 48 b8 xx xx xx xx xx xx xx xx - // jmp rax ff e0 - - pBufferRW[0] = 0x48; - pBufferRW[1] = 0xB8; + if (IsJmpAbsAvailable()) + { + // jmpabs (11 bytes) + nop padding = 12 bytes + emitJmpAbsJump(pBufferRX, pBufferRW, target); + pBufferRW[11] = 0x90; // nop padding + } + else + { + // Fallback: mov rax, imm64; jmp rax (12 bytes) + pBufferRW[0] = 0x48; + pBufferRW[1] = 0xB8; - SET_UNALIGNED_64(&pBufferRW[2], target); + SET_UNALIGNED_64(&pBufferRW[2], target); - pBufferRW[10] = 0xFF; - pBufferRW[11] = 0xE0; + pBufferRW[10] = 0xFF; + pBufferRW[11] = 0xE0; + } _ASSERTE(DbgIsExecutable(pBufferRX, 12)); } +void emitJmpAbsJump(LPBYTE pBufferRX, LPBYTE pBufferRW, LPVOID target) +{ + CONTRACTL + { + THROWS; + GC_NOTRIGGER; + MODE_ANY; + + PRECONDITION(CheckPointer(pBufferRX)); + PRECONDITION(CheckPointer(pBufferRW)); + PRECONDITION(IsJmpAbsAvailable()); // Caller must check APX availability + } + CONTRACTL_END; + + // jmpabs instruction (APX): D5 00 A1 xx xx xx xx xx xx xx xx + pBufferRW[0] = 0xD5; + pBufferRW[1] = 0x00; + pBufferRW[2] = 0xA1; + + SET_UNALIGNED_64(&pBufferRW[3], target); + + _ASSERTE(DbgIsExecutable(pBufferRX, 11)); +} + INT32 rel32UsingJumpStub(INT32 UNALIGNED * pRel32, PCODE target, MethodDesc *pMethod, LoaderAllocator *pLoaderAllocator /* = NULL */, bool throwOnOutOfMemoryWithinRange /*= true*/) { diff --git a/src/coreclr/vm/amd64/cgencpu.h b/src/coreclr/vm/amd64/cgencpu.h index f0c29085a74417..a7c166c1e464ec 100644 --- a/src/coreclr/vm/amd64/cgencpu.h +++ b/src/coreclr/vm/amd64/cgencpu.h @@ -484,12 +484,25 @@ inline TADDR GetSecondArgReg(CONTEXT *context) extern "C" void* GetCurrentSP(); +// 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, LoaderAllocator *pLoaderAllocator = NULL, bool throwOnOutOfMemoryWithinRange = true); 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); diff --git a/src/coreclr/vm/amd64/virtualcallstubcpu.hpp b/src/coreclr/vm/amd64/virtualcallstubcpu.hpp index 01d6a21e01a47a..6e270b3933a08e 100644 --- a/src/coreclr/vm/amd64/virtualcallstubcpu.hpp +++ b/src/coreclr/vm/amd64/virtualcallstubcpu.hpp @@ -124,78 +124,163 @@ a DispatchStubLong which is bigger but is a full 64-bit jump. */ /*DispatchStubShort********************************************************************************* This is the logical continuation of DispatchStub for the case when the failure target is within -a rel32 jump (DISPL). */ +a rel32 jump (DISPL). Uses a union to handle both legacy and APX encodings. */ struct DispatchStubShort { friend struct DispatchHolder; friend struct DispatchStub; static BOOL isShortStub(LPCBYTE pCode); - inline PCODE implTarget() const { LIMITED_METHOD_CONTRACT; return (PCODE) _implTarget; } + + inline PCODE implTarget() const + { + LIMITED_METHOD_CONTRACT; + return IsJmpAbsAvailable() ? (PCODE)_apx._implTarget : (PCODE)_legacy._implTarget; + } inline TADDR implTargetSlot() const { LIMITED_METHOD_CONTRACT; - return (TADDR)&_implTarget; + return IsJmpAbsAvailable() ? (TADDR)&_apx._implTarget : (TADDR)&_legacy._implTarget; } - inline PCODE failTarget() const { LIMITED_METHOD_CONTRACT; return (PCODE) &_failDispl + sizeof(DISPL) + _failDispl; } + inline PCODE failTarget() const + { + LIMITED_METHOD_CONTRACT; + if (IsJmpAbsAvailable()) + { + // APX: displacement is relative to the next instruction, the jmpabs + return (PCODE)&_apx.jmpabsPrefix + _apx._failDispl; + } + else + { + // Legacy: displacement base after the jne instruction + return (PCODE)&_legacy._failDispl + sizeof(DISPL) + _legacy._failDispl; + } + } private: - BYTE part1 [2]; // 48 B8 mov rax, - size_t _implTarget; // xx xx xx xx xx xx xx xx 64-bit address - BYTE part2[2]; // 0f 85 jne - DISPL _failDispl; // xx xx xx xx failEntry ;must be forward jmp for perf reasons - BYTE part3 [2]; // FF E0 jmp rax + // _implTarget is backpatched atomically, so it must be 8-byte aligned. + union + { + struct + { + BYTE nop; // 90 nop ; aligns _implTarget + BYTE part1[2]; // 48 B8 mov rax, + size_t _implTarget; // xx xx xx xx xx xx xx xx 64-bit address at +3 + BYTE part2[2]; // 0F 85 jne + DISPL _failDispl; // xx xx xx xx failEntry + BYTE part3[2]; // FF E0 jmp rax + } _legacy; + + struct + { + BYTE nop[2]; // 66 90 nop ; aligns _implTarget + BYTE part1[2]; // 0F 85 jne near + DISPL _failDispl; // xx xx xx xx 4-byte displacement + BYTE jmpabsPrefix[3]; // D5 00 A1 jmpabs + size_t _implTarget; // xx xx xx xx xx xx xx xx 64-bit address at +11 + } _apx; + + }; }; -#define DispatchStubShort_offsetof_failDisplBase (offsetof(DispatchStubLong, _failDispl) + sizeof(DISPL)) +#define DispatchStubShort_offsetof_failDisplBase (offsetof(DispatchStubShort, _legacy._failDispl) + sizeof(DISPL)) inline BOOL DispatchStubShort::isShortStub(LPCBYTE pCode) { - LIMITED_METHOD_CONTRACT; - return reinterpret_cast(pCode)->part2[0] == 0x0f; + LIMITED_METHOD_DAC_CONTRACT; + + if (IsJmpAbsAvailable()) + { + // APX short opens with nop2 (66 90), long with jne near (0F 85). Test byte 1, not byte 0: + // byte 0 is an instruction boundary and a breakpoint would overwrite it with int3. + return pCode[1] == 0x90; + } + + // Legacy: both open with nop; mov rax, imm64; the jne that follows differs (0F 85 vs 75). + return reinterpret_cast(pCode)->_legacy.part2[0] == 0x0F; } /*DispatchStubLong********************************************************************************** This is the logical continuation of DispatchStub for the case when the failure target is not -reachable by a rel32 jump (DISPL). */ +reachable by a rel32 jump (DISPL). Uses a union to handle both legacy and APX encodings. */ struct DispatchStubLong { friend struct DispatchHolder; friend struct DispatchStub; static inline BOOL isLongStub(LPCBYTE pCode); - inline PCODE implTarget() const { LIMITED_METHOD_CONTRACT; return (PCODE) _implTarget; } + inline PCODE implTarget() const + { + LIMITED_METHOD_CONTRACT; + return IsJmpAbsAvailable() ? (PCODE)_apx._implTarget : (PCODE)_legacy._implTarget; + } inline TADDR implTargetSlot() const { LIMITED_METHOD_CONTRACT; - return (TADDR)&_implTarget; + return IsJmpAbsAvailable() ? (TADDR)&_apx._implTarget : (TADDR)&_legacy._implTarget; } - inline PCODE failTarget() const { LIMITED_METHOD_CONTRACT; return (PCODE) _failTarget; } + inline PCODE failTarget() const + { + LIMITED_METHOD_CONTRACT; + return IsJmpAbsAvailable() ? (PCODE)_apx._failTarget : (PCODE)_legacy._failTarget; + } private: - BYTE part1[2]; // 48 B8 mov rax, - size_t _implTarget; // xx xx xx xx xx xx xx xx 64-bit address - BYTE part2 [1]; // 75 jne - BYTE _failDispl; // xx failLabel - BYTE part3 [2]; // FF E0 jmp rax - // failLabel: - BYTE part4 [2]; // 48 B8 mov rax, - size_t _failTarget; // xx xx xx xx xx xx xx xx 64-bit address - BYTE part5 [2]; // FF E0 jmp rax + // Padding aligns _implTarget as in DispatchStubShort. + union + { + struct + { + BYTE nop; // 90 nop ; aligns _implTarget + BYTE part1[2]; // 48 B8 mov rax, + size_t _implTarget; // xx xx xx xx xx xx xx xx 64-bit address at +3 + BYTE part2 [1]; // 75 jne + BYTE _failDispl; // xx failLabel + BYTE part3 [2]; // FF E0 jmp rax + // failLabel: + BYTE part4 [2]; // 48 B8 mov rax, + size_t _failTarget; // xx xx xx xx xx xx xx xx 64-bit address + BYTE part5 [2]; // FF E0 jmp rax + BYTE pad[8]; // CC ... unreachable padding + } _legacy; + + struct + { + BYTE part1[2]; // 0F 85 jne near + DISPL _failDispl; // xx xx xx xx failLabel + BYTE nop[2]; // 66 90 nop ; aligns _implTarget + BYTE jmpabsPrefix[3]; // D5 00 A1 jmpabs + size_t _implTarget; // xx xx xx xx xx xx xx xx 64-bit address at +11 + // failLabel: + BYTE failJmpabsPrefix[3]; // D5 00 A1 jmpabs + size_t _failTarget; // xx xx xx xx xx xx xx xx 64-bit address + BYTE pad[5]; // CC ... unreachable padding + } _apx; + + }; }; -#define DispatchStubLong_offsetof_failDisplBase (offsetof(DispatchStubLong, _failDispl) + sizeof(BYTE)) -#define DispatchStubLong_offsetof_failLabel (offsetof(DispatchStubLong, part4[0])) +#define DispatchStubLong_offsetof_failDisplBase (offsetof(DispatchStubLong, _legacy._failDispl) + sizeof(BYTE)) +#define DispatchStubLong_offsetof_failLabel (offsetof(DispatchStubLong, _legacy.part4[0])) + +#define DispatchStubLong_offsetof_apx_failDisplBase (offsetof(DispatchStubLong, _apx.nop)) +#define DispatchStubLong_offsetof_apx_failLabel (offsetof(DispatchStubLong, _apx.failJmpabsPrefix)) inline BOOL DispatchStubLong::isLongStub(LPCBYTE pCode) { - LIMITED_METHOD_CONTRACT; - return reinterpret_cast(pCode)->part2[0] == 0x75; + LIMITED_METHOD_DAC_CONTRACT; + + if (IsJmpAbsAvailable()) + { + return pCode[1] == 0x85; + } + + return reinterpret_cast(pCode)->_legacy.part2[0] == 0x75; } /*DispatchStub************************************************************************************** @@ -284,7 +369,6 @@ struct DispatchStub BYTE _entryPoint [2]; // 48 B8 mov rax, size_t _expectedMT; // xx xx xx xx xx xx xx xx 64-bit address BYTE part1 [3]; // 48 39 XX cmp [THIS_REG], rax - BYTE nopOp; // 90 nop ; 1-byte nop to align _implTarget // Followed by either DispatchStubShort or DispatchStubLong, depending // on whether we were able to make a rel32 or had to make an abs64 jump @@ -324,9 +408,21 @@ struct DispatchHolder static BOOL CanShortJumpDispatchStubReachFailTarget(PCODE failTarget, LPCBYTE stubMemory) { STATIC_CONTRACT_WRAPPER; - LPCBYTE pFrom = stubMemory + sizeof(DispatchStub) + DispatchStubShort_offsetof_failDisplBase; - size_t cbRelJump = failTarget - (PCODE)pFrom; - return FitsInI4(cbRelJump); + + if (IsJmpAbsAvailable()) + { + // APX + LPCBYTE pFrom = stubMemory + sizeof(DispatchStub) + offsetof(DispatchStubShort, _apx.jmpabsPrefix); + size_t cbRelJump = failTarget - (PCODE)pFrom; + return FitsInI4(cbRelJump); + } + else + { + // Legacy encoding + LPCBYTE pFrom = stubMemory + sizeof(DispatchStub) + DispatchStubShort_offsetof_failDisplBase; + size_t cbRelJump = failTarget - (PCODE)pFrom; + return FitsInI4(cbRelJump); + } } DispatchStub* stub() { LIMITED_METHOD_CONTRACT; return reinterpret_cast(this); } @@ -590,13 +686,19 @@ void LookupHolder::Initialize(LookupHolder* pLookupHolderRX, PCODE resolveWorke void DispatchHolder::InitializeStatic() { // Check that _implTarget is aligned in the DispatchStub for backpatching - static_assert(((sizeof(DispatchStub) + offsetof(DispatchStubShort, _implTarget)) % sizeof(void *)) == 0); - static_assert(((sizeof(DispatchStub) + offsetof(DispatchStubLong, _implTarget)) % sizeof(void *)) == 0); + static_assert(((sizeof(DispatchStub) + offsetof(DispatchStubShort, _legacy._implTarget)) % sizeof(void *)) == 0); + static_assert(((sizeof(DispatchStub) + offsetof(DispatchStubShort, _apx._implTarget)) % sizeof(void *)) == 0); + static_assert(((sizeof(DispatchStub) + offsetof(DispatchStubLong, _legacy._implTarget)) % sizeof(void *)) == 0); + static_assert(((sizeof(DispatchStub) + offsetof(DispatchStubLong, _apx._implTarget)) % sizeof(void *)) == 0); static_assert(((sizeof(DispatchStub) + sizeof(DispatchStubShort)) % sizeof(void*)) == 0); static_assert(((sizeof(DispatchStub) + sizeof(DispatchStubLong)) % sizeof(void*)) == 0); static_assert((DispatchStubLong_offsetof_failLabel - DispatchStubLong_offsetof_failDisplBase) < INT8_MAX); + // Each stub must fit its CODE_SIZE_ALIGN slot (16-byte granularity). + static_assert((sizeof(DispatchStub) + sizeof(DispatchStubShort)) == 32); + static_assert((sizeof(DispatchStub) + sizeof(DispatchStubLong)) == 48); + // Common dispatch stub initialization dispatchInit._entryPoint [0] = 0x48; dispatchInit._entryPoint [1] = 0xB8; @@ -604,32 +706,65 @@ void DispatchHolder::InitializeStatic() dispatchInit.part1 [0] = X64_INSTR_CMP_IND_THIS_REG_RAX & 0xff; dispatchInit.part1 [1] = (X64_INSTR_CMP_IND_THIS_REG_RAX >> 8) & 0xff; dispatchInit.part1 [2] = (X64_INSTR_CMP_IND_THIS_REG_RAX >> 16) & 0xff; - dispatchInit.nopOp = 0x90; - - // Short dispatch stub initialization - dispatchShortInit.part1 [0] = 0x48; - dispatchShortInit.part1 [1] = 0xb8; - dispatchShortInit._implTarget = 0xcccccccccccccccc; - dispatchShortInit.part2 [0] = 0x0F; - dispatchShortInit.part2 [1] = 0x85; - dispatchShortInit._failDispl = 0xcccccccc; - dispatchShortInit.part3 [0] = 0xFF; - dispatchShortInit.part3 [1] = 0xE0; - - // Long dispatch stub initialization - dispatchLongInit.part1 [0] = 0x48; - dispatchLongInit.part1 [1] = 0xb8; - dispatchLongInit._implTarget = 0xcccccccccccccccc; - dispatchLongInit.part2 [0] = 0x75; - dispatchLongInit._failDispl = BYTE(DispatchStubLong_offsetof_failLabel - DispatchStubLong_offsetof_failDisplBase); - dispatchLongInit.part3 [0] = 0xFF; - dispatchLongInit.part3 [1] = 0xE0; - // failLabel: - dispatchLongInit.part4 [0] = 0x48; - dispatchLongInit.part4 [1] = 0xb8; - dispatchLongInit._failTarget = 0xcccccccccccccccc; - dispatchLongInit.part5 [0] = 0xFF; - dispatchLongInit.part5 [1] = 0xE0; + + if (IsJmpAbsAvailable()) + { + // Short dispatch stub initialization (APX encoding) + dispatchShortInit._apx.nop [0] = 0x66; + dispatchShortInit._apx.nop [1] = INSTR_NOP; + dispatchShortInit._apx.part1 [0] = 0x0F; + dispatchShortInit._apx.part1 [1] = 0x85; + dispatchShortInit._apx._failDispl = 0xcccccccc; + dispatchShortInit._apx.jmpabsPrefix [0] = 0xD5; + dispatchShortInit._apx.jmpabsPrefix [1] = 0x00; + dispatchShortInit._apx.jmpabsPrefix [2] = 0xA1; + dispatchShortInit._apx._implTarget = 0xcccccccccccccccc; + + // Long dispatch stub initialization (APX encoding) + dispatchLongInit._apx.part1 [0] = 0x0F; + dispatchLongInit._apx.part1 [1] = 0x85; + dispatchLongInit._apx._failDispl = (DISPL)(DispatchStubLong_offsetof_apx_failLabel - DispatchStubLong_offsetof_apx_failDisplBase); + dispatchLongInit._apx.nop [0] = 0x66; + dispatchLongInit._apx.nop [1] = INSTR_NOP; + dispatchLongInit._apx.jmpabsPrefix [0] = 0xD5; + dispatchLongInit._apx.jmpabsPrefix [1] = 0x00; + dispatchLongInit._apx.jmpabsPrefix [2] = 0xA1; + dispatchLongInit._apx._implTarget = 0xcccccccccccccccc; + dispatchLongInit._apx.failJmpabsPrefix [0] = 0xD5; + dispatchLongInit._apx.failJmpabsPrefix [1] = 0x00; + dispatchLongInit._apx.failJmpabsPrefix [2] = 0xA1; + dispatchLongInit._apx._failTarget = 0xcccccccccccccccc; + memset(dispatchLongInit._apx.pad, INSTR_INT3, sizeof(dispatchLongInit._apx.pad)); + } + else + { + // Short dispatch stub initialization (legacy encoding) + dispatchShortInit._legacy.nop = INSTR_NOP; + dispatchShortInit._legacy.part1 [0] = 0x48; + dispatchShortInit._legacy.part1 [1] = 0xb8; + dispatchShortInit._legacy._implTarget = 0xcccccccccccccccc; + dispatchShortInit._legacy.part2 [0] = 0x0F; + dispatchShortInit._legacy.part2 [1] = 0x85; + dispatchShortInit._legacy._failDispl = 0xcccccccc; + dispatchShortInit._legacy.part3 [0] = 0xFF; + dispatchShortInit._legacy.part3 [1] = 0xE0; + + // Long dispatch stub initialization (legacy encoding) + dispatchLongInit._legacy.nop = INSTR_NOP; + dispatchLongInit._legacy.part1 [0] = 0x48; + dispatchLongInit._legacy.part1 [1] = 0xb8; + dispatchLongInit._legacy._implTarget = 0xcccccccccccccccc; + dispatchLongInit._legacy.part2 [0] = 0x75; + dispatchLongInit._legacy._failDispl = BYTE(DispatchStubLong_offsetof_failLabel - DispatchStubLong_offsetof_failDisplBase); + dispatchLongInit._legacy.part3 [0] = 0xFF; + dispatchLongInit._legacy.part3 [1] = 0xE0; + dispatchLongInit._legacy.part4 [0] = 0x48; + dispatchLongInit._legacy.part4 [1] = 0xb8; + dispatchLongInit._legacy._failTarget = 0xcccccccccccccccc; + dispatchLongInit._legacy.part5 [0] = 0xFF; + dispatchLongInit._legacy.part5 [1] = 0xE0; + memset(dispatchLongInit._legacy.pad, INSTR_INT3, sizeof(dispatchLongInit._legacy.pad)); + } }; void DispatchHolder::Initialize(DispatchHolder* pDispatchHolderRX, PCODE implTarget, PCODE failTarget, size_t expectedMT, @@ -657,11 +792,22 @@ void DispatchHolder::Initialize(DispatchHolder* pDispatchHolderRX, PCODE implTa *shortStubRW = dispatchShortInit; // fill in the dynamic data - size_t displ = (failTarget - ((PCODE) &shortStubRX->_failDispl + sizeof(DISPL))); - CONSISTENCY_CHECK(FitsInI4(displ)); - shortStubRW->_failDispl = (DISPL) displ; - shortStubRW->_implTarget = (size_t) implTarget; - CONSISTENCY_CHECK((PCODE)&shortStubRX->_failDispl + sizeof(DISPL) + shortStubRX->_failDispl == failTarget); + if (IsJmpAbsAvailable()) + { + size_t displ = failTarget - (PCODE)&shortStubRX->_apx.jmpabsPrefix; + CONSISTENCY_CHECK(FitsInI4(displ)); + shortStubRW->_apx._failDispl = (DISPL)displ; + shortStubRW->_apx._implTarget = (size_t)implTarget; + CONSISTENCY_CHECK((PCODE)&shortStubRX->_apx.jmpabsPrefix + shortStubRW->_apx._failDispl == failTarget); + } + else + { + size_t displ = failTarget - ((PCODE)&shortStubRX->_legacy._failDispl + sizeof(DISPL)); + CONSISTENCY_CHECK(FitsInI4(displ)); + shortStubRW->_legacy._failDispl = (DISPL)displ; + shortStubRW->_legacy._implTarget = (size_t)implTarget; + CONSISTENCY_CHECK((PCODE)&shortStubRX->_legacy._failDispl + sizeof(DISPL) + shortStubRW->_legacy._failDispl == failTarget); + } } else { @@ -672,8 +818,16 @@ void DispatchHolder::Initialize(DispatchHolder* pDispatchHolderRX, PCODE implTa *longStub = dispatchLongInit; // fill in the dynamic data - longStub->_implTarget = implTarget; - longStub->_failTarget = failTarget; + if (IsJmpAbsAvailable()) + { + longStub->_apx._implTarget = implTarget; + longStub->_apx._failTarget = failTarget; + } + else + { + longStub->_legacy._implTarget = implTarget; + longStub->_legacy._failTarget = failTarget; + } } } diff --git a/src/coreclr/vm/ceemain.cpp b/src/coreclr/vm/ceemain.cpp index d01a24bb0c4e72..e33fd4356e5523 100644 --- a/src/coreclr/vm/ceemain.cpp +++ b/src/coreclr/vm/ceemain.cpp @@ -857,6 +857,8 @@ void EEStartupHelper() g_pEEShutDownEvent = new CLREvent(); g_pEEShutDownEvent->CreateManualEvent(FALSE); + ExecutionManager::Init(); + VirtualCallStubManager::InitStatic(); // Setup the domains. Threads are started in a default domain. @@ -866,8 +868,6 @@ void EEStartupHelper() COMDelegate::Init(); - ExecutionManager::Init(); - #ifdef FEATURE_PERFMAP PerfMap::SignalDependenciesReady(); #endif diff --git a/src/coreclr/vm/codeman.cpp b/src/coreclr/vm/codeman.cpp index c115a131e49832..2607100cb5d34d 100644 --- a/src/coreclr/vm/codeman.cpp +++ b/src/coreclr/vm/codeman.cpp @@ -1938,6 +1938,10 @@ void EEJitManager::SetCpuInfo() #endif // TARGET_X86 || TARGET_AMD64 m_CPUCompileFlags = CPUCompileFlags; + +#ifdef TARGET_AMD64 + g_isJmpAbsAvailable = CPUCompileFlags.GetInstructionSetFlags().HasInstructionSet(InstructionSet_APX); +#endif // TARGET_AMD64 } // Define some data that we can use to get a better idea of what happened when we get a Watson dump that indicates the JIT failed to load. diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/SOSDacImpl.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/SOSDacImpl.cs index 0083167031b474..941329d009946a 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/SOSDacImpl.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy/SOSDacImpl.cs @@ -2280,17 +2280,28 @@ int ISOSDacInterface.GetJitManagerList(uint count, DacpJitManagerInfo* managers, return hr; } - private bool IsJumpRel64(TargetPointer pThunk) + private bool IsJumpAbs64(TargetPointer pThunk) + => 0xD5 == _target.Read(pThunk) && + 0x00 == _target.Read(pThunk + 1) && + 0xA1 == _target.Read(pThunk + 2); + + private bool IsJumpMovRax64(TargetPointer pThunk) => 0x48 == _target.Read(pThunk) && 0xB8 == _target.Read(pThunk + 1) && 0xFF == _target.Read(pThunk + 10) && 0xE0 == _target.Read(pThunk + 11); + private bool IsJumpRel64(TargetPointer pThunk) + => IsJumpAbs64(pThunk) || IsJumpMovRax64(pThunk); + private TargetPointer DecodeJump64(TargetPointer pThunk) { Debug.Assert(IsJumpRel64(pThunk), "Expected a jump thunk"); - return _target.ReadPointer(pThunk + 2); + // The immediate follows the 3-byte jmpabs opcode or the 2-byte mov rax opcode. + return IsJumpAbs64(pThunk) + ? _target.ReadPointer(pThunk + 3) + : _target.ReadPointer(pThunk + 2); } int ISOSDacInterface.GetJumpThunkTarget(void* ctx, ClrDataAddress* targetIP, ClrDataAddress* targetMD)