StackFrame.h 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  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. ///
  7. /// Note: The below description is applicable to the x86-x64 calling
  8. /// convention on Windows. For other platforms, look at the comments in
  9. /// the appropriate cpp file like StackFrame.SystemV.cpp
  10. ///
  11. /*
  12. * Stackwalking on x86-64:
  13. * ----------------------
  14. *
  15. * On x86 the EBP register always points to the current stack frame, which, at
  16. * offset 0 contains a pointer to its caller's stack frame. Walking the stack
  17. * is accomplished by walking this linked list. Currently VC++ does not build
  18. * RBP frames on x86-64. However, the format of a function's stack frame is fairly
  19. * restricted. This and the fact that each function has metadata makes a stack walk
  20. * possible.
  21. *
  22. * The x86-64 ABI (that VC++ uses) is an x86 "fastcall" like calling convention
  23. * that uses RCX, RDX, R8 and R9 to pass the first 4 QWORD parameters with stack backing
  24. * for those registers. The caller is responsible for allocating space for parameters to
  25. * the callee and *always* allocates 4 extra QWORDs that the callee uses to "home" the
  26. * argument registers (typically). Home addresses are required for the register arguments
  27. * so a contiguous area is available in case the callee uses a va_list. However, the callee
  28. * is not required to save the value in the registers into these slots and generally does
  29. * not unless the compiler deems it necessary. The caller always cleans the stack.
  30. *
  31. * All non-leaf functions (functions that either alloca / call other functions / use exception
  32. * handling) are annotated with data that describes how non-volatile registers
  33. * can be restored (say during a stack unwind). This data structure lives in the PDATA section
  34. * of a PE image. It describes the address limits of the prolog which
  35. * - saves argument registers in their home addresses
  36. * - pushes non-volatile registers used by the function on the stack
  37. * - allocates locals
  38. * - establishes a frame pointer (usually only if the function uses alloca)
  39. * The epilog trims the stack by reversing the effects of the prolog. Epilogs follow a
  40. * strict set of rules so that the operating system can scan a code stream to identify one.
  41. * This eliminates the need for more metadata. Epilogs are usually of the form
  42. *
  43. * add RSP, stack_frame_fixed_size
  44. * pop Rn
  45. * ...
  46. * ret
  47. *
  48. * Since there is no linked list on the stack to follow, the x86-64 stack walker
  49. * (the platform specific portions of which are implemented in Amd64StackFrame and Javascript
  50. * specific portions in JavascriptStackWalker) uses the above mentioned metadata to
  51. * "unwind" the stack to get to a caller from its callee. Since we don't really want
  52. * to unwind the stack, we use the RtlVirtualUnwind API that does the unwind using a copy
  53. * of portions of the real stack using an instance of CONTEXT as the register file.
  54. *
  55. * The Javascript stack walker needs to find out what the return address, argv is for a given
  56. * stack frame.
  57. * - return address: to match against one in a script context that serves to identify a
  58. * stack frame where a stack walk should start (frameIdOfScriptExitFunction)
  59. * or terminate (returnAddrOfScriptEntryFunction).
  60. * - argv: used to implement caller args, find the line number (from the function object) when
  61. * an exception is thrown etc.
  62. *
  63. * During stack unwind, the return address for a given stack frame is the address RIP is
  64. * pointing to when we unwind to its caller. Similarly argv is the address that RSP is pointing
  65. * to when we unwind to its caller. We make sure that the arguments passed in registers are
  66. * homed by making use of the /homeparams switch.
  67. *
  68. * A "JavascriptFrame" is a stack frame established either by JITted script code or by the
  69. * interpreter whose arguments are always:
  70. * JavascriptFunction * <-- instance of the javascript function object that this frame
  71. * corresponds to.
  72. * CallInfo
  73. * this <-- first script argument (hidden "this" pointer).
  74. * ... <-- rest of the script arguments.
  75. */
  76. namespace Js {
  77. class ScriptContext;
  78. #ifdef _WIN32
  79. class Amd64StackFrame {
  80. public:
  81. Amd64StackFrame();
  82. ~Amd64StackFrame();
  83. bool InitializeByFrameId(void * returnAddress, ScriptContext* scriptContext) { return InitializeByReturnAddress(returnAddress, scriptContext); }
  84. bool InitializeByReturnAddress(void * returnAddress, ScriptContext* scriptContext);
  85. bool Next();
  86. void *GetInstructionPointer();
  87. void **GetArgv(bool isCurrentContextNative = false, bool shouldCheckForNativeAddr = true);
  88. void *GetReturnAddress(bool isCurrentContextNative = false, bool shouldCheckForNativeAddr = true);
  89. void *GetAddressOfReturnAddress(bool isCurrentContextNative = false, bool shouldCheckForNativeAddr = true);
  90. void *GetAddressOfInstructionPointer() { return this->addressOfCodeAddr; }
  91. bool SkipToFrame(void * returnAddress);
  92. void *GetFrame() const;
  93. size_t GetStackCheckCodeHeight() { return this->stackCheckCodeHeight; }
  94. static bool IsInStackCheckCode(void *entry, void * codeAddr, size_t stackCheckCodeHeight);
  95. private:
  96. void* addressOfCodeAddr;
  97. ScriptContext *scriptContext;
  98. ULONG64 imageBase;
  99. RUNTIME_FUNCTION *functionEntry;
  100. CONTEXT *currentContext;
  101. // We store the context of the caller the first time we retrieve it so that
  102. // consecutive operations that need the caller context don't have to retrieve
  103. // it again. The callerContext is only valid if hasCallerContext is true. When
  104. // currentContext is changed, callerContext must be invalidated by calling
  105. // OnCurrentContextUpdated().
  106. bool hasCallerContext;
  107. CONTEXT *callerContext;
  108. size_t stackCheckCodeHeight;
  109. inline void EnsureFunctionEntry();
  110. inline bool EnsureCallerContext(bool isCurrentContextNative);
  111. inline void OnCurrentContextUpdated();
  112. static bool NextFromNativeAddress(CONTEXT * context);
  113. static bool Next(CONTEXT *context, ULONG64 imageBase, RUNTIME_FUNCTION *runtimeFunction);
  114. static const size_t stackCheckCodeHeightThreadBound = StackFrameConstants::StackCheckCodeHeightThreadBound;
  115. static const size_t stackCheckCodeHeightNotThreadBound = StackFrameConstants::StackCheckCodeHeightNotThreadBound;
  116. static const size_t stackCheckCodeHeightWithInterruptProbe = StackFrameConstants::StackCheckCodeHeightWithInterruptProbe;
  117. };
  118. #else
  119. // Models a stack frame based on SystemV ABI for AMD64
  120. // This is very similar to the model for x86
  121. class Amd64StackFrame
  122. {
  123. public:
  124. Amd64StackFrame() : frame(nullptr), codeAddr(nullptr), stackCheckCodeHeight(0), addressOfCodeAddr(nullptr) {};
  125. bool InitializeByFrameId(void * frameAddress, ScriptContext* scriptContext);
  126. bool InitializeByReturnAddress(void * returnAddress, ScriptContext* scriptContext);
  127. bool Next();
  128. void * GetInstructionPointer() { return codeAddr; }
  129. void ** GetArgv(bool isCurrentContextNative = false, bool shouldCheckForNativeAddr = true) { return frame + 2; }
  130. void * GetReturnAddress(bool isCurrentContextNative = false, bool shouldCheckForNativeAddr = true) { return frame[1]; }
  131. void * GetAddressOfReturnAddress(bool isCurrentContextNative = false, bool shouldCheckForNativeAddr = true) { return &frame[1]; }
  132. void * GetAddressOfInstructionPointer() const { return addressOfCodeAddr; }
  133. void * GetFrame() const { return (void *)frame; }
  134. void SetReturnAddress(void * address) { frame[1] = address; }
  135. bool SkipToFrame(void * frameAddress);
  136. size_t GetStackCheckCodeHeight() { return this->stackCheckCodeHeight; }
  137. static bool IsInStackCheckCode(void *entry, void *codeAddr, size_t stackCheckCodeHeight);
  138. private:
  139. void ** frame; // rbp
  140. void * codeAddr; // rip
  141. void * addressOfCodeAddr;
  142. size_t stackCheckCodeHeight;
  143. static const size_t stackCheckCodeHeightThreadBound = StackFrameConstants::StackCheckCodeHeightThreadBound;
  144. static const size_t stackCheckCodeHeightNotThreadBound = StackFrameConstants::StackCheckCodeHeightNotThreadBound;
  145. static const size_t stackCheckCodeHeightWithInterruptProbe = StackFrameConstants::StackCheckCodeHeightWithInterruptProbe;
  146. };
  147. #endif
  148. class Amd64ContextsManager
  149. {
  150. public:
  151. Amd64ContextsManager();
  152. #ifdef _WIN32
  153. private:
  154. static const int CONTEXT_PAIR_COUNT = 2;
  155. enum
  156. {
  157. GENERAL_CONTEXT = 0,
  158. OOM_CONTEXT = 1,
  159. NUM_CONTEXTS = 2
  160. };
  161. typedef int ContextsIndex;
  162. CONTEXT contexts[CONTEXT_PAIR_COUNT * NUM_CONTEXTS];
  163. _Field_range_(GENERAL_CONTEXT, NUM_CONTEXTS)
  164. ContextsIndex curIndex;
  165. _Ret_writes_(CONTEXT_PAIR_COUNT) CONTEXT* InternalGet(
  166. _In_range_(GENERAL_CONTEXT, OOM_CONTEXT) ContextsIndex index);
  167. private:
  168. friend class Amd64StackFrame;
  169. _Ret_writes_(CONTEXT_PAIR_COUNT) CONTEXT* Allocate();
  170. void Release(_In_ CONTEXT* contexts);
  171. #endif
  172. };
  173. };