(index<- )        ./libstd/rt/stack.rs

    git branch:    * master           5200215 auto merge of #14035 : alexcrichton/rust/experimental, r=huonw
    modified:    Fri May  9 13:02:28 2014
   1  // Copyright 2013 The Rust Project Developers. See the COPYRIGHT
   2  // file at the top-level directory of this distribution and at
   3  // http://rust-lang.org/COPYRIGHT.
   4  //
   5  // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
   6  // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
   7  // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
   8  // option. This file may not be copied, modified, or distributed
   9  // except according to those terms.
  10  
  11  //! Rust stack-limit management
  12  //!
  13  //! Currently Rust uses a segmented-stack-like scheme in order to detect stack
  14  //! overflow for rust tasks. In this scheme, the prologue of all functions are
  15  //! preceded with a check to see whether the current stack limits are being
  16  //! exceeded.
  17  //!
  18  //! This module provides the functionality necessary in order to manage these
  19  //! stack limits (which are stored in platform-specific locations). The
  20  //! functions here are used at the borders of the task lifetime in order to
  21  //! manage these limits.
  22  //!
  23  //! This function is an unstable module because this scheme for stack overflow
  24  //! detection is not guaranteed to continue in the future. Usage of this module
  25  //! is discouraged unless absolutely necessary.
  26  
  27  pub static RED_ZONE: uint = 20 * 1024;
  28  
  29  /// This function is invoked from rust's current __morestack function. Segmented
  30  /// stacks are currently not enabled as segmented stacks, but rather one giant
  31  /// stack segment. This means that whenever we run out of stack, we want to
  32  /// truly consider it to be stack overflow rather than allocating a new stack.
  33  #[no_mangle]      // - this is called from C code
  34  #[no_split_stack] // - it would be sad for this function to trigger __morestack
  35  #[doc(hidden)]    // - Function must be `pub` to get exported, but it's
  36                    //   irrelevant for documentation purposes.
  37  #[cfg(not(test))] // in testing, use the original libstd's version
  38  pub extern "C" fn rust_stack_exhausted() {
  39      use option::{Option, None, Some};
  40      use owned::Box;
  41      use rt::local::Local;
  42      use rt::task::Task;
  43      use str::Str;
  44      use intrinsics;
  45  
  46      unsafe {
  47          // We're calling this function because the stack just ran out. We need
  48          // to call some other rust functions, but if we invoke the functions
  49          // right now it'll just trigger this handler being called again. In
  50          // order to alleviate this, we move the stack limit to be inside of the
  51          // red zone that was allocated for exactly this reason.
  52          let limit = get_sp_limit();
  53          record_sp_limit(limit - RED_ZONE / 2);
  54  
  55          // This probably isn't the best course of action. Ideally one would want
  56          // to unwind the stack here instead of just aborting the entire process.
  57          // This is a tricky problem, however. There's a few things which need to
  58          // be considered:
  59          //
  60          //  1. We're here because of a stack overflow, yet unwinding will run
  61          //     destructors and hence arbitrary code. What if that code overflows
  62          //     the stack? One possibility is to use the above allocation of an
  63          //     extra 10k to hope that we don't hit the limit, and if we do then
  64          //     abort the whole program. Not the best, but kind of hard to deal
  65          //     with unless we want to switch stacks.
  66          //
  67          //  2. LLVM will optimize functions based on whether they can unwind or
  68          //     not. It will flag functions with 'nounwind' if it believes that
  69          //     the function cannot trigger unwinding, but if we do unwind on
  70          //     stack overflow then it means that we could unwind in any function
  71          //     anywhere. We would have to make sure that LLVM only places the
  72          //     nounwind flag on functions which don't call any other functions.
  73          //
  74          //  3. The function that overflowed may have owned arguments. These
  75          //     arguments need to have their destructors run, but we haven't even
  76          //     begun executing the function yet, so unwinding will not run the
  77          //     any landing pads for these functions. If this is ignored, then
  78          //     the arguments will just be leaked.
  79          //
  80          // Exactly what to do here is a very delicate topic, and is possibly
  81          // still up in the air for what exactly to do. Some relevant issues:
  82          //
  83          //  #3555 - out-of-stack failure leaks arguments
  84          //  #3695 - should there be a stack limit?
  85          //  #9855 - possible strategies which could be taken
  86          //  #9854 - unwinding on windows through __morestack has never worked
  87          //  #2361 - possible implementation of not using landing pads
  88  
  89          let taskOption<Box<Task>> = Local::try_take();
  90          let name = match task {
  91              Some(ref task) => {
  92                  task.name.as_ref().map(|n| n.as_slice())
  93              }
  94              None => None
  95          };
  96          let name = name.unwrap_or("<unknown>");
  97  
  98          // See the message below for why this is not emitted to the
  99          // task's logger. This has the additional conundrum of the
 100          // logger may not be initialized just yet, meaning that an FFI
 101          // call would happen to initialized it (calling out to libuv),
 102          // and the FFI call needs 2MB of stack when we just ran out.
 103          rterrln!("task '{}' has overflowed its stack", name);
 104  
 105          intrinsics::abort();
 106      }
 107  }
 108  
 109  #[inline(always)]
 110  pub unsafe fn record_stack_bounds(stack_lo: uint, stack_hi: uint) {
 111      // When the old runtime had segmented stacks, it used a calculation that was
 112      // "limit + RED_ZONE + FUDGE". The red zone was for things like dynamic
 113      // symbol resolution, llvm function calls, etc. In theory this red zone
 114      // value is 0, but it matters far less when we have gigantic stacks because
 115      // we don't need to be so exact about our stack budget. The "fudge factor"
 116      // was because LLVM doesn't emit a stack check for functions < 256 bytes in
 117      // size. Again though, we have giant stacks, so we round all these
 118      // calculations up to the nice round number of 20k.
 119      record_sp_limit(stack_lo + RED_ZONE);
 120  
 121      return target_record_stack_bounds(stack_lo, stack_hi);
 122  
 123      #[cfg(not(windows))] #[cfg(not(target_arch = "x86_64"))] #[inline(always)]
 124      unsafe fn target_record_stack_bounds(_stack_louint, _stack_hiuint) {}
 125      #[cfg(windows, target_arch = "x86_64")] #[inline(always)]
 126      unsafe fn target_record_stack_bounds(stack_lo: uint, stack_hi: uint) {
 127          // Windows compiles C functions which may check the stack bounds. This
 128          // means that if we want to perform valid FFI on windows, then we need
 129          // to ensure that the stack bounds are what they truly are for this
 130          // task. More info can be found at:
 131          //   https://github.com/mozilla/rust/issues/3445#issuecomment-26114839
 132          //
 133          // stack range is at TIB: %gs:0x08 (top) and %gs:0x10 (bottom)
 134          asm!("mov $0, %gs:0x08" :: "r"(stack_hi) :: "volatile");
 135          asm!("mov $0, %gs:0x10" :: "r"(stack_lo) :: "volatile");
 136      }
 137  }
 138  
 139  /// Records the current limit of the stack as specified by `end`.
 140  ///
 141  /// This is stored in an OS-dependent location, likely inside of the thread
 142  /// local storage. The location that the limit is stored is a pre-ordained
 143  /// location because it's where LLVM has emitted code to check.
 144  ///
 145  /// Note that this cannot be called under normal circumstances. This function is
 146  /// changing the stack limit, so upon returning any further function calls will
 147  /// possibly be triggering the morestack logic if you're not careful.
 148  ///
 149  /// Also note that this and all of the inside functions are all flagged as
 150  /// "inline(always)" because they're messing around with the stack limits.  This
 151  /// would be unfortunate for the functions themselves to trigger a morestack
 152  /// invocation (if they were an actual function call).
 153  #[inline(always)]
 154  pub unsafe fn record_sp_limit(limit: uint) {
 155      return target_record_sp_limit(limit);
 156  
 157      // x86-64
 158      #[cfg(target_arch = "x86_64", target_os = "macos")] #[inline(always)]
 159      unsafe fn target_record_sp_limit(limit: uint) {
 160          asm!("movq $$0x60+90*8, %rsi
 161                movq $0, %gs:(%rsi)" :: "r"(limit) : "rsi" : "volatile")
 162      }
 163      #[cfg(target_arch = "x86_64", target_os = "linux")] #[inline(always)]
 164      unsafe fn target_record_sp_limit(limit: uint) {
 165          asm!("movq $0, %fs:112" :: "r"(limit) :: "volatile")
 166      }
 167      #[cfg(target_arch = "x86_64", target_os = "win32")] #[inline(always)]
 168      unsafe fn target_record_sp_limit(limit: uint) {
 169          // see: http://en.wikipedia.org/wiki/Win32_Thread_Information_Block
 170          // store this inside of the "arbitrary data slot", but double the size
 171          // because this is 64 bit instead of 32 bit
 172          asm!("movq $0, %gs:0x28" :: "r"(limit) :: "volatile")
 173      }
 174      #[cfg(target_arch = "x86_64", target_os = "freebsd")] #[inline(always)]
 175      unsafe fn target_record_sp_limit(limit: uint) {
 176          asm!("movq $0, %fs:24" :: "r"(limit) :: "volatile")
 177      }
 178  
 179      // x86
 180      #[cfg(target_arch = "x86", target_os = "macos")] #[inline(always)]
 181      unsafe fn target_record_sp_limit(limit: uint) {
 182          asm!("movl $$0x48+90*4, %eax
 183                movl $0, %gs:(%eax)" :: "r"(limit) : "eax" : "volatile")
 184      }
 185      #[cfg(target_arch = "x86", target_os = "linux")]
 186      #[cfg(target_arch = "x86", target_os = "freebsd")] #[inline(always)]
 187      unsafe fn target_record_sp_limit(limit: uint) {
 188          asm!("movl $0, %gs:48" :: "r"(limit) :: "volatile")
 189      }
 190      #[cfg(target_arch = "x86", target_os = "win32")] #[inline(always)]
 191      unsafe fn target_record_sp_limit(limit: uint) {
 192          // see: http://en.wikipedia.org/wiki/Win32_Thread_Information_Block
 193          // store this inside of the "arbitrary data slot"
 194          asm!("movl $0, %fs:0x14" :: "r"(limit) :: "volatile")
 195      }
 196  
 197      // mips, arm - Some brave soul can port these to inline asm, but it's over
 198      //             my head personally
 199      #[cfg(target_arch = "mips")]
 200      #[cfg(target_arch = "arm")] #[inline(always)]
 201      unsafe fn target_record_sp_limit(limit: uint) {
 202          use libc::c_void;
 203          return record_sp_limit(limit as *c_void);
 204          extern {
 205              fn record_sp_limit(limit: *c_void);
 206          }
 207      }
 208  }
 209  
 210  /// The counterpart of the function above, this function will fetch the current
 211  /// stack limit stored in TLS.
 212  ///
 213  /// Note that all of these functions are meant to be exact counterparts of their
 214  /// brethren above, except that the operands are reversed.
 215  ///
 216  /// As with the setter, this function does not have a __morestack header and can
 217  /// therefore be called in a "we're out of stack" situation.
 218  #[inline(always)]
 219  pub unsafe fn get_sp_limit() -> uint {
 220      return target_get_sp_limit();
 221  
 222      // x86-64
 223      #[cfg(target_arch = "x86_64", target_os = "macos")] #[inline(always)]
 224      unsafe fn target_get_sp_limit() -> uint {
 225          let limit;
 226          asm!("movq $$0x60+90*8, %rsi
 227                movq %gs:(%rsi), $0" : "=r"(limit) :: "rsi" : "volatile");
 228          return limit;
 229      }
 230      #[cfg(target_arch = "x86_64", target_os = "linux")] #[inline(always)]
 231      unsafe fn target_get_sp_limit() -> uint {
 232          let limit;
 233          asm!("movq %fs:112, $0" : "=r"(limit) ::: "volatile");
 234          return limit;
 235      }
 236      #[cfg(target_arch = "x86_64", target_os = "win32")] #[inline(always)]
 237      unsafe fn target_get_sp_limit() -> uint {
 238          let limit;
 239          asm!("movq %gs:0x28, $0" : "=r"(limit) ::: "volatile");
 240          return limit;
 241      }
 242      #[cfg(target_arch = "x86_64", target_os = "freebsd")] #[inline(always)]
 243      unsafe fn target_get_sp_limit() -> uint {
 244          let limit;
 245          asm!("movq %fs:24, $0" : "=r"(limit) ::: "volatile");
 246          return limit;
 247      }
 248  
 249      // x86
 250      #[cfg(target_arch = "x86", target_os = "macos")] #[inline(always)]
 251      unsafe fn target_get_sp_limit() -> uint {
 252          let limit;
 253          asm!("movl $$0x48+90*4, %eax
 254                movl %gs:(%eax), $0" : "=r"(limit) :: "eax" : "volatile");
 255          return limit;
 256      }
 257      #[cfg(target_arch = "x86", target_os = "linux")]
 258      #[cfg(target_arch = "x86", target_os = "freebsd")] #[inline(always)]
 259      unsafe fn target_get_sp_limit() -> uint {
 260          let limit;
 261          asm!("movl %gs:48, $0" : "=r"(limit) ::: "volatile");
 262          return limit;
 263      }
 264      #[cfg(target_arch = "x86", target_os = "win32")] #[inline(always)]
 265      unsafe fn target_get_sp_limit() -> uint {
 266          let limit;
 267          asm!("movl %fs:0x14, $0" : "=r"(limit) ::: "volatile");
 268          return limit;
 269      }
 270  
 271      // mips, arm - Some brave soul can port these to inline asm, but it's over
 272      //             my head personally
 273      #[cfg(target_arch = "mips")]
 274      #[cfg(target_arch = "arm")] #[inline(always)]
 275      unsafe fn target_get_sp_limit() -> uint {
 276          use libc::c_void;
 277          return get_sp_limit() as uint;
 278          extern {
 279              fn get_sp_limit() -> *c_void;
 280          }
 281      }
 282  }