| //------------------------------------------------------------------------------------------------------- |
| // Copyright (C) Microsoft Corporation and contributors. All rights reserved. |
| // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. |
| //------------------------------------------------------------------------------------------------------- |
| #include "Backend.h" |
| #include "ExternalHelperMethod.h" |
| // Parser includes |
| #include "RegexCommon.h" |
| |
| #include "Library/RegexHelper.h" |
| |
| #ifdef ENABLE_SCRIPT_DEBUGGING |
| #include "Debug/DiagHelperMethodWrapper.h" |
| #endif |
| #include "Math/CrtSSE2Math.h" |
| #include "Library/JavascriptGeneratorFunction.h" |
| #include "RuntimeMathPch.h" |
| |
| namespace IR |
| { |
| |
| intptr_t const JnHelperMethodAddresses[] = |
| { |
| #define HELPERCALL(Name, Address, Attributes) reinterpret_cast<intptr_t>(Address), |
| // Because of order-of-initialization problems with the vtable address static field |
| // and this array, we're going to have to fill these in as we go along. |
| #include "JnHelperMethodList.h" |
| NULL |
| }; |
| |
| intptr_t const *GetHelperMethods() |
| { |
| return JnHelperMethodAddresses; |
| } |
| |
| #if ENABLE_DEBUG_CONFIG_OPTIONS && defined(_CONTROL_FLOW_GUARD) |
| class HelperTableCheck |
| { |
| public: |
| HelperTableCheck() { |
| CheckJnHelperTable(JnHelperMethodAddresses); |
| } |
| }; |
| |
| // Dummy global to trigger CheckJnHelperTable call at load time. |
| static HelperTableCheck LoadTimeHelperTableCheck; |
| |
| void CheckJnHelperTable(intptr_t const* table) |
| { |
| MEMORY_BASIC_INFORMATION memBuffer; |
| |
| // Make sure the helper table is in read-only memory for security reasons. |
| SIZE_T byteCount; |
| byteCount = VirtualQuery(table, &memBuffer, sizeof(memBuffer)); |
| |
| Assert(byteCount); |
| |
| // Note: .rdata is merged with .text on x86. |
| if (memBuffer.Protect != PAGE_READONLY && memBuffer.Protect != PAGE_EXECUTE_READ) |
| { |
| AssertMsg(false, "JnHelperMethodAddress table needs to be read-only for security reasons"); |
| |
| Fatal(); |
| } |
| } |
| #endif |
| |
| #ifdef ENABLE_SCRIPT_DEBUGGING |
| static intptr_t const helperMethodWrappers[] = { |
| reinterpret_cast<intptr_t>(&Js::HelperMethodWrapper0), |
| reinterpret_cast<intptr_t>(&Js::HelperMethodWrapper1), |
| reinterpret_cast<intptr_t>(&Js::HelperMethodWrapper2), |
| reinterpret_cast<intptr_t>(&Js::HelperMethodWrapper3), |
| reinterpret_cast<intptr_t>(&Js::HelperMethodWrapper4), |
| reinterpret_cast<intptr_t>(&Js::HelperMethodWrapper5), |
| reinterpret_cast<intptr_t>(&Js::HelperMethodWrapper6), |
| reinterpret_cast<intptr_t>(&Js::HelperMethodWrapper7), |
| reinterpret_cast<intptr_t>(&Js::HelperMethodWrapper8), |
| reinterpret_cast<intptr_t>(&Js::HelperMethodWrapper9), |
| reinterpret_cast<intptr_t>(&Js::HelperMethodWrapper10), |
| reinterpret_cast<intptr_t>(&Js::HelperMethodWrapper11), |
| reinterpret_cast<intptr_t>(&Js::HelperMethodWrapper12), |
| reinterpret_cast<intptr_t>(&Js::HelperMethodWrapper13), |
| reinterpret_cast<intptr_t>(&Js::HelperMethodWrapper14), |
| reinterpret_cast<intptr_t>(&Js::HelperMethodWrapper15), |
| reinterpret_cast<intptr_t>(&Js::HelperMethodWrapper16), |
| }; |
| #endif |
| |
| ///---------------------------------------------------------------------------- |
| /// |
| /// GetMethodAddress |
| /// |
| /// returns the memory address of the helperMethod, |
| /// which can the address of debugger wrapper that intercept the original helper. |
| /// |
| ///---------------------------------------------------------------------------- |
| intptr_t |
| GetMethodAddress(ThreadContextInfo * context, IR::HelperCallOpnd* opnd) |
| { |
| Assert(opnd); |
| #ifdef ENABLE_SCRIPT_DEBUGGING |
| #if defined(_M_ARM32_OR_ARM64) |
| #define LowererMDFinal LowererMD |
| #else |
| #define LowererMDFinal LowererMDArch |
| #endif |
| |
| CompileAssert(_countof(helperMethodWrappers) == LowererMDFinal::MaxArgumentsToHelper + 1); |
| |
| if (opnd->IsDiagHelperCallOpnd()) |
| { |
| // Note: all arguments are already loaded for the original helper. Here we just return the address. |
| IR::DiagHelperCallOpnd* diagOpnd = (IR::DiagHelperCallOpnd*)opnd; |
| |
| if (0 <= diagOpnd->m_argCount && diagOpnd->m_argCount <= LowererMDFinal::MaxArgumentsToHelper) |
| { |
| return ShiftAddr(context, helperMethodWrappers[diagOpnd->m_argCount]); |
| } |
| else |
| { |
| AssertMsg(FALSE, "Unsupported arg count (need to implement)."); |
| } |
| } |
| #endif |
| return GetMethodOriginalAddress(context, opnd->m_fnHelper); |
| } |
| |
| // TODO: Remove this define once makes it into WINNT.h |
| #ifndef DECLSPEC_GUARDIGNORE |
| #if (_MSC_FULL_VER >= 170065501) && !defined(__clang__) |
| #define DECLSPEC_GUARDIGNORE __declspec(guard(ignore)) |
| #else |
| #define DECLSPEC_GUARDIGNORE |
| #endif |
| #endif |
| |
| // We need the helper table to be in read-only memory for obvious security reasons. |
| // Import function ptr require dynamic initialization, and cause the table to be in read-write memory. |
| // Additionally, all function ptrs are automatically marked as safe CFG addresses by the compiler. |
| // __declspec(guard(ignore)) can be used on methods to have the compiler not mark these as valid CFG targets. |
| DECLSPEC_GUARDIGNORE _NOINLINE intptr_t GetNonTableMethodAddress(ThreadContextInfo * context, JnHelperMethod helperMethod) |
| { |
| switch (helperMethod) |
| { |
| // |
| // DllImport methods |
| // |
| #if defined(_M_IX86) |
| // These are internal CRT functions which don't use a standard calling convention |
| case HelperDirectMath_Acos: |
| return ShiftAddr(context, __libm_sse2_acos); |
| |
| case HelperDirectMath_Asin: |
| return ShiftAddr(context, __libm_sse2_asin); |
| |
| case HelperDirectMath_Atan: |
| return ShiftAddr(context, __libm_sse2_atan); |
| |
| case HelperDirectMath_Atan2: |
| return ShiftAddr(context, __libm_sse2_atan2); |
| |
| case HelperDirectMath_Cos: |
| return ShiftAddr(context, __libm_sse2_cos); |
| |
| case HelperDirectMath_Exp: |
| return ShiftAddr(context, __libm_sse2_exp); |
| |
| case HelperDirectMath_Log: |
| return ShiftAddr(context, __libm_sse2_log); |
| |
| case HelperDirectMath_Sin: |
| return ShiftAddr(context, __libm_sse2_sin); |
| |
| case HelperDirectMath_Tan: |
| return ShiftAddr(context, __libm_sse2_tan); |
| |
| case HelperAtomicStore64: |
| return ShiftAddr(context, (double(*)(double))InterlockedExchange64); |
| |
| case HelperMemoryBarrier: |
| #ifdef _M_HYBRID_X86_ARM64 |
| AssertOrFailFastMsg(false, "The usage below fails to build for CHPE, and HelperMemoryBarrier is only required " |
| "for WASM threads, which are currently disabled"); |
| return 0; |
| #else |
| return ShiftAddr(context, (void(*)())MemoryBarrier); |
| #endif // !_M_HYBRID_X86_ARM64 |
| #endif |
| |
| case HelperDirectMath_FloorDb: |
| return ShiftStdcallAddr(context, Js::JavascriptMath::Floor); |
| |
| case HelperDirectMath_CeilDb: |
| return ShiftStdcallAddr(context, Js::JavascriptMath::Ceil); |
| |
| case HelperDirectMath_FloorFlt: |
| return ShiftStdcallAddr(context, Js::JavascriptMath::FloorF); |
| |
| case HelperDirectMath_CeilFlt: |
| return ShiftStdcallAddr(context, Js::JavascriptMath::CeilF); |
| |
| // |
| // These are statically initialized to an import thunk, but let's keep them out of the table in case a new CRT changes this |
| // |
| case HelperWMemCmp: |
| return ShiftCdeclAddr(context, wmemcmp); |
| |
| case HelperMemCpy: |
| return ShiftCdeclAddr(context, (void *(__cdecl *)(void *, void const*, size_t))memcpy); |
| |
| #if defined(_M_X64) || defined(_M_ARM32_OR_ARM64) |
| case HelperDirectMath_Acos: |
| return ShiftCdeclAddr(context, (double(__cdecl *)(double))acos); |
| |
| case HelperDirectMath_Asin: |
| return ShiftCdeclAddr(context, (double(__cdecl *)(double))asin); |
| |
| case HelperDirectMath_Atan: |
| return ShiftCdeclAddr(context, (double(__cdecl *)(double))atan); |
| |
| case HelperDirectMath_Atan2: |
| return ShiftCdeclAddr(context, (double(__cdecl *)(double, double))atan2); |
| |
| case HelperDirectMath_Cos: |
| return ShiftCdeclAddr(context, (double(__cdecl *)(double))cos); |
| |
| case HelperDirectMath_Exp: |
| return ShiftCdeclAddr(context, (double(__cdecl *)(double))exp); |
| |
| case HelperDirectMath_Log: |
| return ShiftCdeclAddr(context, (double(__cdecl *)(double))log); |
| |
| case HelperDirectMath_Sin: |
| return ShiftCdeclAddr(context, (double(__cdecl *)(double))sin); |
| |
| case HelperDirectMath_Tan: |
| return ShiftCdeclAddr(context, (double(__cdecl *)(double))tan); |
| #endif |
| |
| // |
| // Methods that we don't want to get marked as CFG targets as they make unprotected calls |
| // |
| |
| #ifdef _CONTROL_FLOW_GUARD |
| case HelperGuardCheckCall: |
| return (intptr_t)__guard_check_icall_fptr; // OOP JIT: ntdll load at same address across all process |
| #endif |
| |
| case HelperOp_TryCatch: |
| return ShiftStdcallAddr(context, Js::JavascriptExceptionOperators::OP_TryCatch); |
| |
| case HelperOp_TryFinally: |
| return ShiftStdcallAddr(context, Js::JavascriptExceptionOperators::OP_TryFinally); |
| |
| |
| case HelperOp_TryFinallyNoOpt: |
| return ShiftStdcallAddr(context, Js::JavascriptExceptionOperators::OP_TryFinallyNoOpt); |
| |
| // |
| // Methods that we don't want to get marked as CFG targets as they dump all registers to a controlled address |
| // |
| case HelperSaveAllRegistersAndBailOut: |
| return ShiftStdcallAddr(context, LinearScanMD::SaveAllRegistersAndBailOut); |
| case HelperSaveAllRegistersAndBranchBailOut: |
| return ShiftStdcallAddr(context, LinearScanMD::SaveAllRegistersAndBranchBailOut); |
| |
| #ifdef _M_IX86 |
| case HelperSaveAllRegistersNoSse2AndBailOut: |
| return ShiftStdcallAddr(context, LinearScanMD::SaveAllRegistersNoSse2AndBailOut); |
| case HelperSaveAllRegistersNoSse2AndBranchBailOut: |
| return ShiftStdcallAddr(context, LinearScanMD::SaveAllRegistersNoSse2AndBranchBailOut); |
| #endif |
| |
| } |
| |
| Assume(UNREACHED); |
| |
| return 0; |
| } |
| |
| ///---------------------------------------------------------------------------- |
| /// |
| /// GetMethodOriginalAddress |
| /// |
| /// returns the memory address of the helperMethod, |
| /// this one is never the intercepted by debugger helper. |
| /// |
| ///---------------------------------------------------------------------------- |
| intptr_t GetMethodOriginalAddress(ThreadContextInfo * context, JnHelperMethod helperMethod) |
| { |
| AssertOrFailFast(helperMethod >= 0 && helperMethod < IR::JnHelperMethodCount); |
| intptr_t address = GetHelperMethods()[static_cast<WORD>(helperMethod)]; |
| if (address == 0) |
| { |
| return GetNonTableMethodAddress(context, helperMethod); |
| } |
| |
| return ShiftAddr(context, address); |
| } |
| |
| #if DBG_DUMP || defined(ENABLE_IR_VIEWER) |
| |
| |
| |
| char16 const * const JnHelperMethodNames[] = |
| { |
| #define HELPERCALL(Name, Address, Attributes) _u("") STRINGIZEW(Name) _u(""), |
| #include "JnHelperMethodList.h" |
| NULL |
| }; |
| |
| ///---------------------------------------------------------------------------- |
| /// |
| /// GetMethodName |
| /// |
| /// returns the string representing the name of the helperMethod. |
| /// |
| ///---------------------------------------------------------------------------- |
| |
| char16 const* |
| GetMethodName(JnHelperMethod helperMethod) |
| { |
| return JnHelperMethodNames[static_cast<WORD>(helperMethod)]; |
| } |
| |
| #endif //#if DBG_DUMP |
| |
| |
| } //namespace IR |
| |
| #if DBG_DUMP || defined(ENABLE_IR_VIEWER) |
| const char16 *GetVtableName(VTableValue value) |
| { |
| switch (value) |
| { |
| #if !defined(_M_X64) |
| case VtableJavascriptNumber: |
| return _u("vtable JavascriptNumber"); |
| break; |
| #endif |
| case VtableDynamicObject: |
| return _u("vtable DynamicObject"); |
| break; |
| case VtableInvalid: |
| return _u("vtable Invalid"); |
| break; |
| case VtablePropertyString: |
| return _u("vtable PropertyString"); |
| break; |
| case VtableJavascriptBoolean: |
| return _u("vtable JavascriptBoolean"); |
| break; |
| case VtableJavascriptArray: |
| return _u("vtable JavascriptArray"); |
| break; |
| case VtableInt8Array: |
| return _u("vtable Int8Array"); |
| break; |
| case VtableUint8Array: |
| return _u("vtable Uint8Array"); |
| break; |
| case VtableUint8ClampedArray: |
| return _u("vtable Uint8ClampedArray"); |
| break; |
| case VtableInt16Array: |
| return _u("vtable Int16Array"); |
| break; |
| case VtableUint16Array: |
| return _u("vtable Uint16Array"); |
| break; |
| case VtableInt32Array: |
| return _u("vtable Int32Array"); |
| break; |
| case VtableUint32Array: |
| return _u("vtable Uint32Array"); |
| break; |
| case VtableFloat32Array: |
| return _u("vtable Float32Array"); |
| break; |
| case VtableFloat64Array: |
| return _u("vtable Float64Array"); |
| break; |
| case VtableJavascriptPixelArray: |
| return _u("vtable JavascriptPixelArray"); |
| break; |
| case VtableInt64Array: |
| return _u("vtable Int64Array"); |
| break; |
| case VtableUint64Array: |
| return _u("vtable Uint64Array"); |
| break; |
| case VtableInt8VirtualArray: |
| return _u("vtable Int8VirtualArray"); |
| break; |
| case VtableUint8VirtualArray: |
| return _u("vtable Uint8VirtualArray"); |
| break; |
| case VtableUint8ClampedVirtualArray: |
| return _u("vtable Uint8ClampedVirtualArray"); |
| break; |
| case VtableInt16VirtualArray: |
| return _u("vtable Int16VirtualArray"); |
| break; |
| case VtableUint16VirtualArray: |
| return _u("vtable Uint16VirtualArray"); |
| break; |
| case VtableInt32VirtualArray: |
| return _u("vtable Int32VirtualArray"); |
| break; |
| case VtableUint32VirtualArray: |
| return _u("vtable Uint32VirtualArray"); |
| break; |
| case VtableFloat32VirtualArray: |
| return _u("vtable Float32VirtualArray"); |
| break; |
| case VtableFloat64VirtualArray: |
| return _u("vtable Float64VirtualArray"); |
| break; |
| case VtableBoolArray: |
| return _u("vtable BoolArray"); |
| break; |
| case VtableCharArray: |
| return _u("vtable CharArray"); |
| break; |
| case VtableNativeIntArray: |
| return _u("vtable NativeIntArray"); |
| break; |
| case VtableNativeFloatArray: |
| return _u("vtable NativeFloatArray"); |
| break; |
| case VtableJavascriptNativeIntArray: |
| return _u("vtable JavascriptNativeIntArray"); |
| break; |
| case VtableJavascriptRegExp: |
| return _u("vtable JavascriptRegExp"); |
| break; |
| case VtableStackScriptFunction: |
| return _u("vtable StackScriptFunction"); |
| break; |
| case VtableScriptFunctionWithInlineCacheAndHomeObj: |
| return _u("vtable ScriptFunctionWithInlineCacheAndHomeObj"); |
| break; |
| case VtableScriptFunctionWithInlineCacheHomeObjAndComputedName: |
| return _u("vtable ScriptFunctionWithInlineCacheHomeObjAndComputedName"); |
| break; |
| case VtableConcatStringMulti: |
| return _u("vtable ConcatStringMulti"); |
| break; |
| case VtableCompoundString: |
| return _u("vtable CompoundString"); |
| break; |
| default: |
| Assert(false); |
| break; |
| } |
| |
| return _u("vtable unknown"); |
| } |
| #endif |
| |
| namespace HelperMethodAttributes |
| { |
| |
| // Position: same as in JnHelperMethod enum. |
| // Value: one or more of OR'ed HelperMethodAttribute values. |
| static const BYTE JnHelperMethodAttributes[] = |
| { |
| #define HELPERCALL(Name, Address, Attributes) Attributes, |
| #include "JnHelperMethodList.h" |
| }; |
| |
| // Returns true if the helper can throw non-OOM / non-SO exception. |
| bool CanThrow(IR::JnHelperMethod helper) |
| { |
| return (JnHelperMethodAttributes[helper] & AttrCanThrow) != 0; |
| } |
| |
| bool IsInVariant(IR::JnHelperMethod helper) |
| { |
| return (JnHelperMethodAttributes[helper] & AttrInVariant) != 0; |
| } |
| |
| bool CanBeReentrant(IR::JnHelperMethod helper) |
| { |
| return (JnHelperMethodAttributes[helper] & AttrCanNotBeReentrant) == 0; |
| } |
| |
| bool TempObjectProducing(IR::JnHelperMethod helper) |
| { |
| return (JnHelperMethodAttributes[helper] & AttrTempObjectProducing) != 0; |
| } |
| |
| #ifdef DBG_DUMP |
| struct ValidateHelperHeaders |
| { |
| ValidateHelperHeaders() |
| { |
| #define HELPERCALL(Name, Address, Attributes) |
| #define HELPERCALLCHK(Name, Address, Attributes) \ |
| Assert(JitHelperUtils::helper##Name##_implemented); |
| #include "../Backend/JnHelperMethodList.h" |
| } |
| }; |
| ValidateHelperHeaders validateHelperHeaders; |
| #endif |
| |
| } //namespace HelperMethodAttributes |