(index<- )        ./libstd/rt/unwind.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  //! Stack unwinding
  12  
  13  // Implementation of Rust stack unwinding
  14  //
  15  // For background on exception handling and stack unwinding please see
  16  // "Exception Handling in LLVM" (llvm.org/docs/ExceptionHandling.html) and
  17  // documents linked from it.
  18  // These are also good reads:
  19  //     http://theofilos.cs.columbia.edu/blog/2013/09/22/base_abi/
  20  //     http://monoinfinito.wordpress.com/series/exception-handling-in-c/
  21  //     http://www.airs.com/blog/index.php?s=exception+frames
  22  //
  23  // ~~~ A brief summary ~~~
  24  // Exception handling happens in two phases: a search phase and a cleanup phase.
  25  //
  26  // In both phases the unwinder walks stack frames from top to bottom using
  27  // information from the stack frame unwind sections of the current process's
  28  // modules ("module" here refers to an OS module, i.e. an executable or a
  29  // dynamic library).
  30  //
  31  // For each stack frame, it invokes the associated "personality routine", whose
  32  // address is also stored in the unwind info section.
  33  //
  34  // In the search phase, the job of a personality routine is to examine exception
  35  // object being thrown, and to decide whether it should be caught at that stack
  36  // frame.  Once the handler frame has been identified, cleanup phase begins.
  37  //
  38  // In the cleanup phase, personality routines invoke cleanup code associated
  39  // with their stack frames (i.e. destructors).  Once stack has been unwound down
  40  // to the handler frame level, unwinding stops and the last personality routine
  41  // transfers control to its' catch block.
  42  //
  43  // ~~~ Frame unwind info registration ~~~
  44  // Each module has its' own frame unwind info section (usually ".eh_frame"), and
  45  // unwinder needs to know about all of them in order for unwinding to be able to
  46  // cross module boundaries.
  47  //
  48  // On some platforms, like Linux, this is achieved by dynamically enumerating
  49  // currently loaded modules via the dl_iterate_phdr() API and finding all
  50  // .eh_frame sections.
  51  //
  52  // Others, like Windows, require modules to actively register their unwind info
  53  // sections by calling __register_frame_info() API at startup.  In the latter
  54  // case it is essential that there is only one copy of the unwinder runtime in
  55  // the process.  This is usually achieved by linking to the dynamic version of
  56  // the unwind runtime.
  57  //
  58  // Currently Rust uses unwind runtime provided by libgcc.
  59  
  60  use any::{Any, AnyRefExt};
  61  use cast;
  62  use fmt;
  63  use kinds::Send;
  64  use mem;
  65  use option::{Some, None, Option};
  66  use owned::Box;
  67  use prelude::drop;
  68  use ptr::RawPtr;
  69  use result::{Err, Ok};
  70  use rt::backtrace;
  71  use rt::local::Local;
  72  use rt::task::Task;
  73  use str::Str;
  74  use task::TaskResult;
  75  use intrinsics;
  76  
  77  use uw = rt::libunwind;
  78  
  79  pub struct Unwinder {
  80      unwinding: bool,
  81      cause: Option<Box<Any:Send>>
  82  }
  83  
  84  impl Unwinder {
  85      pub fn new() -> Unwinder {
  86          Unwinder {
  87              unwinding: false,
  88              cause: None,
  89          }
  90      }
  91  
  92      pub fn unwinding(&self) -> bool {
  93          self.unwinding
  94      }
  95  
  96      pub fn try(&mut self, f||) {
  97          use raw::Closure;
  98          use libc::{c_void};
  99  
 100          unsafe {
 101              let closureClosure = cast::transmute(f);
 102              let ep = rust_try(try_fn, closure.code as *c_void,
 103                                closure.env as *c_void);
 104              if !ep.is_null() {
 105                  rtdebug!("caught {}", (*ep).exception_class);
 106                  uw::_Unwind_DeleteException(ep);
 107              }
 108          }
 109  
 110          extern fn try_fn(code: *c_void, env: *c_void) {
 111              unsafe {
 112                  let closure|| = cast::transmute(Closure {
 113                      code: code as *(),
 114                      env: env as *(),
 115                  });
 116                  closure();
 117              }
 118          }
 119  
 120          extern {
 121              // Rust's try-catch
 122              // When f(...) returns normally, the return value is null.
 123              // When f(...) throws, the return value is a pointer to the caught
 124              // exception object.
 125              fn rust_try(f: extern "C" fn(*c_void, *c_void),
 126                          code: *c_void,
 127                          data: *c_void) -> *uw::_Unwind_Exception;
 128          }
 129      }
 130  
 131      pub fn begin_unwind(&mut self, causeBox<Any:Send>) -> ! {
 132          rtdebug!("begin_unwind()");
 133  
 134          self.unwinding = true;
 135          self.cause = Some(cause);
 136  
 137          rust_fail();
 138  
 139          // An uninlined, unmangled function upon which to slap yer breakpoints
 140          #[inline(never)]
 141          #[no_mangle]
 142          fn rust_fail() -> ! {
 143              unsafe {
 144                  let exception = box uw::_Unwind_Exception {
 145                      exception_class: rust_exception_class(),
 146                      exception_cleanup: exception_cleanup,
 147                      private: [0, ..uw::unwinder_private_data_size],
 148                  };
 149                  let error = uw::_Unwind_RaiseException(cast::transmute(exception));
 150                  rtabort!("Could not unwind stack, error = {}", error as int)
 151              }
 152  
 153              extern "C" fn exception_cleanup(_unwind_codeuw::_Unwind_Reason_Code,
 154                                              exception: *uw::_Unwind_Exception) {
 155                  rtdebug!("exception_cleanup()");
 156                  unsafe {
 157                      let _Box<uw::_Unwind_Exception> =
 158                          cast::transmute(exception);
 159                  }
 160              }
 161          }
 162      }
 163  
 164      pub fn result(&mut self) -> TaskResult {
 165          if self.unwinding {
 166              Err(self.cause.take().unwrap())
 167          } else {
 168              Ok(())
 169          }
 170      }
 171  }
 172  
 173  // Rust's exception class identifier.  This is used by personality routines to
 174  // determine whether the exception was thrown by their own runtime.
 175  fn rust_exception_class() -> uw::_Unwind_Exception_Class {
 176      // M O Z \0  R U S T -- vendor, language
 177      0x4d4f5a_00_52555354
 178  }
 179  
 180  // We could implement our personality routine in pure Rust, however exception
 181  // info decoding is tedious.  More importantly, personality routines have to
 182  // handle various platform quirks, which are not fun to maintain.  For this
 183  // reason, we attempt to reuse personality routine of the C language:
 184  // __gcc_personality_v0.
 185  //
 186  // Since C does not support exception catching, __gcc_personality_v0 simply
 187  // always returns _URC_CONTINUE_UNWIND in search phase, and always returns
 188  // _URC_INSTALL_CONTEXT (i.e. "invoke cleanup code") in cleanup phase.
 189  //
 190  // This is pretty close to Rust's exception handling approach, except that Rust
 191  // does have a single "catch-all" handler at the bottom of each task's stack.
 192  // So we have two versions:
 193  // - rust_eh_personality, used by all cleanup landing pads, which never catches,
 194  //   so the behavior of __gcc_personality_v0 is perfectly adequate there, and
 195  // - rust_eh_personality_catch, used only by rust_try(), which always catches.
 196  //   This is achieved by overriding the return value in search phase to always
 197  //   say "catch!".
 198  
 199  #[cfg(not(target_arch = "arm"), not(test))]
 200  #[doc(hidden)]
 201  #[allow(visible_private_types)]
 202  pub mod eabi {
 203      use uw = rt::libunwind;
 204      use libc::c_int;
 205  
 206      extern "C" {
 207          fn __gcc_personality_v0(versionc_int,
 208                                  actionsuw::_Unwind_Action,
 209                                  exception_classuw::_Unwind_Exception_Class,
 210                                  ue_header: *uw::_Unwind_Exception,
 211                                  context: *uw::_Unwind_Context)
 212              -> uw::_Unwind_Reason_Code;
 213      }
 214  
 215      #[lang="eh_personality"]
 216      #[no_mangle] // so we can reference it by name from middle/trans/base.rs
 217      pub extern "C" fn rust_eh_personality(
 218          versionc_int,
 219          actionsuw::_Unwind_Action,
 220          exception_classuw::_Unwind_Exception_Class,
 221          ue_header: *uw::_Unwind_Exception,
 222          context: *uw::_Unwind_Context
 223      ) -> uw::_Unwind_Reason_Code
 224      {
 225          unsafe {
 226              __gcc_personality_v0(version, actions, exception_class, ue_header,
 227                                   context)
 228          }
 229      }
 230  
 231      #[no_mangle] // referenced from rust_try.ll
 232      pub extern "C" fn rust_eh_personality_catch(
 233          versionc_int,
 234          actionsuw::_Unwind_Action,
 235          exception_classuw::_Unwind_Exception_Class,
 236          ue_header: *uw::_Unwind_Exception,
 237          context: *uw::_Unwind_Context
 238      ) -> uw::_Unwind_Reason_Code
 239      {
 240          if (actions as c_int & uw::_UA_SEARCH_PHASE as c_int) != 0 { // search phase
 241              uw::_URC_HANDLER_FOUND // catch!
 242          }
 243          else { // cleanup phase
 244              unsafe {
 245                   __gcc_personality_v0(version, actions, exception_class, ue_header,
 246                                        context)
 247              }
 248          }
 249      }
 250  }
 251  
 252  // ARM EHABI uses a slightly different personality routine signature,
 253  // but otherwise works the same.
 254  #[cfg(target_arch = "arm", not(test))]
 255  #[allow(visible_private_types)]
 256  pub mod eabi {
 257      use uw = rt::libunwind;
 258      use libc::c_int;
 259  
 260      extern "C" {
 261          fn __gcc_personality_v0(state: uw::_Unwind_State,
 262                                  ue_header: *uw::_Unwind_Exception,
 263                                  context: *uw::_Unwind_Context)
 264              -> uw::_Unwind_Reason_Code;
 265      }
 266  
 267      #[lang="eh_personality"]
 268      #[no_mangle] // so we can reference it by name from middle/trans/base.rs
 269      pub extern "C" fn rust_eh_personality(
 270          state: uw::_Unwind_State,
 271          ue_header: *uw::_Unwind_Exception,
 272          context: *uw::_Unwind_Context
 273      ) -> uw::_Unwind_Reason_Code
 274      {
 275          unsafe {
 276              __gcc_personality_v0(state, ue_header, context)
 277          }
 278      }
 279  
 280      #[no_mangle] // referenced from rust_try.ll
 281      pub extern "C" fn rust_eh_personality_catch(
 282          state: uw::_Unwind_State,
 283          ue_header: *uw::_Unwind_Exception,
 284          context: *uw::_Unwind_Context
 285      ) -> uw::_Unwind_Reason_Code
 286      {
 287          if (state as c_int & uw::_US_ACTION_MASK as c_int)
 288                             == uw::_US_VIRTUAL_UNWIND_FRAME as c_int { // search phase
 289              uw::_URC_HANDLER_FOUND // catch!
 290          }
 291          else { // cleanup phase
 292              unsafe {
 293                   __gcc_personality_v0(state, ue_header, context)
 294              }
 295          }
 296      }
 297  }
 298  
 299  #[cold]
 300  #[no_mangle]
 301  #[cfg(not(test))]
 302  pub extern fn rust_fail_bounds_check(file: *u8, line: uint,
 303                                       index: uint, len: uint) -> ! {
 304      use str::raw::c_str_to_static_slice;
 305  
 306      let msg = format!("index out of bounds: the len is {} but the index is {}",
 307                        len as uint, index as uint);
 308      begin_unwind(msg, unsafe { c_str_to_static_slice(file as *i8) }, line)
 309  }
 310  
 311  // Entry point of failure from the libcore crate
 312  #[no_mangle]
 313  #[cfg(not(test))]
 314  pub extern fn rust_begin_unwind(msg: &str, file: &'static str, line: uint) -> ! {
 315      use str::StrAllocating;
 316      begin_unwind(msg.to_owned(), file, line)
 317  }
 318  
 319  /// The entry point for unwinding with a formatted message.
 320  ///
 321  /// This is designed to reduce the amount of code required at the call
 322  /// site as much as possible (so that `fail!()` has as low an impact
 323  /// on (e.g.) the inlining of other functions as possible), by moving
 324  /// the actual formatting into this shared place.
 325  #[inline(never)] #[cold]
 326  pub fn begin_unwind_fmt(msg: &fmt::Arguments, file: &'static str, line: uint) -> ! {
 327      // We do two allocations here, unfortunately. But (a) they're
 328      // required with the current scheme, and (b) we don't handle
 329      // failure + OOM properly anyway (see comment in begin_unwind
 330      // below).
 331      begin_unwind_inner(box fmt::format(msg), file, line)
 332  }
 333  
 334  /// This is the entry point of unwinding for fail!() and assert!().
 335  #[inline(never)] #[cold] // avoid code bloat at the call sites as much as possible
 336  pub fn begin_unwind<M: Any + Send>(msgM, file: &'static str, line: uint) -> ! {
 337      // Note that this should be the only allocation performed in this code path.
 338      // Currently this means that fail!() on OOM will invoke this code path,
 339      // but then again we're not really ready for failing on OOM anyway. If
 340      // we do start doing this, then we should propagate this allocation to
 341      // be performed in the parent of this task instead of the task that's
 342      // failing.
 343  
 344      // see below for why we do the `Any` coercion here.
 345      begin_unwind_inner(box msg, file, line)
 346  }
 347  
 348  
 349  /// The core of the unwinding.
 350  ///
 351  /// This is non-generic to avoid instantiation bloat in other crates
 352  /// (which makes compilation of small crates noticably slower). (Note:
 353  /// we need the `Any` object anyway, we're not just creating it to
 354  /// avoid being generic.)
 355  ///
 356  /// Do this split took the LLVM IR line counts of `fn main() { fail!()
 357  /// }` from ~1900/3700 (-O/no opts) to 180/590.
 358  #[inline(never)] #[cold] // this is the slow path, please never inline this
 359  fn begin_unwind_inner(msgBox<Any:Send>,
 360                        file: &'static str,
 361                        line: uint) -> ! {
 362      let mut task;
 363      {
 364          let msg_s = match msg.as_ref::<&'static str>() {
 365              Some(s) => *s,
 366              None => match msg.as_ref::<~str>() {
 367                  Some(s) => s.as_slice(),
 368                  None => "Box<Any>",
 369              }
 370          };
 371  
 372          // It is assumed that all reasonable rust code will have a local task at
 373          // all times. This means that this `try_take` will succeed almost all of
 374          // the time. There are border cases, however, when the runtime has
 375          // *almost* set up the local task, but hasn't quite gotten there yet. In
 376          // order to get some better diagnostics, we print on failure and
 377          // immediately abort the whole process if there is no local task
 378          // available.
 379          let opt_taskOption<Box<Task>> = Local::try_take();
 380          task = match opt_task {
 381              Some(t) => t,
 382              None => {
 383                  rterrln!("failed at '{}', {}:{}", msg_s, file, line);
 384                  if backtrace::log_enabled() {
 385                      let mut err = ::rt::util::Stderr;
 386                      let _err = backtrace::write(&mut err);
 387                  } else {
 388                      rterrln!("run with `RUST_BACKTRACE=1` to see a backtrace");
 389                  }
 390                  unsafe { intrinsics::abort() }
 391              }
 392          };
 393  
 394          // See comments in io::stdio::with_task_stdout as to why we have to be
 395          // careful when using an arbitrary I/O handle from the task. We
 396          // essentially need to dance to make sure when a task is in TLS when
 397          // running user code.
 398          let name = task.name.take();
 399          {
 400              let n = name.as_ref().map(|n| n.as_slice()).unwrap_or("<unnamed>");
 401  
 402              match task.stderr.take() {
 403                  Some(mut stderr) => {
 404                      Local::put(task);
 405                      // FIXME: what to do when the task printing fails?
 406                      let _err = format_args!(|args| ::fmt::writeln(stderr, args),
 407                                              "task '{}' failed at '{}', {}:{}",
 408                                              n, msg_s, file, line);
 409                      if backtrace::log_enabled() {
 410                          let _err = backtrace::write(stderr);
 411                      }
 412                      task = Local::take();
 413  
 414                      match mem::replace(&mut task.stderr, Some(stderr)) {
 415                          Some(prev) => {
 416                              Local::put(task);
 417                              drop(prev);
 418                              task = Local::take();
 419                          }
 420                          None => {}
 421                      }
 422                  }
 423                  None => {
 424                      rterrln!("task '{}' failed at '{}', {}:{}", n, msg_s,
 425                               file, line);
 426                      if backtrace::log_enabled() {
 427                          let mut err = ::rt::util::Stderr;
 428                          let _err = backtrace::write(&mut err);
 429                      }
 430                  }
 431              }
 432          }
 433          task.name = name;
 434  
 435          if task.unwinder.unwinding {
 436              // If a task fails while it's already unwinding then we
 437              // have limited options. Currently our preference is to
 438              // just abort. In the future we may consider resuming
 439              // unwinding or otherwise exiting the task cleanly.
 440              rterrln!("task failed during unwinding (double-failure - total drag!)")
 441              rterrln!("rust must abort now. so sorry.");
 442  
 443              // Don't print the backtrace twice (it would have already been
 444              // printed if logging was enabled).
 445              if !backtrace::log_enabled() {
 446                  let mut err = ::rt::util::Stderr;
 447                  let _err = backtrace::write(&mut err);
 448              }
 449              unsafe { intrinsics::abort() }
 450          }
 451      }
 452  
 453      // The unwinder won't actually use the task at all, so we put the task back
 454      // into TLS right before we invoke the unwinder, but this means we need an
 455      // unsafe reference back to the unwinder once it's in TLS.
 456      Local::put(task);
 457      unsafe {
 458          let task*mut Task = Local::unsafe_borrow();
 459          (*task).unwinder.begin_unwind(msg);
 460      }
 461  }


libstd/rt/unwind.rs:78:1-78:1 -struct- definition:
pub struct Unwinder {
    unwinding: bool,
    cause: Option<Box<Any:Send>>
references:- 4
85:     pub fn new() -> Unwinder {
86:         Unwinder {
87:             unwinding: false,
libstd/rt/task.rs:
49:     pub storage: LocalStorage,
50:     pub unwinder: Unwinder,
51:     pub death: Death,


libstd/rt/unwind.rs:358:76-358:76 -fn- definition:
fn begin_unwind_inner(msg: Box<Any:Send>,
                      file: &'static str,
                      line: uint) -> ! {
references:- 2
330:     // below).
331:     begin_unwind_inner(box fmt::format(msg), file, line)
332: }
--
344:     // see below for why we do the `Any` coercion here.
345:     begin_unwind_inner(box msg, file, line)
346: }


libstd/rt/unwind.rs:335:83-335:83 -fn- definition:
pub fn begin_unwind<M: Any + Send>(msg: M, file: &'static str, line: uint) -> ! {
    // Note that this should be the only allocation performed in this code path.
    // Currently this means that fail!() on OOM will invoke this code path,
references:- 63
libstd/macros.rs:


libstd/rt/unwind.rs:325:25-325:25 -fn- definition:
pub fn begin_unwind_fmt(msg: &fmt::Arguments, file: &'static str, line: uint) -> ! {
    // We do two allocations here, unfortunately. But (a) they're
    // required with the current scheme, and (b) we don't handle
references:- 121
libstd/macros.rs: