InterpreterThunkEmitter.h 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  1. //-------------------------------------------------------------------------------------------------------
  2. // Copyright (C) Microsoft. All rights reserved.
  3. // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
  4. //-------------------------------------------------------------------------------------------------------
  5. #pragma once
  6. #if ENABLE_NATIVE_CODEGEN
  7. class ThunkBlock
  8. {
  9. private:
  10. #if PDATA_ENABLED
  11. void* registeredPdataTable;
  12. #endif
  13. BYTE* start;
  14. BVFixed* freeList;
  15. DWORD thunkCount;
  16. public:
  17. ThunkBlock(BYTE* start, DWORD thunkCount) :
  18. start(start),
  19. thunkCount(thunkCount),
  20. freeList(NULL)
  21. #if PDATA_ENABLED
  22. , registeredPdataTable(NULL)
  23. #endif
  24. {}
  25. bool Contains(BYTE* address) const;
  26. void Release(BYTE* address);
  27. bool EnsureFreeList(ArenaAllocator* allocator);
  28. BYTE* AllocateFromFreeList();
  29. bool IsFreeListEmpty() const;
  30. BYTE* GetStart() const { return start; }
  31. #if PDATA_ENABLED
  32. void* GetPdata() const { return registeredPdataTable; }
  33. void SetPdata(void* pdata) { Assert(!this->registeredPdataTable); this->registeredPdataTable = pdata; }
  34. #endif
  35. private:
  36. BVIndex FromThunkAddress(BYTE* address);
  37. BYTE* ToThunkAddress(BVIndex index);
  38. };
  39. //
  40. // Emits interpreter thunks such that each javascript function in the interpreter gets a unique return address on call.
  41. // This unique address can be used to identify the function when a stackwalk is
  42. // performed using ETW.
  43. //
  44. // On every call, we generate 1 thunk (size ~ 1 page) that is implemented as a jump table.
  45. // The shape of the thunk is as follows:
  46. // 1. Function prolog
  47. // 2. Determine the thunk number from the JavascriptFunction object passed as first argument.
  48. // 3. Calculate the address of the correct calling point and jump to it.
  49. // 4. Make the call and jump to the epilog.
  50. // 5. Function epilog.
  51. //
  52. class InterpreterThunkEmitter
  53. {
  54. public:
  55. // InterpreterThunkSize must be public to be accessible to all code in InterpreterThunkEmitter.cpp.
  56. #ifdef _M_X64
  57. #ifdef _WIN32
  58. static constexpr size_t InterpreterThunkSize = 96;
  59. #else // Sys V AMD64
  60. static constexpr size_t InterpreterThunkSize = 72;
  61. #endif
  62. #elif defined(_M_ARM)
  63. static constexpr size_t InterpreterThunkSize = 72;
  64. #elif defined(_M_ARM64)
  65. static constexpr size_t InterpreterThunkSize = 64;
  66. #else
  67. static constexpr size_t InterpreterThunkSize = 56;
  68. #endif
  69. private:
  70. /* ------- instance methods --------*/
  71. InProcEmitBufferManager emitBufferManager;
  72. SListBase<ThunkBlock> thunkBlocks;
  73. SListBase<ThunkBlock> freeListedThunkBlocks;
  74. bool isAsmInterpreterThunk; // To emit address of InterpreterAsmThunk or InterpreterThunk
  75. BYTE* thunkBuffer;
  76. ArenaAllocator* allocator;
  77. Js::ScriptContext * scriptContext;
  78. DWORD thunkCount; // Count of thunks available in the current thunk block
  79. static constexpr BYTE PageCount = 1;
  80. /* ------private helpers -----------*/
  81. bool NewThunkBlock();
  82. #ifdef ENABLE_OOP_NATIVE_CODEGEN
  83. bool NewOOPJITThunkBlock();
  84. #endif
  85. static void EncodeInterpreterThunk(
  86. __in_bcount(InterpreterThunkSize) BYTE* thunkBuffer,
  87. __in const intptr_t thunkBufferStartAddress,
  88. __in const intptr_t epilogStart,
  89. __in const DWORD epilogSize,
  90. __in const intptr_t interpreterThunk);
  91. #if defined(_M_ARM32_OR_ARM64)
  92. static DWORD EncodeMove(DWORD opCode, int reg, DWORD imm16);
  93. static void GeneratePdata(_In_ const BYTE* entryPoint, _In_ const DWORD functionSize, _Out_ RUNTIME_FUNCTION* function);
  94. #endif
  95. /*-------static helpers ---------*/
  96. inline static DWORD FillDebugBreak(_Out_writes_bytes_all_(count) BYTE* dest, _In_ DWORD count);
  97. inline static DWORD CopyWithAlignment(_Out_writes_bytes_all_(sizeInBytes) BYTE* dest, _In_ const DWORD sizeInBytes, _In_reads_bytes_(srcSize) const BYTE* src, _In_ const DWORD srcSize, _In_ const DWORD alignment);
  98. template<class T>
  99. inline static void Emit(__in_bcount(sizeof(T) + offset) BYTE* dest, __in const DWORD offset, __in const T value)
  100. {
  101. AssertMsg(*(T*) (dest + offset) == 0, "Overwriting an already existing opcode?");
  102. *(T*)(dest + offset) = value;
  103. };
  104. BYTE* AllocateFromFreeList(PVOID* ppDynamicInterpreterThunk);
  105. public:
  106. static const BYTE ThunkSize;
  107. static constexpr uint BlockSize = AutoSystemInfo::PageSize * PageCount;
  108. static void* ConvertToEntryPoint(PVOID dynamicInterpreterThunk);
  109. InterpreterThunkEmitter(Js::ScriptContext * context, ArenaAllocator* allocator, CustomHeap::InProcCodePageAllocators * codePageAllocators, bool isAsmInterpreterThunk = false);
  110. BYTE* GetNextThunk(PVOID* ppDynamicInterpreterThunk);
  111. SListBase<ThunkBlock>* GetThunkBlocksList();
  112. void Close();
  113. void Release(BYTE* thunkAddress, bool addtoFreeList);
  114. // Returns true if the argument falls within the range managed by this buffer.
  115. #if DBG
  116. bool IsInHeap(void* address);
  117. #endif
  118. const InProcEmitBufferManager* GetEmitBufferManager() const
  119. {
  120. return &emitBufferManager;
  121. }
  122. static void FillBuffer(
  123. _In_ ThreadContextInfo * context,
  124. _In_ bool asmJsThunk,
  125. _In_ intptr_t finalAddr,
  126. _In_ size_t bufferSize,
  127. _Out_writes_bytes_all_(BlockSize) BYTE* buffer,
  128. #if PDATA_ENABLED
  129. _Out_ PRUNTIME_FUNCTION * pdataTableStart,
  130. _Out_ intptr_t * epilogEndAddr,
  131. #endif
  132. _Out_ DWORD * thunkCount
  133. );
  134. };
  135. #endif