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