128 lines · cpp
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-exception6//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_UNIX14# include <sys/resource.h> // for getrlimit15#endif16 17#ifdef _MSC_VER18# include <intrin.h> // for _AddressOfReturnAddress19#endif20 21#ifndef LLVM_HAS_SPLIT_STACKS22# include "llvm/Support/thread.h"23#endif24 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#else33 volatile char CharOnStack = 0;34 // The volatile store here is intended to escape the local variable, to35 // prevent the compiler from optimizing CharOnStack into anything other36 // 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#endif42}43 44unsigned llvm::getDefaultStackSize() {45#ifdef LLVM_ON_UNIX46 rlimit RL;47 getrlimit(RLIMIT_STACK, &RL);48 return RL.rlim_cur;49#else50 // Clang recursively parses, instantiates templates, and evaluates constant51 // expressions. We've found 8MiB to be a reasonable stack size given the way52 // Clang works and the way C++ is commonly written.53 return 8 << 20;54#endif55}56 57// Not an anonymous namespace to avoid warning about undefined local function.58namespace llvm {59#ifdef LLVM_HAS_SPLIT_STACKS_AARCH6460void 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 cfi64// directives are being emitted or not.65//66// When adding new platforms it may be better to move to a .S file with macros67// 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 stack75 "stp xzr, x16, [x0, #0x00]\n\t" // save old sp76 "stp x29, x30, [x0, #0x10]\n\t" // save fp, lr77 "mov sp, x0\n\t" // switch to new stack78 "add x29, x0, #0x10\n\t" // switch to new frame79 ".cfi_def_cfa w29, 16\n\t"80 ".cfi_offset w30, -8\n\t" // lr81 ".cfi_offset w29, -16\n\t" // fp82 83 "mov x0, x2\n\t" // Ctx is the only argument84 "blr x1\n\t" // call Fn85 86 "ldp x29, x30, [sp, #0x10]\n\t" // restore fp, lr87 "ldp xzr, x16, [sp, #0x00]\n\t" // load old sp88 "mov sp, x16\n\t"89 "ret\n\t"90 ".cfi_endproc"91);92#endif93} // namespace llvm94 95namespace {96#ifdef LLVM_HAS_SPLIT_STACKS97void callback(void *Ctx) {98 (*reinterpret_cast<function_ref<void()> *>(Ctx))();99}100#endif101} // namespace102 103#ifdef LLVM_HAS_SPLIT_STACKS104void 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 where111 // we're bouncing accross the edge of a stack boundry, and112 // - many malloc implemenations will already provide guard pages for113 // 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#else122void 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#endif128