//------------------------------------------------------------------------------------------------------- // 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" #include "Debug/DiagHelperMethodWrapper.h" #include "Math/JavascriptSSE2MathOperators.h" #include "Math/JavascriptSSE2MathOperators.inl" #include "Math/CrtSSE2Math.h" #include "Library/JavascriptGeneratorFunction.h" namespace IR { intptr_t const JnHelperMethodAddresses[] = { #define HELPERCALL(Name, Address, Attributes) reinterpret_cast(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" #undef HELPERCALL NULL }; #if defined(_M_IX86) intptr_t const JnHelperMethodAddresses_SSE2[] = { #define SSE2MATH #define HELPERCALL(Name, Address, Attributes) reinterpret_cast(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" #undef HELPERCALL #undef SSE2MATH NULL }; intptr_t const *GetHelperMethods() { if (AutoSystemInfo::Data.SSE2Available()) { return JnHelperMethodAddresses_SSE2; } return JnHelperMethodAddresses; } #else intptr_t const *GetHelperMethods() { return JnHelperMethodAddresses; } #endif #if ENABLE_DEBUG_CONFIG_OPTIONS && defined(_CONTROL_FLOW_GUARD) class HelperTableCheck { public: HelperTableCheck() { CheckJnHelperTable(JnHelperMethodAddresses); #if defined(_M_IX86) CheckJnHelperTable(JnHelperMethodAddresses_SSE2); #endif } }; // 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 static intptr_t const helperMethodWrappers[] = { reinterpret_cast(&Js::HelperMethodWrapper0), reinterpret_cast(&Js::HelperMethodWrapper1), reinterpret_cast(&Js::HelperMethodWrapper2), reinterpret_cast(&Js::HelperMethodWrapper3), reinterpret_cast(&Js::HelperMethodWrapper4), reinterpret_cast(&Js::HelperMethodWrapper5), reinterpret_cast(&Js::HelperMethodWrapper6), reinterpret_cast(&Js::HelperMethodWrapper7), reinterpret_cast(&Js::HelperMethodWrapper8), reinterpret_cast(&Js::HelperMethodWrapper9), reinterpret_cast(&Js::HelperMethodWrapper10), reinterpret_cast(&Js::HelperMethodWrapper11), reinterpret_cast(&Js::HelperMethodWrapper12), reinterpret_cast(&Js::HelperMethodWrapper13), reinterpret_cast(&Js::HelperMethodWrapper14), reinterpret_cast(&Js::HelperMethodWrapper15), reinterpret_cast(&Js::HelperMethodWrapper16), }; ///---------------------------------------------------------------------------- /// /// 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); #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 SHIFT_ADDR(context, helperMethodWrappers[diagOpnd->m_argCount]); } else { AssertMsg(FALSE, "Unsupported arg count (need to implement)."); } } 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) // TODO: OOP JIT, have some way to validate that these are all loaded from CRT case HelperDirectMath_Acos: return SHIFT_CRT_ADDR(context, (double(*)(double))__libm_sse2_acos); case HelperDirectMath_Asin: return SHIFT_CRT_ADDR(context, (double(*)(double))__libm_sse2_asin); case HelperDirectMath_Atan: return SHIFT_CRT_ADDR(context, (double(*)(double))__libm_sse2_atan); case HelperDirectMath_Atan2: return SHIFT_CRT_ADDR(context, (double(*)(double, double))__libm_sse2_atan2); case HelperDirectMath_Cos: return SHIFT_CRT_ADDR(context, (double(*)(double))__libm_sse2_cos); case HelperDirectMath_Exp: return SHIFT_CRT_ADDR(context, (double(*)(double))__libm_sse2_exp); case HelperDirectMath_Log: return SHIFT_CRT_ADDR(context, (double(*)(double))__libm_sse2_log); case HelperDirectMath_Sin: return SHIFT_CRT_ADDR(context, (double(*)(double))__libm_sse2_sin); case HelperDirectMath_Tan: return SHIFT_CRT_ADDR(context, (double(*)(double))__libm_sse2_tan); #endif case HelperDirectMath_FloorDb: return SHIFT_CRT_ADDR(context, (double(*)(double))floor); case HelperDirectMath_CeilDb: return SHIFT_CRT_ADDR(context, (double(*)(double))ceil); // // 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 SHIFT_CRT_ADDR(context, (int(*)(const char16 *, const char16 *, size_t))wmemcmp); case HelperMemCpy: return SHIFT_CRT_ADDR(context, (void*(*)(void *, void const*, size_t))memcpy); case HelperDirectMath_FloorFlt: return SHIFT_CRT_ADDR(context, (float(*)(float))floor); case HelperDirectMath_CeilFlt: return SHIFT_CRT_ADDR(context, (float(*)(float))ceil); #if defined(_M_X64) case HelperDirectMath_Acos: return SHIFT_CRT_ADDR(context, (double(*)(double))acos); case HelperDirectMath_Asin: return SHIFT_CRT_ADDR(context, (double(*)(double))asin); case HelperDirectMath_Atan: return SHIFT_CRT_ADDR(context, (double(*)(double))atan); case HelperDirectMath_Atan2: return SHIFT_CRT_ADDR(context, (double(*)(double, double))atan2); case HelperDirectMath_Cos: return SHIFT_CRT_ADDR(context, (double(*)(double))cos); case HelperDirectMath_Exp: return SHIFT_CRT_ADDR(context, (double(*)(double))exp); case HelperDirectMath_Log: return SHIFT_CRT_ADDR(context, (double(*)(double))log); case HelperDirectMath_Sin: return SHIFT_CRT_ADDR(context, (double(*)(double))sin); case HelperDirectMath_Tan: return SHIFT_CRT_ADDR(context, (double(*)(double))tan); #elif defined(_M_ARM32_OR_ARM64) case HelperDirectMath_Acos: return SHIFT_CRT_ADDR(context, (double(*)(double))acos); case HelperDirectMath_Asin: return SHIFT_CRT_ADDR(context, (double(*)(double))asin); case HelperDirectMath_Atan: return SHIFT_CRT_ADDR(context, (double(*)(double))atan); case HelperDirectMath_Atan2: return SHIFT_CRT_ADDR(context, (double(*)(double, double))atan2); case HelperDirectMath_Cos: return SHIFT_CRT_ADDR(context, (double(*)(double))cos); case HelperDirectMath_Exp: return SHIFT_CRT_ADDR(context, (double(*)(double))exp); case HelperDirectMath_Log: return SHIFT_CRT_ADDR(context, (double(*)(double))log); case HelperDirectMath_Sin: return SHIFT_CRT_ADDR(context, (double(*)(double))sin); case HelperDirectMath_Tan: return SHIFT_CRT_ADDR(context, (double(*)(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 SHIFT_ADDR(context, Js::JavascriptExceptionOperators::OP_TryCatch); case HelperOp_TryFinally: return SHIFT_ADDR(context, Js::JavascriptExceptionOperators::OP_TryFinally); case HelperOp_TryFinallySimpleJit: return SHIFT_ADDR(context, Js::JavascriptExceptionOperators::OP_TryFinallySimpleJit); // // Methods that we don't want to get marked as CFG targets as they dump all registers to a controlled address // case HelperSaveAllRegistersAndBailOut: return SHIFT_ADDR(context, LinearScanMD::SaveAllRegistersAndBailOut); case HelperSaveAllRegistersAndBranchBailOut: return SHIFT_ADDR(context, LinearScanMD::SaveAllRegistersAndBranchBailOut); #ifdef _M_IX86 case HelperSaveAllRegistersNoSse2AndBailOut: return SHIFT_ADDR(context, LinearScanMD::SaveAllRegistersNoSse2AndBailOut); case HelperSaveAllRegistersNoSse2AndBranchBailOut: return SHIFT_ADDR(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) { intptr_t address = GetHelperMethods()[static_cast(helperMethod)]; if (address == 0) { return GetNonTableMethodAddress(context, helperMethod); } return SHIFT_ADDR(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" #undef HELPERCALL NULL }; ///---------------------------------------------------------------------------- /// /// GetMethodName /// /// returns the string representing the name of the helperMethod. /// ///---------------------------------------------------------------------------- char16 const* GetMethodName(JnHelperMethod helperMethod) { return JnHelperMethodNames[static_cast(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 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" #undef HELPERCALL }; // 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; } } //namespace HelperMethodAttributes