xref: /freebsd/contrib/llvm-project/llvm/lib/Support/ProgramStack.cpp (revision 770cf0a5f02dc8983a89c6568d741fbc25baa999)
1 //===--- RunOnNewStack.cpp - Crash Recovery -------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "llvm/Support/ProgramStack.h"
10 #include "llvm/Config/config.h"
11 #include "llvm/Support/Compiler.h"
12 
13 #ifdef LLVM_ON_UNIX
14 # include <sys/resource.h> // for getrlimit
15 #endif
16 
17 #ifdef _MSC_VER
18 # include <intrin.h>  // for _AddressOfReturnAddress
19 #endif
20 
21 #ifndef LLVM_HAS_SPLIT_STACKS
22 # include "llvm/Support/thread.h"
23 #endif
24 
25 using namespace llvm;
26 
27 uintptr_t llvm::getStackPointer() {
28 #if __GNUC__ || __has_builtin(__builtin_frame_address)
29   return (uintptr_t)__builtin_frame_address(0);
30 #elif defined(_MSC_VER)
31   return (uintptr_t)_AddressOfReturnAddress();
32 #else
33   volatile char CharOnStack = 0;
34   // The volatile store here is intended to escape the local variable, to
35   // prevent the compiler from optimizing CharOnStack into anything other
36   // than a char on the stack.
37   //
38   // Tested on: MSVC 2015 - 2019, GCC 4.9 - 9, Clang 3.2 - 9, ICC 13 - 19.
39   char *volatile Ptr = &CharOnStack;
40   return (uintptr_t)Ptr;
41 #endif
42 }
43 
44 unsigned llvm::getDefaultStackSize() {
45 #ifdef LLVM_ON_UNIX
46   rlimit RL;
47   getrlimit(RLIMIT_STACK, &RL);
48   return RL.rlim_cur;
49 #else
50   // Clang recursively parses, instantiates templates, and evaluates constant
51   // expressions. We've found 8MiB to be a reasonable stack size given the way
52   // Clang works and the way C++ is commonly written.
53   return 8 << 20;
54 #endif
55 }
56 
57 // Not an anonymous namespace to avoid warning about undefined local function.
58 namespace llvm {
59 #ifdef LLVM_HAS_SPLIT_STACKS_AARCH64
60 void runOnNewStackImpl(void *Stack, void (*Fn)(void *), void *Ctx) __asm__(
61     "_ZN4llvm17runOnNewStackImplEPvPFvS0_ES0_");
62 
63 // This can't use naked functions because there is no way to know if cfi
64 // directives are being emitted or not.
65 //
66 // When adding new platforms it may be better to move to a .S file with macros
67 // for dealing with platform differences.
68 __asm__ (
69     ".globl  _ZN4llvm17runOnNewStackImplEPvPFvS0_ES0_\n\t"
70     ".p2align  2\n\t"
71     "_ZN4llvm17runOnNewStackImplEPvPFvS0_ES0_:\n\t"
72     ".cfi_startproc\n\t"
73     "mov       x16, sp\n\t"
74     "sub       x0, x0, #0x20\n\t"            // subtract space from stack
75     "stp       xzr, x16, [x0, #0x00]\n\t"    // save old sp
76     "stp       x29, x30, [x0, #0x10]\n\t"    // save fp, lr
77     "mov       sp, x0\n\t"                   // switch to new stack
78     "add       x29, x0, #0x10\n\t"           // switch to new frame
79     ".cfi_def_cfa w29, 16\n\t"
80     ".cfi_offset w30, -8\n\t"                // lr
81     ".cfi_offset w29, -16\n\t"               // fp
82 
83     "mov       x0, x2\n\t"                   // Ctx is the only argument
84     "blr       x1\n\t"                       // call Fn
85 
86     "ldp       x29, x30, [sp, #0x10]\n\t"    // restore fp, lr
87     "ldp       xzr, x16, [sp, #0x00]\n\t"    // load old sp
88     "mov       sp, x16\n\t"
89     "ret\n\t"
90     ".cfi_endproc"
91 );
92 #endif
93 } // namespace llvm
94 
95 namespace {
96 #ifdef LLVM_HAS_SPLIT_STACKS
97 void callback(void *Ctx) {
98   (*reinterpret_cast<function_ref<void()> *>(Ctx))();
99 }
100 #endif
101 } // namespace
102 
103 #ifdef LLVM_HAS_SPLIT_STACKS
104 void llvm::runOnNewStack(unsigned StackSize, function_ref<void()> Fn) {
105   if (StackSize == 0)
106     StackSize = getDefaultStackSize();
107 
108   // We use malloc here instead of mmap because:
109   //   - it's simpler,
110   //   - many malloc implementations will reuse the allocation in cases where
111   //     we're bouncing accross the edge of a stack boundry, and
112   //   - many malloc implemenations will already provide guard pages for
113   //     allocations this large.
114   void *Stack = malloc(StackSize);
115   void *BottomOfStack = (char *)Stack + StackSize;
116 
117   runOnNewStackImpl(BottomOfStack, callback, &Fn);
118 
119   free(Stack);
120 }
121 #else
122 void llvm::runOnNewStack(unsigned StackSize, function_ref<void()> Fn) {
123   llvm::thread Thread(
124       StackSize == 0 ? std::nullopt : std::optional<unsigned>(StackSize), Fn);
125   Thread.join();
126 }
127 #endif
128