eathread_callstack_x86.cpp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627
  1. ///////////////////////////////////////////////////////////////////////////////
  2. // Copyright (c) Electronic Arts Inc. All rights reserved.
  3. ///////////////////////////////////////////////////////////////////////////////
  4. ///////////////////////////////////////////////////////////////////////////////
  5. // This file implements a manual callstack reader for the x86 platform.
  6. // It is an alternative to EACallstack_Win32.cpp, which implements
  7. // callstack reading by standard Microsoft debug functions. This file
  8. // on the other hand avoids the use of Microsoft calls and instead
  9. // directly walks the callstack via reading the registers and x86
  10. // instructions. This file can also be used to read x86 callstacks on
  11. // non-Microsoft x86 platforms such as Linux.
  12. ///////////////////////////////////////////////////////////////////////////////
  13. #include <eathread/eathread_callstack.h>
  14. #include <eathread/eathread_callstack_context.h>
  15. #include <eathread/eathread_storage.h>
  16. #include <string.h>
  17. #if EATHREAD_GLIBC_BACKTRACE_AVAILABLE
  18. #include <signal.h>
  19. #include <execinfo.h>
  20. #endif
  21. #if defined(EA_COMPILER_MSVC) && defined(EA_PROCESSOR_X86_64)
  22. EA_DISABLE_ALL_VC_WARNINGS()
  23. #include <math.h> // VS2008 has an acknowledged bug that requires math.h (and possibly also string.h) to be #included before intrin.h.
  24. #include <intrin.h>
  25. EA_RESTORE_ALL_VC_WARNINGS()
  26. #endif
  27. #if defined(EA_COMPILER_CLANG)
  28. #include <unwind.h>
  29. #endif
  30. namespace EA
  31. {
  32. namespace Thread
  33. {
  34. ///////////////////////////////////////////////////////////////////////////////
  35. // InitCallstack
  36. //
  37. EATHREADLIB_API void InitCallstack()
  38. {
  39. }
  40. ///////////////////////////////////////////////////////////////////////////////
  41. // ShutdownCallstack
  42. //
  43. EATHREADLIB_API void ShutdownCallstack()
  44. {
  45. }
  46. #if defined(EA_COMPILER_MSVC)
  47. ///////////////////////////////////////////////////////////////////////////////
  48. // ReturnAddressToCallingAddress
  49. //
  50. // Convert the return address to the calling address.
  51. //
  52. void* ReturnAddressToCallingAddress(unsigned char* return_address)
  53. {
  54. // While negative array indices can be considered non-idiomatic it
  55. // was felt that they are semantically appropriate as this code bases
  56. // its comparisons from the return address and that it would be cleaner
  57. // than using *(return_address - index).
  58. // Three op-codes are used for the call instruction, 9A, E8, and FF.
  59. // For a reference on the IA32 instruction format, see:
  60. // http://www.cs.princeton.edu/courses/archive/spr06/cos217/reading/ia32vol2.pdf
  61. // 9A cp - CALL ptr16:32 (7-byte)
  62. if(return_address[-7] == 0x9A)
  63. {
  64. return (return_address - 7);
  65. }
  66. // E8 cd - CALL rel32 (5-byte)
  67. else if(return_address[-5] == 0xE8)
  68. {
  69. return (return_address - 5);
  70. }
  71. else
  72. {
  73. // The third opcode to specify "call" instructions is FF.
  74. // Unfortunately this instruction also needs the succeeding ModR/M
  75. // byte to fully determine instruction length. The SIB value is
  76. // another byte used for extending the range of addressing modes
  77. // supported by the ModR/M byte. The values of this ModR/M byte
  78. // used in conjunction with the call instruction are as follows:
  79. //
  80. // 7-byte call:
  81. // FF [ModR/M] [SIB] [4-byte displacement]
  82. // * ModR/M is either 0x94 or 0x9C
  83. //
  84. // 6-byte call:
  85. // FF [ModR/M] [4-byte displacement]
  86. // * ModR/M can be:
  87. // * 0x90 - 0x9F EXCLUDING 0x94 or 0x9C
  88. // * 0x15 or 0x1D
  89. //
  90. // 4-byte call:
  91. // FF [ModR/M] [SIB] [1-byte displacement]
  92. // * ModR/M is either 0x54 or 0x5C
  93. //
  94. // 3-byte call:
  95. // FF [ModR/M] [1-byte displacement]
  96. // * ModR/M can be:
  97. // * 0x50 - 0x5F EXCLUDING 0x54 or 0x5C
  98. // FF [ModR/M] [SIB]
  99. // * ModR/M is either 0x14 or 0x1C
  100. //
  101. // 2-byte call:
  102. // FF [ModR/M]
  103. // * ModR/M can be:
  104. // * 0xD0 - 0xDF
  105. // * 0x10 - 0x1F EXCEPT 0x14, 0x15, 0x1C, or 0x1D
  106. // The mask of F8 is used because we want to mask out the bottom
  107. // three bits (which are most often used for register selection).
  108. const unsigned char rm_mask = 0xF8;
  109. // 7-byte format:
  110. if(return_address[-7] == 0xFF &&
  111. (return_address[-6] == 0x94 || return_address[-6] == 0x9C))
  112. {
  113. return (return_address - 7);
  114. }
  115. // 6-byte format:
  116. // FF [ModR/M] [4-byte displacement]
  117. else if(return_address[-6] == 0xFF &&
  118. ((return_address[-5] & rm_mask) == 0x90 || (return_address[-5] & rm_mask) == 0x98) &&
  119. (return_address[-5] != 0x94 && return_address[-5] != 0x9C))
  120. {
  121. return (return_address - 6);
  122. }
  123. // Alternate 6-byte format:
  124. else if(return_address[-6] == 0xFF &&
  125. (return_address[-5] == 0x15 || return_address[-5] == 0x1D))
  126. {
  127. return (return_address - 6);
  128. }
  129. // 4-byte format:
  130. // FF [ModR/M] [SIB] [1-byte displacement]
  131. else if(return_address[-4] == 0xFF &&
  132. (return_address[-3] == 0x54 || return_address[-3] == 0x5C))
  133. {
  134. return (return_address - 4);
  135. }
  136. // 3-byte format:
  137. // FF [ModR/M] [1-byte displacement]
  138. else if(return_address[-3] == 0xFF &&
  139. ((return_address[-2] & rm_mask) == 0x50 || (return_address[-2] & rm_mask) == 0x58) &&
  140. (return_address[-2] != 0x54 && return_address[-2] != 0x5C))
  141. {
  142. return (return_address - 3);
  143. }
  144. // Alternate 3-byte format:
  145. // FF [ModR/M] [SIB]
  146. else if(return_address[-3] == 0xFF &&
  147. (return_address[-2] == 0x14 || return_address[-2] == 0x1C))
  148. {
  149. return (return_address - 3);
  150. }
  151. // 2-byte calling format:
  152. // FF [ModR/M]
  153. else if(return_address[-2] == 0xFF &&
  154. ((return_address[-1] & rm_mask) == 0xD0 || (return_address[-1] & rm_mask) == 0xD8))
  155. {
  156. return (return_address - 2);
  157. }
  158. // Alternate 2-byte calling format:
  159. // FF [ModR/M]
  160. else if(return_address[-2] == 0xFF &&
  161. ((return_address[-1] & rm_mask) == 0x10 || (return_address[-1] & rm_mask) == 0x18) &&
  162. (return_address[-1] != 0x14 && return_address[-1] != 0x15 &&
  163. return_address[-1] != 0x1C && return_address[-1] != 0x1D))
  164. {
  165. return (return_address - 2);
  166. }
  167. else
  168. {
  169. EA_FAIL_MSG("EACallstack: Unable to determine calling address!");
  170. }
  171. }
  172. EA_FAIL_MSG("EACallstack: Unable to determine calling address!");
  173. return NULL;
  174. }
  175. ///////////////////////////////////////////////////////////////////////////////
  176. // GetCallstack
  177. //
  178. // Capture up to nReturnAddressArrayCapacity elements of the call stack,
  179. // or the whole callstack, whichever is smaller.
  180. //
  181. EATHREADLIB_API size_t GetCallstack(void* pReturnAddressArray[], size_t nReturnAddressArrayCapacity, const CallstackContext* pContext)
  182. {
  183. size_t index = 0;
  184. // The pContext option is not currently supported.
  185. unsigned char* local_ebp;
  186. if(pContext)
  187. local_ebp = (unsigned char*)pContext->mEBP;
  188. else
  189. {
  190. __asm mov local_ebp, ebp
  191. // Remove the top return address because that's the one for this function
  192. // which we most likely don't want in our pReturnAddressArray.
  193. local_ebp = *(reinterpret_cast<unsigned char**>(local_ebp));
  194. }
  195. for(index = 0; index < nReturnAddressArrayCapacity; ++index)
  196. {
  197. unsigned char* return_address_ptr = *(reinterpret_cast<unsigned char**>(local_ebp + 4));
  198. if(return_address_ptr == 0)
  199. {
  200. pReturnAddressArray[index] = 0;
  201. break;
  202. }
  203. else
  204. {
  205. pReturnAddressArray[index] = ReturnAddressToCallingAddress(return_address_ptr);
  206. local_ebp = *(reinterpret_cast<unsigned char**>(local_ebp));
  207. }
  208. }
  209. return index;
  210. }
  211. #elif defined(EA_COMPILER_GNUC) || defined(EA_COMPILER_CLANG)
  212. EATHREADLIB_API void GetInstructionPointer(void *&p)
  213. {
  214. p = __builtin_return_address(0);
  215. }
  216. #if !EATHREAD_GLIBC_BACKTRACE_AVAILABLE && defined(EA_COMPILER_CLANG)
  217. struct CallstackState
  218. {
  219. void** current;
  220. void** end;
  221. };
  222. static _Unwind_Reason_Code UnwindCallback(struct _Unwind_Context* context, void* arg)
  223. {
  224. CallstackState* state = static_cast<CallstackState*>(arg);
  225. uintptr_t pc = _Unwind_GetIP(context);
  226. if (pc)
  227. {
  228. if (state->current == state->end)
  229. {
  230. return _URC_END_OF_STACK;
  231. }
  232. else
  233. {
  234. *state->current++ = reinterpret_cast<void*>(pc);
  235. }
  236. }
  237. return _URC_NO_REASON;
  238. }
  239. #endif
  240. ///////////////////////////////////////////////////////////////////////////////
  241. // GetCallstack
  242. //
  243. // Capture up to nReturnAddressArrayCapacity elements of the call stack,
  244. // or the whole callstack, whichever is smaller.
  245. //
  246. EATHREADLIB_API size_t GetCallstack(void* pReturnAddressArray[], size_t nReturnAddressArrayCapacity, const CallstackContext* pContext)
  247. {
  248. #if EATHREAD_GLIBC_BACKTRACE_AVAILABLE
  249. size_t count = 0;
  250. // The pContext option is not currently supported.
  251. if(pContext == NULL)
  252. {
  253. count = (size_t)backtrace(pReturnAddressArray, nReturnAddressArrayCapacity);
  254. if(count > 0)
  255. {
  256. --count; // Remove the first entry, because it refers to this function and by design we don't include this function.
  257. memmove(pReturnAddressArray, pReturnAddressArray + 1, count * sizeof(void*));
  258. }
  259. }
  260. return count;
  261. #elif defined(EA_COMPILER_CLANG)
  262. size_t count = 0;
  263. // The pContext option is not currently supported.
  264. if (pContext == NULL)
  265. {
  266. CallstackState state = { pReturnAddressArray, pReturnAddressArray + nReturnAddressArrayCapacity };
  267. _Unwind_Backtrace(UnwindCallback, &state);
  268. count = state.current - pReturnAddressArray;
  269. if (count > 0)
  270. {
  271. --count; // Remove the first entry, because it refers to this function and by design we don't include this function.
  272. memmove(pReturnAddressArray, pReturnAddressArray + 1, count * sizeof(void*));
  273. }
  274. }
  275. return count;
  276. #else
  277. size_t index = 0;
  278. // The pContext option is not currently supported.
  279. if(pContext == NULL)
  280. {
  281. void** sp;
  282. void** new_sp;
  283. #if defined(EA_PROCESSOR_X86)
  284. // Stack frame format:
  285. // sp[0] pointer to previous frame
  286. // sp[1] caller address
  287. // sp[2] first argument
  288. // ...
  289. sp = (void**)&pReturnAddressArray - 2;
  290. #elif defined(EA_PROCESSOR_X86_64)
  291. // Arguments are passed in registers on x86-64, so we can't just offset from &pReturnAddressArray.
  292. sp = (void**) __builtin_frame_address(0);
  293. #endif
  294. for(int count = 0; sp && (index < nReturnAddressArrayCapacity); sp = new_sp, ++count)
  295. {
  296. if(count > 0) // We skip the current frame.
  297. pReturnAddressArray[index++] = *(sp + 1);
  298. new_sp = (void**)*sp;
  299. if((new_sp < sp) || (new_sp > (sp + 100000)))
  300. break;
  301. }
  302. }
  303. return index;
  304. #endif
  305. }
  306. #endif
  307. ///////////////////////////////////////////////////////////////////////////////
  308. // GetCallstackContext
  309. //
  310. EATHREADLIB_API void GetCallstackContext(CallstackContext& context, const Context* pContext)
  311. {
  312. #if defined(EA_PROCESSOR_X86_64)
  313. context.mRIP = pContext->Rip;
  314. context.mRSP = pContext->Rsp;
  315. context.mRBP = pContext->Rbp;
  316. #elif defined(EA_PROCESSOR_X86)
  317. context.mEIP = pContext->Eip;
  318. context.mESP = pContext->Esp;
  319. context.mEBP = pContext->Ebp;
  320. #endif
  321. }
  322. ///////////////////////////////////////////////////////////////////////////////
  323. // GetModuleFromAddress
  324. //
  325. EATHREADLIB_API size_t GetModuleFromAddress(const void* address, char* pModuleName, size_t moduleNameCapacity)
  326. {
  327. #ifdef EA_PLATFORM_WINDOWS
  328. MEMORY_BASIC_INFORMATION mbi;
  329. if(VirtualQuery(address, &mbi, sizeof(mbi)))
  330. {
  331. HMODULE hModule = (HMODULE)mbi.AllocationBase;
  332. if(hModule)
  333. return GetModuleFileNameA(hModule, pModuleName, (DWORD)moduleNameCapacity);
  334. }
  335. #else
  336. // Not currently implemented for the given platform.
  337. EA_UNUSED(address);
  338. EA_UNUSED(moduleNameCapacity);
  339. #endif
  340. pModuleName[0] = 0;
  341. return 0;
  342. }
  343. ///////////////////////////////////////////////////////////////////////////////
  344. // GetModuleHandleFromAddress
  345. //
  346. EATHREADLIB_API ModuleHandle GetModuleHandleFromAddress(const void* pAddress)
  347. {
  348. #ifdef EA_PLATFORM_WINDOWS
  349. MEMORY_BASIC_INFORMATION mbi;
  350. if(VirtualQuery(pAddress, &mbi, sizeof(mbi)))
  351. return (ModuleHandle)mbi.AllocationBase;
  352. #else
  353. // Not currently implemented for the given platform.
  354. EA_UNUSED(pAddress);
  355. #endif
  356. return 0;
  357. }
  358. #ifdef EA_PLATFORM_WINDOWS
  359. ///////////////////////////////////////////////////////////////////////////////
  360. // GetThreadIdFromThreadHandleWin32
  361. //
  362. static DWORD GetThreadIdFromThreadHandleWin32(HANDLE hThread)
  363. {
  364. struct THREAD_BASIC_INFORMATION_WIN32
  365. {
  366. BOOL ExitStatus;
  367. PVOID TebBaseAddress;
  368. DWORD UniqueProcessId;
  369. DWORD UniqueThreadId;
  370. DWORD AffinityMask;
  371. DWORD Priority;
  372. DWORD BasePriority;
  373. };
  374. static HMODULE hKernel32 = NULL;
  375. if(!hKernel32)
  376. hKernel32 = LoadLibraryA("kernel32.dll");
  377. if(hKernel32)
  378. {
  379. typedef DWORD (WINAPI *GetThreadIdFunc)(HANDLE);
  380. static GetThreadIdFunc pGetThreadIdFunc = NULL;
  381. if(!pGetThreadIdFunc)
  382. pGetThreadIdFunc = (GetThreadIdFunc)(uintptr_t)GetProcAddress(hKernel32, "GetThreadId");
  383. if(pGetThreadIdFunc)
  384. return pGetThreadIdFunc(hThread);
  385. }
  386. static HMODULE hNTDLL = NULL;
  387. if(!hNTDLL)
  388. hNTDLL = LoadLibraryA("ntdll.dll");
  389. if(hNTDLL)
  390. {
  391. typedef LONG (WINAPI *NtQueryInformationThreadFunc)(HANDLE, int, PVOID, ULONG, PULONG);
  392. static NtQueryInformationThreadFunc pNtQueryInformationThread = NULL;
  393. if(!pNtQueryInformationThread)
  394. pNtQueryInformationThread = (NtQueryInformationThreadFunc)(uintptr_t)GetProcAddress(hNTDLL, "NtQueryInformationThread");
  395. if(pNtQueryInformationThread)
  396. {
  397. THREAD_BASIC_INFORMATION_WIN32 tbi;
  398. if(pNtQueryInformationThread(hThread, 0, &tbi, sizeof(tbi), NULL) == 0)
  399. return tbi.UniqueThreadId;
  400. }
  401. }
  402. return 0;
  403. }
  404. #endif // #ifdef EA_PLATFORM_WINDOWS
  405. ///////////////////////////////////////////////////////////////////////////////
  406. // GetCallstackContext
  407. //
  408. // Under Windows, the threadId parameter is expected to be a thread HANDLE,
  409. // which is different from a windows integer thread id.
  410. // On Unix the threadId parameter is expected to be a pthread id.
  411. //
  412. EATHREADLIB_API bool GetCallstackContext(CallstackContext& context, intptr_t threadId)
  413. {
  414. #ifdef EA_PLATFORM_WINDOWS
  415. if((threadId == kThreadIdInvalid) || (threadId == kThreadIdCurrent))
  416. threadId = (intptr_t)GetCurrentThread(); // GetCurrentThread returns a thread 'pseudohandle' and not a real thread handle.
  417. const DWORD dwThreadIdCurrent = GetCurrentThreadId();
  418. const DWORD dwThreadIdArg = GetThreadIdFromThreadHandleWin32((HANDLE)threadId);
  419. CONTEXT win32CONTEXT;
  420. if(dwThreadIdCurrent == dwThreadIdArg)
  421. {
  422. // With VC++, EIP is not accessible directly, but we can use an assembly trick to get it.
  423. // VC++ and Intel C++ compile this fine, but Metrowerks 7 has a bug and fails.
  424. __asm{
  425. mov win32CONTEXT.Ebp, EBP
  426. mov win32CONTEXT.Esp, ESP
  427. call GetEIP
  428. GetEIP:
  429. pop win32CONTEXT.Eip
  430. }
  431. }
  432. else
  433. {
  434. // In this case we are working with a separate thread, so we suspend it
  435. // and read information about it and then resume it. Windows requires that
  436. // GetThreadContext be called on a thread that is stopped. There is a small
  437. // problem when running under the VC++ debugger: It reportedly calls
  438. // ResumeThread on suspended threads itself and so the sequence below can
  439. // be affected when stepped through with a debugger.
  440. SuspendThread((HANDLE)threadId);
  441. win32CONTEXT.ContextFlags = CONTEXT_CONTROL | CONTEXT_INTEGER;
  442. GetThreadContext((HANDLE)threadId, &win32CONTEXT);
  443. ResumeThread((HANDLE)threadId);
  444. }
  445. context.mEBP = (uint32_t)win32CONTEXT.Ebp;
  446. context.mESP = (uint32_t)win32CONTEXT.Esp;
  447. context.mEIP = (uint32_t)win32CONTEXT.Eip;
  448. return true;
  449. #else
  450. // Not currently implemented for the given platform.
  451. EA_UNUSED(context);
  452. EA_UNUSED(threadId);
  453. memset(&context, 0, sizeof(context));
  454. return false;
  455. #endif
  456. }
  457. ///////////////////////////////////////////////////////////////////////////////
  458. // GetCallstackContextSysThreadId
  459. //
  460. EATHREADLIB_API bool GetCallstackContextSysThreadId(CallstackContext& context, intptr_t sysThreadId)
  461. {
  462. #ifdef EA_PLATFORM_WINDOWS
  463. // To do: Implement this if/when necessary.
  464. EA_UNUSED(context);
  465. EA_UNUSED(sysThreadId);
  466. return false;
  467. #else
  468. return GetCallstackContext(context, sysThreadId);
  469. #endif
  470. }
  471. // To do: Remove the usage of sStackBase for the platforms that it's not needed,
  472. // as can be seen from the logic below. For example Linux in general doesn't need it.
  473. EA::Thread::ThreadLocalStorage sStackBase;
  474. ///////////////////////////////////////////////////////////////////////////////
  475. // SetStackBase
  476. //
  477. EATHREADLIB_API void SetStackBase(void* pStackBase)
  478. {
  479. if(pStackBase)
  480. sStackBase.SetValue(pStackBase);
  481. else
  482. {
  483. #if defined(EA_COMPILER_MSVC) && defined(EA_PROCESSOR_X86)
  484. __asm mov pStackBase, ESP
  485. #elif defined(EA_COMPILER_MSVC) && defined(EA_PROCESSOR_X86_64)
  486. pStackBase = _AddressOfReturnAddress(); // Need to #include <intrin.h> for this.
  487. #elif (defined(EA_COMPILER_GNUC) && defined(EA_PROCESSOR_X86)) || (defined(EA_COMPILER_CLANG) && defined(EA_PROCESSOR_X86))
  488. asm volatile("mov %%esp, %0" : "=g"(pStackBase));
  489. #elif (defined(EA_COMPILER_GNUC) && defined(EA_PROCESSOR_X86_64)) || (defined(EA_COMPILER_CLANG) && defined(EA_PROCESSOR_X86_64))
  490. asm volatile("mov %%rsp, %0" : "=g"(pStackBase));
  491. #else
  492. #error Unsupported compiler/processor combination.
  493. #endif
  494. if(pStackBase)
  495. SetStackBase(pStackBase);
  496. // Else failure; do nothing.
  497. }
  498. }
  499. ///////////////////////////////////////////////////////////////////////////////
  500. // GetStackBase
  501. //
  502. EATHREADLIB_API void* GetStackBase()
  503. {
  504. #if defined(EA_PLATFORM_UNIX)
  505. void* pBase;
  506. if(GetPthreadStackInfo(&pBase, NULL))
  507. return pBase;
  508. #endif
  509. // Else we require the user to have set this previously, usually via a call
  510. // to SetStackBase() in the start function of this currently executing
  511. // thread (or main for the main thread).
  512. return sStackBase.GetValue();
  513. }
  514. ///////////////////////////////////////////////////////////////////////////////
  515. // GetStackLimit
  516. //
  517. EATHREADLIB_API void* GetStackLimit()
  518. {
  519. #if defined(EA_PLATFORM_UNIX)
  520. void* pLimit;
  521. if(GetPthreadStackInfo(NULL, &pLimit))
  522. return pLimit;
  523. #endif
  524. // We call strcat in order to create a stack frame here and to avoid a
  525. // compiler warning about returning a local variable in the case that we
  526. // simply returned &stackLocation itself.
  527. char stackLocation = 0;
  528. char* pStack = strcat(&stackLocation, "");
  529. return (void*)((uintptr_t)pStack & ~4095); // Round down to nearest page, as the stack grows downward.
  530. }
  531. } // namespace Callstack
  532. } // namespace EA