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
25using namespace llvm;
26
27uintptr_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
44unsigned llvm::getDefaultStackSize() {
45#ifdef LLVM_ON_UNIX
46 rlimit RL;
47 getrlimit(RLIMIT_STACK, rlimits: &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.
58namespace llvm {
59#ifdef LLVM_HAS_SPLIT_STACKS_AARCH64
60void 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
95namespace {
96#ifdef LLVM_HAS_SPLIT_STACKS
97void callback(void *Ctx) {
98 (*reinterpret_cast<function_ref<void()> *>(Ctx))();
99}
100#endif
101} // namespace
102
103#ifdef LLVM_HAS_SPLIT_STACKS
104void 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
122void 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