InterpreterThunkEmitter.h 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  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. #ifdef 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. public:
  16. ThunkBlock(BYTE* start) :
  17. start(start),
  18. freeList(NULL)
  19. #if PDATA_ENABLED
  20. , registeredPdataTable(NULL)
  21. #endif
  22. {}
  23. bool Contains(BYTE* address) const;
  24. void Release(BYTE* address);
  25. bool EnsureFreeList(ArenaAllocator* allocator);
  26. BYTE* AllocateFromFreeList();
  27. bool IsFreeListEmpty() const;
  28. BYTE* GetStart() const { return start; }
  29. #if PDATA_ENABLED
  30. void* GetPdata() const { return registeredPdataTable; }
  31. void SetPdata(void* pdata) { Assert(!this->registeredPdataTable); this->registeredPdataTable = pdata; }
  32. #endif
  33. private:
  34. BVIndex FromThunkAddress(BYTE* address);
  35. BYTE* ToThunkAddress(BVIndex index);
  36. };
  37. //
  38. // Emits interpreter thunks such that each javascript function in the interpreter gets a unique return address on call.
  39. // This unique address can be used to identify the function when a stackwalk is
  40. // performed using ETW.
  41. //
  42. // On every call, we generate 1 thunk (size ~ 1 page) that is implemented as a jump table.
  43. // The shape of the thunk is as follows:
  44. // 1. Function prolog
  45. // 2. Determine the thunk number from the JavascriptFunction object passed as first argument.
  46. // 3. Calculate the address of the correct calling point and jump to it.
  47. // 4. Make the call and jump to the epilog.
  48. // 5. Function epilog.
  49. //
  50. class InterpreterThunkEmitter
  51. {
  52. private:
  53. /* ------- instance methods --------*/
  54. EmitBufferManager<> emitBufferManager;
  55. EmitBufferAllocation *allocation;
  56. SListBase<ThunkBlock> thunkBlocks;
  57. SListBase<ThunkBlock> freeListedThunkBlocks;
  58. void * interpreterThunk; // the static interpreter thunk invoked by the dynamic emitted thunk
  59. BYTE* thunkBuffer;
  60. ArenaAllocator* allocator;
  61. DWORD thunkCount; // Count of thunks available in the current thunk block
  62. /* -------static constants ----------*/
  63. // Interpreter thunk buffer includes function prolog, setting up of arguments, jumping to the appropriate calling point.
  64. static const BYTE ThunkAddressOffset;
  65. static const BYTE FunctionBodyOffset;
  66. static const BYTE DynamicThunkAddressOffset;
  67. static const BYTE InterpreterThunkEmitter::CallBlockStartAddrOffset;
  68. static const BYTE InterpreterThunkEmitter::ThunkSizeOffset;
  69. static const BYTE InterpreterThunkEmitter::ErrorOffset;
  70. #if defined(_M_ARM)
  71. static const BYTE InterpreterThunkEmitter::CallBlockStartAddressInstrOffset;
  72. static const BYTE InterpreterThunkEmitter::CallThunkSizeInstrOffset;
  73. #endif
  74. static const BYTE InterpreterThunk[];
  75. // Call buffer includes a call to the inner interpreter thunk and eventual jump to the epilog
  76. static const BYTE JmpOffset;
  77. static const BYTE Call[];
  78. static const BYTE Epilog[];
  79. static const BYTE PageCount;
  80. #if defined(_M_X64)
  81. static const BYTE PrologSize;
  82. static const BYTE StackAllocSize;
  83. #endif
  84. /* ------private helpers -----------*/
  85. void NewThunkBlock();
  86. void EncodeInterpreterThunk(__in_bcount(thunkSize) BYTE* thunkBuffer, __in_bcount(thunkSize) BYTE* thunkBufferStartAddress, __in const DWORD thunkSize, __in_bcount(epilogSize) BYTE* epilogStart, __in const DWORD epilogSize);
  87. #if defined(_M_ARM32_OR_ARM64)
  88. DWORD EncodeMove(DWORD opCode, int reg, DWORD imm16);
  89. void GeneratePdata(_In_ const BYTE* entryPoint, _In_ const DWORD functionSize, _Out_ RUNTIME_FUNCTION* function);
  90. #endif
  91. /*-------static helpers ---------*/
  92. inline static DWORD FillDebugBreak(__out_bcount_full(count) BYTE* dest, __in DWORD count);
  93. inline static DWORD CopyWithAlignment(__in_bcount(sizeInBytes) BYTE* dest, __in const DWORD sizeInBytes, __in_bcount(srcSize) const BYTE* src, __in_range(0, sizeInBytes) const DWORD srcSize, __in const DWORD alignment);
  94. template<class T>
  95. inline static void Emit(__in_bcount(sizeof(T) + offset) BYTE* dest, __in const DWORD offset, __in const T value)
  96. {
  97. AssertMsg(*(T*) (dest + offset) == 0, "Overwriting an already existing opcode?");
  98. *(T*)(dest + offset) = value;
  99. };
  100. public:
  101. static const BYTE HeaderSize;
  102. static const BYTE ThunkSize;
  103. static const uint ThunksPerBlock;
  104. static const uint BlockSize;
  105. static void* ConvertToEntryPoint(PVOID dynamicInterpreterThunk);
  106. InterpreterThunkEmitter(AllocationPolicyManager * policyManager, ArenaAllocator* allocator, void * interpreterThunk);
  107. BYTE* GetNextThunk(PVOID* ppDynamicInterpreterThunk);
  108. BYTE* AllocateFromFreeList(PVOID* ppDynamicInterpreterThunk);
  109. void Close();
  110. void Release(BYTE* thunkAddress, bool addtoFreeList);
  111. // Returns true if the argument falls within the range managed by this buffer.
  112. inline bool IsInRange(void* address)
  113. {
  114. return emitBufferManager.IsInRange(address);
  115. }
  116. const EmitBufferManager<>* GetEmitBufferManager() const
  117. {
  118. return &emitBufferManager;
  119. }
  120. };
  121. #endif